加速像素迭代

5

我遇到的问题是无法快速地迭代所有UIImage中的像素,检查每个颜色是否与特定颜色(绿色)相同。迭代必须在用户启动时立即处理。目前存在约0.5秒的延迟。

我正在使用这个来创建视图的截图:

func screenshot() -> UIImage { 

    UIGraphicsBeginImageContextWithOptions(view.bounds.size, false, UIScreen.main.scale)
    view.layer.render(in: UIGraphicsGetCurrentContext()!)
    let screenShot = UIGraphicsGetImageFromCurrentImageContext()
    UIGraphicsEndImageContext()
    return screenShot!  
}

获取单个像素颜色的方法如下:

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)
    }
}

同时,需要循环检查图像的每个像素:

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
        }
    }
}

希望能得到您的建议。

编辑

当比例因子为0.0时,性能会受到影响;我正在寻求解决方法。


FYI - 这样的问题可能更适合 https://codereview.stackexchange.com。 - rmaddy
我应该在那里发布相同的问题吗? - Tim
1
不要重复发布,请选择一个地方发布。 - rmaddy
3个回答

4

这是一份比您发布的代码更高效的版本:

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
        }
    }
}

这段代码消除了原始代码中的许多低效性。数据仅获取一次。不使用点。数据字节直接访问。无需创建UIColor。无需转换为CGFloat


出于好奇,这段代码相比你原来的代码快了多少? - rmaddy
这几乎是瞬间完成的,不像以前那样了。非常感谢。 - Tim
@Tim,我看到你取消了答案。代码有问题吗? - rmaddy
只有在非常老的iOS设备上,如iPad 2或iPhone 3G上,比例尺才会是1.0。那些旧设备非常慢。您真的需要支持这些旧设备使用的旧版本的iOS吗? - rmaddy
这其实更有意义。当您使用0时,它会使用设备的屏幕比例。您的旧代码生成1x图像。现在,使用UIGraphicsBeginImageContextWithOptions,您正在生成2x和3x图像。这是4倍和9倍的像素数量,因此处理时间将增加4或9倍。 - rmaddy
显示剩余2条评论

1
我建议将 data 指针进行缓存
let data: UnsafePointer<UInt8> = ...

与每次调用 getPixelColor 方法时从头开始计算相反。

另外:考虑创建一个简单的 struct Color,而不是使用标准的 class UIColor。创建(和释放)那么多的 class 实例可能会很快变得昂贵!


顺便提一下,找到所需的像素后,您还应该添加一个简单的break。当然,假设您的最终算法保持不变;-)

1
这太大了。目前的代码非常低效。 - rmaddy
感谢您的回复,@Paulo Mattos。我正在研究这个问题。 - Tim

0

首先,一旦找到绿色像素,请立即跳出循环:

yLoop: 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
            break yLoop
        }
    }
}

既然你只想知道是否存在一个绿色像素,那么在找到一个后继续查找就没有意义了。这将平均加快您的检查速度50%(尽管右下角的单个绿色像素所需时间与以往相同)。


谢谢您的回复。我考虑过这个问题,但我需要迭代每个像素。问题已更新。 - Tim

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接