Metal图像处理——直方图均衡化
Java编程精选
点击右侧关注,免费入门到精通!
作者:落影loyinglin
链接:https://www.jianshu.com/p/1c8e814edab4
前言
Metal入门教程总结
正文
核心思路
首先,我们用直方图来表示一张图像:横坐标代表的是颜色值,纵坐标代表的是该颜色值在图像中出现次数。
如图,对于某些图像,可能出现颜色值集中分布在某个区间的情况。
直方图均衡化(Histogram Equalization) ,指的是对图像的颜色值进行重新分配,使得颜色值的分布更加均匀。
本文用compute shader对图像的颜色值进行统计,然后计算得出映射关系,由fragment shader进行颜色映射处理。
效果展示
具体步骤
1、Metal的render管道、compute管道配置;
同前文,不再赘述,详见
Metal入门教程总结
。
2、CPU进行直方图均衡化处理;
2.1 把UIImage转成Bytes;
2.2 颜色统计;
// CPU进行统计 for int 0 for int 0 uint 4
2.3 映射关系; 3 LY_CHANNEL_SIZE < 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 0 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进行颜色统计;
[[textureLYKernelTextureIndexSource]] [[buffer(LYKernelBufferIndexOutput)]]kernel voidgrayKernel(texture2dread> sourceTexture
, // 输出的bufferuint2 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,得到[0
,255
]的颜色值// 颜色统计,每个像素点计一次atomic_fetch_add_explicit(&out.channel;[0
][rgb.r],1
, memory_order_relaxed);atomic_fetch_add_explicit(&out.channel;[1
][rgb.g],1
, memory_order_relaxed);atomic_fetch_add_explicit(&out.channel;[2
][rgb.b],
1
, memory_order_relaxed);}}read>
atomic_fetch_add_explicit是用于在多线程进行数据操作,具体的函数解释见这里。
3.2 映射关系处理;
compute shader回调后,根据GPU统计的颜色分布结果,求出映射关系;
*buffer = (LYLocalBuffer * < 3 i for int j 0; j LY_CHANNEL_SIZE j val i buffer-LYLocalBuffer
i
][j
]; // 当前[0, j]累计出现的总次数convertBuffer->channel[i
][j
] = val[i]* 1.0 *
(LY_CHANNEL_
SIZE - 1) / sum;// 对比CPU和GPU处理的结果if (buffer->channel[i
][j
] != strongSelf->cpuColorBuffer.channel[i
][j
]) { // 如果不相同,则把对应的结果输出printf("%d, %d, gpuBuffer:%u cpuBuffer:%u", i, j, buffer->channel[
i
][j
], strongSelf->cpuColorBuffer.channel[i
][j
]);}}}memset(buffer, 0, strongSelf.colorBuffer.length);3.3 根据映射关系处理原图片,并渲染到屏幕上;
_in]], // stage_ < floatfragment float4samplingShader(RasterizerData input [[stage
_filter::linear, min_
filter::linear); // sampler是采样器float4 colorSample = colorTexture.sample(textureSampler, input.textureCoordinate); // 得到纹理对应位置的颜色int3 rgb = int3(colorSample.rgb * SIZE); // 记得先乘以SIZEcolorSample.rgb = float3(convertBuffer.channel[0
][rgb.r
], convertBuffer.channel[1
][rgb.g
], convertBuffer.channel[2
][rgb.b
]) / SIZE; // 返回的值也要经过归一化处理return colorSample;}