计算图像中的颜色:`NSCountedSet`和`colorAtX`非常慢

时间:2015-06-24 12:36:01

标签: objective-c swift cocoa nscolor

我正在制作一个OS X应用程序,它根据图像的主要颜色创建颜色方案。

作为第一步,我使用NSCountedSetcolorAtX来获取图片中的所有颜色并计算其出现次数:

func sampleImage(#width: Int, height: Int, imageRep: NSBitmapImageRep) -> (NSCountedSet, NSCountedSet) {
    // Store all colors from image
    var colors = NSCountedSet(capacity: width * height)
    // Store the colors from left edge of the image
    var leftEdgeColors = NSCountedSet(capacity: height)
    // Loop over the image pixels
    var x = 0
    var y = 0
    while x < width {
        while y < height {
            // Instruments shows that `colorAtX` is very slow
            // and using `NSCountedSet` is also very slow
            if let color = imageRep.colorAtX(x, y: y) {
                if x == 0 {
                    leftEdgeColors.addObject(color)
                }
                colors.addObject(color)
            }
            y++
        }
        // Reset y every x loop
        y = 0
        // We sample a vertical line every x pixels
        x += 1
    }
    return (colors, leftEdgeColors)
}

我的问题是这很慢。在Instruments中,我发现存在两大瓶颈:NSCountedSetcolorAtX

所以首先我想可能用纯粹的Swift等价替换NSCountedSet,但新实现的速度比NSCountedSet慢得多。

对于colorAtX,有this有趣的SO答案,但我无法将其转换为Swift(我不能使用桥接头来为Objective-C项目)。

我在尝试翻译时遇到的问题是我不理解答案中的unsigned charchar部分。

我应该尝试比使用colorAtX更快地扫描颜色?

  • 继续努力调整Objective-C答案,因为这是一个很好的答案?尽管现在被困住,也许我可以在以后实现这一目标。

  • 使用我不知道的其他Foundation / Cocoa方法?

  • 还有什么我可以尝试改进我的代码吗?

TL; DR

colorAtX速度很慢,由于unsigned char,我不明白如何使this Objective-C answer适应Swift。

1 个答案:

答案 0 :(得分:1)

colorAtX()的最快替代方法是使用let bitmapBytes = imageRep.bitmapData迭代图像的原始字节,并根据该信息自己组合颜色,如果它只是RGBA数据,这应该非常简单。而不是你的x / y循环,做这样的事情......

let bitmapBytes = imageRep.bitmapData
var colors = Dictionary<UInt32, Int>()

var index = 0
for _ in 0..<(width * height) {
    let r = UInt32(bitmapBytes[index++])
    let g = UInt32(bitmapBytes[index++])
    let b = UInt32(bitmapBytes[index++])
    let a = UInt32(bitmapBytes[index++])
    let finalColor = (r << 24) + (g << 16) + (b << 8) + a   

    if colors[finalColor] == nil {
        colors[finalColor] = 1
    } else {
        colors[finalColor]!++
    }
}

你必须检查RGBA值的顺序,我猜对了!

维持计数的最快方法可能只是像素值的[Int,Int]字典计数,做colors[color]++之类的事情。如果需要,稍后可以使用NSColor(calibratedRed red: CGFloat, green green: CGFloat, blue blue: CGFloat, alpha alpha: CGFloat)

将其转换为NSColor