AyB*_*Bay 2 image-processing core-image uiimage ios ciimage
我希望分析 iOS 上 UIImage 中最主要的颜色(颜色出现在大多数像素中),我偶然发现了 Core Image 的基于过滤器的 API,尤其是 CIAreaHistogram。
看起来这个过滤器可能对我有帮助,但我很难理解 API。首先它说过滤器的输出是一个一维图像,它是输入箱的长度和一个像素的高度。我如何读取这些数据?我基本上想找出频率最高的颜色值,所以我希望数据包含每种颜色的某种频率计数,我不清楚这个一维图像将如何表示,因为它实际上并不解释我在这个一维图像中可以期待的数据。如果它真的是一个直方图,为什么它不返回一个表示它的数据结构,比如字典
其次,在 API 中它要求多个 bin?那个输入应该是什么?如果我想要一个精确的分析,输入 bin 参数会是我图像的颜色空间吗?使 bin 值变小有什么作用,我想它只是通过到最近的 bin 的欧几里得距离来近似附近的颜色。如果是这种情况,那将不会产生精确的直方图结果,为什么会有人想要这样做呢?
从 API 角度对上述两个问题的任何输入都会对我有很大帮助
CIAreaHistogram返回一个图像,其中每个像素的 reg、绿色、蓝色和 alpha 值表示图像中该色调的频率。您可以将该图像渲染到一个数组UInt8以查看直方图数据。还有一个未记录的outputData值:
let filter = CIFilter(
name: "CIAreaHistogram",
withInputParameters: [kCIInputImageKey: image])!
let histogramData = filter.valueForKey("outputData")
Run Code Online (Sandbox Code Playgroud)
但是,我发现vImage这是一个更好的处理直方图的框架。首先,您需要创建一个vImage图像格式:
var format = vImage_CGImageFormat(
bitsPerComponent: 8,
bitsPerPixel: 32,
colorSpace: nil,
bitmapInfo: CGBitmapInfo(
rawValue: CGImageAlphaInfo.PremultipliedLast.rawValue),
version: 0,
decode: nil,
renderingIntent: .RenderingIntentDefault)
Run Code Online (Sandbox Code Playgroud)
vImage使用可以从而CGImage不是CIImage实例创建的图像缓冲区(您可以使用.createCGImage方法创建一个图像缓冲区:CIContextvImageBuffer_InitWithCGImage
var inBuffer: vImage_Buffer = vImage_Buffer()
vImageBuffer_InitWithCGImage(
&inBuffer,
&format,
nil,
imageRef,
UInt32(kvImageNoFlags))
Run Code Online (Sandbox Code Playgroud)
现在创建Uint将保存四个通道的直方图值的数组:
let red = [UInt](count: 256, repeatedValue: 0)
let green = [UInt](count: 256, repeatedValue: 0)
let blue = [UInt](count: 256, repeatedValue: 0)
let alpha = [UInt](count: 256, repeatedValue: 0)
let redPtr = UnsafeMutablePointer<vImagePixelCount>(red)
let greenPtr = UnsafeMutablePointer<vImagePixelCount>(green)
let bluePtr = UnsafeMutablePointer<vImagePixelCount>(blue)
let alphaPtr = UnsafeMutablePointer<vImagePixelCount>(alpha)
let rgba = [redPtr, greenPtr, bluePtr, alphaPtr]
let histogram = UnsafeMutablePointer<UnsafeMutablePointer<vImagePixelCount>>(rgba)
Run Code Online (Sandbox Code Playgroud)
最后一步是执行计算,这将填充四个数组,并释放缓冲区的数据:
vImageHistogramCalculation_ARGB8888(&inBuffer, histogram, UInt32(kvImageNoFlags))
free(inBuffer.data)
Run Code Online (Sandbox Code Playgroud)
快速检查alpha不透明图像的数组应该产生 255 个零,最终值对应于图像中的像素数:
print(alpha) // [0, 0, 0, 0, 0 ... 409600]
Run Code Online (Sandbox Code Playgroud)
从视觉角度来看,直方图不会为您提供主色:半黄{1,1,0}半黑{0,0,0}的图像将给出与半红半{1,0,0}绿的图像相同的结果{0,1,0}。
希望这可以帮助,
西蒙
伊恩·奥尔曼 (Ian Ollmann) 仅针对色调计算直方图的想法非常巧妙,可以使用简单的颜色内核来完成。此内核返回仅具有图像色调的单色图像(基于此原始作品)
let shaderString = "kernel vec4 kernelFunc(__sample c)" +
"{" +
" vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);" +
" vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));" +
" vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));" +
" float d = q.x - min(q.w, q.y);" +
" float e = 1.0e-10;" +
" vec3 hsv = vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);" +
" return vec4(vec3(hsv.r), 1.0);" +
"}"
let colorKernel = CIColorKernel(string: shaderString)
Run Code Online (Sandbox Code Playgroud)
如果我得到蓝天图像的色调,则生成的直方图如下所示:
...而温暖的日落给出了这样的直方图:
所以,这看起来是一种获得图像主色调的好技术。
西蒙