我正在制作一个OS X应用程序,它根据图像的主要颜色创建颜色方案。
作为第一步,我使用NSCountedSet
和colorAtX
来获取图片中的所有颜色并计算其出现次数:
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中,我发现存在两大瓶颈:NSCountedSet
和colorAtX
。
所以首先我想可能用纯粹的Swift等价替换NSCountedSet
,但新实现的速度比NSCountedSet
慢得多。
对于colorAtX
,有this有趣的SO答案,但我无法将其转换为Swift(我不能使用桥接头来为Objective-C项目)。
我在尝试翻译时遇到的问题是我不理解答案中的unsigned char
和char
部分。
我应该尝试比使用colorAtX
更快地扫描颜色?
继续努力调整Objective-C答案,因为这是一个很好的答案?尽管现在被困住,也许我可以在以后实现这一目标。
使用我不知道的其他Foundation / Cocoa方法?
还有什么我可以尝试改进我的代码吗?
TL; DR
colorAtX
速度很慢,由于unsigned char
,我不明白如何使this Objective-C answer适应Swift。
答案 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)