UIImage 循环通过像素效率非常低?

Posted

技术标签:

【中文标题】UIImage 循环通过像素效率非常低?【英文标题】:UIImage Loop Through Pixel Highly Inefficient? 【发布时间】:2015-02-16 00:37:20 【问题描述】:

目前我正在使用这种方法循环遍历每个像素,并根据 RGB 值将一个值插入到 3D 数组中。我的程序的其他部分需要这个数组,但是它非常慢。当在 50 x 50 的图片上运行时,它几乎是即时的,但是一旦你开始进入数百 x 数百,它需要很长时间才能使应用程序无用。有人对如何加快我的方法有任何想法吗?

@IBAction func convertImage(sender: AnyObject) 
    if let image = myImageView.image 

        var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
        var data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let height = Int(image.size.height)
        let width = Int(image.size.width)
        var zArry = [Int](count:3, repeatedValue: 0)
        var yArry = [[Int]](count:width, repeatedValue: zArry)
        var xArry = [[[Int]]](count:height, repeatedValue: yArry)

        for (var h = 0; h < height; h++) 
            for (var w = 0; w < width; w++) 
                 var pixelInfo: Int = ((Int(image.size.width) * Int(h)) + Int(w)) * 4
                var rgb = 0
                xArry[h][w][rgb] = Int(data[pixelInfo])
                rgb++
                xArry[h][w][rgb] = Int(data[pixelInfo+1])
                rgb++
                xArry[h][w][rgb] = Int(data[pixelInfo+2])
                
        
        println(xArry[20][20][1])
    

也许有一种方法可以将 UIImage 转换为不同类型的图像并创建像素数组。我愿意接受所有建议。谢谢!

GOAL:目标是使用数组修改所有像素的 RGB 值,并使用修改后的像素创建新图像。我尝试简单地循环遍历所有像素而不存储它们,并将它们修改为新数组以创建图像,但遇到了相同的性能问题。

【问题讨论】:

只是一个建议,但您可以从展开 rgb 循环开始。 好的,循环现在解开了。还是很慢。无论如何感谢您的建议,因为它可能有助于加快速度。 我的一个朋友可能会建议使用NEON指令(单指令/多数据)来并行读取数据,但我不知道细节,也许从swift(与 C/Objective-C 不同)。如果它至少可以为您提供指针... 您也可以尝试在 Xcode 中使用 Profiling 来查看实际慢的部分,以了解您需要优化什么。 这个问题是针对 OSX 的,但解决方案对我类似的问题帮助很大,也许它会帮助你:***.com/questions/31026763/… 【参考方案1】:

更新:

经过无数次尝试,我意识到我正在对 debug 配置进行测试。

切换到 release,现在速度快多了。

Swift 在调试配置上似乎慢了很多倍。

现在您的代码和我的优化版本之间的差异快了好几倍。

使用 image.size.width 而不是局部变量 width 似乎会大大降低速度。

原创

我尝试对其进行了一些优化并提出了这个:

@IBAction func convertImage () 

    if let image = UIImage(named: "test") 

        let pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
        let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

        let height = Int(image.size.height)
        let width = Int(image.size.width)
        let zArry = [Int](count:3, repeatedValue: 0)
        let yArry = [[Int]](count:width, repeatedValue: zArry)
        let xArry = [[[Int]]](count:height, repeatedValue: yArry)

        for (index, value) in xArry.enumerate() 
            for (index1, value1) in value.enumerate() 
                for (index2, var value2) in value1.enumerate() 

                    let pixelInfo: Int = ((width * index) + index1) * 4 + index2

                    value2 = Int(data[pixelInfo])
                
            
        
    

但在我的测试中,这仅快 15%。您需要的是更快的数量级。

另一个想法是在需要时直接使用数据对象,而无需像这样创建数组:

let image = UIImage(named: "test")!

let pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)

let width = Int(image.size.width)

// value for [x][y][z]
let value = Int(data[((width * x) + y) * 4 + z])

你没有说你是如何在你的应用程序中使用这个数组的,但是我觉得即使你找到一种方法来更快地创建这个数组,当你尝试使用它时,你会遇到另一个问题,因为它会也需要很长时间..

【讨论】:

如果你用加法代替乘法,你将获得巨大的性能提升。 repeatedValue: 的初始化也是性能的损失。 花在乘法上的时间大约是 1% i61.tinypic.com/35n7o7l.png 初始化时间似乎可以忽略不计。 我也会把这个放在原帖中,但目标是使用数组来修改所有像素的RGB值,并用修改后的像素创建一个新图像。我尝试简单地循环遍历所有像素而不存储它们,并将它们修改为新数组以创建图像,但遇到了相同的性能问题。 正是那个嵌套循环杀死了你。您可以尝试制作哈希映射以将复杂性降低到 O(1) 或 O(n) 而不是 O(n^2 log n)。或者甚至尝试使用 deepMap 风格的函数,这里有一个关于深度映射的线程,将其转换为 Swift 应该很简单:***.com/questions/25333918/js-deep-map-function 另一种想法是使用线程来更快地处理数据,只需将数据的大小相除(调用大小应该是 O(1)),然后产生 X 多个线程来处理范围内的像素0-10000、10000-20000、20000-30000等。

以上是关于UIImage 循环通过像素效率非常低?的主要内容,如果未能解决你的问题,请参考以下文章

UIImage 混合模式问题

在swift中循环变量

检索透明UIImage的最后一个非透明像素位置

无法设置 UIImage,总是返回 nil

没有 UIImageView 的 UIImage 的反射

如何在 swift 3 中使用 UIImage 数组为零?