The problem I'm having is not being able to iterate over all the pixels in a UIImage quickly enough, checking each colour against a particular one (green). The iteration must be processed as soon as the user initiates it. At the moment there's a ~0.5s delay.
I'm using this to create a screenshot of the view:
func screenshot() -> UIImage {
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
view.layer.render(in: UIGraphicsGetCurrentContext()!)
let screenShot = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return screenShot!
}
This to obtain the colour of a single pixel:
extension UIImage {
func getPixelColor(pos: CGPoint) -> UIColor {
let pixelData = self.cgImage!.dataProvider!.data
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let pixelInfo: Int = ((Int(self.size.width) * Int(pos.y)) + Int(pos.x)) * 4
let r = CGFloat(data[pixelInfo]) / CGFloat(255.0)
let g = CGFloat(data[pixelInfo+1]) / CGFloat(255.0)
let b = CGFloat(data[pixelInfo+2]) / CGFloat(255.0)
let a = CGFloat(data[pixelInfo+3]) / CGFloat(255.0)
return UIColor(red: r, green: g, blue: b, alpha: a)
}
}
And this loop to check each pixel of the image:
testImage = screenshot()
greenPresent = false
for yCo in 0 ..< Int(testImage.size.height) {
for xCo in 0 ..< Int(testImage.size.width) {
if testImage.getPixelColor(pos: CGPoint(x: xCo, y: yCo)) == UIColor(red: 0, green: 1, blue: 0, alpha: 1) {
greenPresent = true
greenCount += 1
}
}
}
Any suggestions would be appreciated.
EDIT
Performance takes a hit on a scale factor of 0.0; I am looking to accommodate this.
Here's a vastly more efficient version of the code you posted:
testImage = imageFromView()
var greenCount = 0
if let cfdata = testImage.cgImage?.dataProvider?.data {
let data = cfdata as Data
for i in stride(from: 0, to: data.count, by: 4) {
let r = data[i]
let g = data[i+1]
let b = data[i+2]
if g == 255 && r == 0 && b == 0 {
greenCount += 1
}
}
}
This code eliminates many inefficiencies in the original code. The data is only obtained once. No points are used. The data bytes are accessed directly. No UIColor
creation is needed. No conversion to CGFloat
is needed.
I would recommend caching the data
pointer:
let data: UnsafePointer<UInt8> = ...
as opposed to computing it from scratch every single time you call the getPixelColor
method.
Also: consider creating a simple struct Color
instead of using the standard class UIColor
. Creating (and deallocating) that many class
instances might get expensive quickly!
By the way, you should also add a simple break
after finding the desired pixel. Of course, assuming your final algorithm stays the same ;-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With