最近公司项目需求需要绘制出来图像的直方图,所以近期研究了一下。
图像的直方图是什么?
图像的直方图跟图像的亮度有很大关系。图像的亮度可以分为0-255个数值,不同的值代表不同的亮度,0代表纯黑色的黑暗区域,255表示最亮的纯白色区域。以亮度作为横轴,图像在不同的亮度下包含的像素的个数作为纵轴得到的图像就是图像的直方图。
有的童鞋想深入了解一下图像的直方图,可以看一下这篇 文章,这里不做过多赘述。
RGBA格式
RGB以R(Red,红色), G(Green,绿色),B(Blue,蓝色)三种基本颜色来通过不同程度的叠加来获取不同的颜色,我们使用RGBA的格式来组织图像的像素,所以一个像素是由4个byte组成的,分别是R,G,B,A,每一个分量都是由一个byte组成,所以每个分量的取值范围是[0,255]。本来我想的是使用CPU来遍历所有的像素,并把每个像素的R值的亮度,G的亮度,B的亮度,L(Luma)的亮度,并把计算这个数据的个数保存到四个256大小的数组中。当我得到一个图片的时候,我脑海中的第一个想法是把图像以RGBA的格式保存到一个Data中。
CPU
let context = CIContext()
let bytesPerRow = Int(image.extent.width) * 4
let bytesPointer = UnsafeMutableRawPointer.allocate(byteCount: bytesPerRow * Int(image.extent.height), alignment: 4)
context.render(image, toBitmap: bytesPointer, rowBytes: bytesPerRow, bounds: image.extent, format: .RGBA8, colorSpace: CGColorSpace(name: CGColorSpace.sRGB))
let pixelData = Data(bytes: bytesPointer, count: bytesPerRow * Int(image.extent.height))
//不要忘记销毁bytesPointer指向的空间
bytesPointer.deallocate()
然后把这个Data按照Uint8的格式进行解码保存到一个数组中,
let allBytes = [Uint8](pixelData)
最后遍历这个数组,生成我们需要的直方图的数据。
//保存[0-255]个亮度上所包含的Red的个数,用于绘制图像直方图
var red:[UInt32] = [UInt32](repeating: 0, count: 256)
//保存[0-255]个亮度上所包含的Green的个数,用于绘制图像直方图
var green:[UInt32] = [UInt32](repeating: 0, count: 256)
//保存[0-255]个亮度上所包含的Blue的个数,用于绘制图像直方图
var blue:[UInt32] = [UInt32](repeating: 0, count: 256)
//保存[0-255]个亮度上所包含的Luma的个数,用于绘制图像直方图
var luma:[UInt32] = [UInt32](repeating: 0, count: 256)
// 4个字节是表示一个像素
for i in 0..<allBytes.count where i % 4 == 0 {
var red =