核心思路
首先,我们用直方图来表示一张图像:横坐标代表的是颜色值,纵坐标代表的是该颜色值在图像中出现次数。
![17ac7a1af77605e7c2afb4c4784e785f.png](https://i-blog.csdnimg.cn/blog_migrate/323d7d1291e412c6ce4718886e7e9edf.jpeg)
如图,对于某些图像,可能出现颜色值集中分布在某个区间的情况。
直方图均衡化(Histogram Equalization) ,指的是对图像的颜色值进行重新分配,使得颜色值的分布更加均匀。
本文用compute shader对图像的颜色值进行统计,然后计算得出映射关系,由fragment shader进行颜色映射处理。
效果展示
![1fa68d242d2841e8a73bf82ae64af724.png](https://i-blog.csdnimg.cn/blog_migrate/686604da6010d514bc82ae884148ef01.jpeg)
具体步骤
1、Metal的render管道、compute管道配置;
2、CPU进行直方图均衡化处理;
2.1 把UIImage转成Bytes;
2.2 颜色统计;
// CPU进行统计
Byte *color = (Byte *)spriteData; for (int i = 0; i < width * height; ++i) { for (int j = 0; j < LY_CHANNEL_NUM; ++j) {
uint c = color[i * 4 + j];
++cpuColorBuffer.channel[j][c];
}
}
2.3 映射关系;
int rgb[3][LY_CHANNEL_SIZE], sum = (int)(width * height); int val[3] = {0}; // 颜色映射
for (int i = 0; i < 3; ++i) { for (int j = 0; j < LY_CHANNEL_SIZE; ++j) {
val[i] += cpuColorBuffer.channel[i][j];
rgb[i][j] = val[i] * 1.0 * (LY_CHANNEL_SIZE - 1) / sum;
}
}
2.4 颜色值修改;
// 值修改
for (int i = 0; i < width * height; ++i) { for (int j = 0; j < LY_CHANNEL_NUM; ++j) {
uint c = color[i * 4 + j];
color[i * 4 + j] = rgb[j][c];
}
}
最后用处理之后的Bytes生成新图片。
3 GPU进行直方图均衡化处理;
3.1 compute shader进行颜色统计;
kernel voidgrayKernel(texture2d<float, access::read> sourceTexture [[textureLYKernelTextureIndexSource]], // 纹理输入,
device LYColorBuffer &out [[buffer(LYKernelBufferIndexOutput)]], // 输出的buffer
uint2 grid [[thread_position_in_grid]]) // 格子索引{ // 边界保护
if(grid.x < sourceTexture.get_width() && grid.y < sourceTexture.get_height())
{
float4 color = sourceTexture.read(grid); // 初始颜色
int3 rgb = int3(color.rgb * SIZE); // 乘以SIZE,得到