关于直方图均衡化以及自适应直方图均衡化(CLAHE)的理论可以去查阅相关博客或者课本,这里只是分享对bmp格式的图片的直方图均衡化处理以及自适应直方图均衡化(CLAHE)处理的代码。
准备工作:首先你得有图像的像素信息,图像的宽度和高度等等。
代码中用到的变量以及定义预先说明下:
int m_nWidth:图像的宽度
int m_nHeight:图像的高度
int m_nDibWidth:四字节对齐后的宽度,比如m_nWidth = 511,m_nDibWidth = 512。
BYTE *m_pImage:原图像数据信息,其大小为m_nDibWidth*m_nHeight
BYTE *m_pImage2:处理后的图像数据信息
下面贴直方图均衡化的代码:
int i, j,k;
BYTE* p, *p2;
int t,N = m_nWidth*m_nHeight;
double hist[256] = {0};
for (i = 0; i < m_nHeight; i++) {
for (j = 0; j < m_nWidth; j++) {
p = m_pImage + i*m_nDibWidth + j;
hist[*p] = hist[*p] + 1.0/N;//计算直方图,归一化的直方图,类似概率密度
}
}
double cum[256] = { 0 };
cum[0] = hist[0];
for (k = 1; k < 256; k++) {
cum[k] = cum[k - 1] + hist[k];//计算累加
}
for (i = 0; i < m_nHeight; i++) {
for (j = 0; j < m_nWidth; j++) {
p = m_pImage + i*m_nDibWidth + j;
p2 = m_pImage2 + i*m_nDibWidth + j;
*p2 = (BYTE)(cum[*p] * 255);
}
}
下面贴自适应直方图均衡化(CLAHE)的代码:
// TODO: 在此添加控件通知处理程序代码
int block = 8;
//将图像均匀分成等矩形大小,8行8列64个块是常用的选择
int width_block = m_nWidth / block;
int height_block = m_nHeight / block;
//存储各个直方图
int **tmp = NULL;
tmp = (int **)malloc(sizeof(int*)*block*block); //arr在这里可以看出成数组,数组的每个成员都是指向int类型的指针,这样每个指针指向的代表一行,共row行
for (int i = 0; i<block*block; i++) //为每行申请空间
{
tmp[i] = (int*)malloc(sizeof(int)* 256); //每一行有col列
}
//存储累积函数
float **C = NULL;
C = (float **)malloc(sizeof(float*)*block*block); //arr在这里可以看出成数组,数组的每个成员都是指向int类型的指针,这样每个指针指向的代表一行,共row行
for (int i = 0; i<block*block; i++) //为每行申请空间
{
C[i] = (float*)malloc(sizeof(float) * 256); //每一行有col列
}
//初始化为0
for (int i = 0; i < block*block; i++) {
for (int j = 0; j < 256; j++) {
C[i][j] = 0;
tmp[i][j] = 0;
}
}
//计算累积函数
for (int i = 0; i < block; i++)
{
for (int j = 0; j < block; j++)
{
int start_x = i * width_block;
int end_x = start_x + width_block;
int start_y = j * height_block;
int end_y = start_y + height_block;
int num = i + block*j;
int total = width_block * height_block;
for (int ii = start_x; ii < end_x; ii++)
{
for (int jj = start_y; jj < end_y; jj++)
{
int index = (int)(*(m_pImage + jj * m_nDibWidth + ii));
tmp[num][index]++;
}
}
//裁剪操作
int average = width_block * height_block / 255;
int LIMIT = 4 * average;
int steal = 0;
for (int k = 0; k < 256; k++)
{
if (tmp[num][k] > LIMIT) {
steal += tmp[num][k] - LIMIT;
tmp[num][k] = LIMIT;
}
}
int bonus = steal / 256;
//hand out the steals averagely
for (int k = 0; k < 256; k++)
{
tmp[num][k] += bonus;
}
//计算累积分布直方图
for (int k = 0; k < 256; k++)
{
if (k == 0)
C[num][k] = 1.0f * tmp[num][k] / total;
else
C[num][k] = C[num][k - 1] + 1.0f * tmp[num][k] / total;
}
}
}
// int[][] new_mat = new int[height][width];
//计算变换后的像素值
//根据像素点的位置,选择不同的计算方法
for (int i = 0; i < m_nWidth; i++)
{
for (int j = 0; j < m_nHeight; j++)
{
//four coners
if (i <= width_block / 2 && j <= height_block / 2)
{
int num = 0;
*(m_pImage2 + j*m_nDibWidth + i) = C[num][((int)(*(m_pImage + j*m_nDibWidth + i)))] * 255;
}
else if (i <= width_block / 2 && j >= ((block - 1)*height_block + height_block / 2)) {
int num = block*(block - 1);
*(m_pImage2 + j*m_nDibWidth + i) = C[num][((int)(*(m_pImage + j*m_nDibWidth + i)))] * 255;
}
else if (i >= ((block - 1)*width_block + width_block / 2) && j <= height_block / 2) {
int num = block - 1;
*(m_pImage2 + j*m_nDibWidth + i) = C[num][((int)(*(m_pImage + j*m_nDibWidth + i)))] * 255;
}
else if (i >= ((block - 1)*width_block + width_block / 2) && j >= ((block - 1)*height_block + height_block / 2)) {
int num = block*block - 1;
*(m_pImage2 + j*m_nDibWidth + i) = C[num][((int)(*(m_pImage + j*m_nDibWidth + i)))] * 255;
}
//four edges except coners
else if (i <= width_block / 2)
{
//线性插值
int num_i = 0;
int num_j = (j - height_block / 2) / height_block;
int num1 = num_j*block + num_i;
int num2 = num1 + block;
float p = (j - (num_j*height_block + height_block / 2)) / (1.0f*height_block);
float q = 1 - p;
*(m_pImage2 + j*m_nDibWidth + i) = ( q*C[num1][((int)(*(m_pImage + j*m_nDibWidth + i)))] + p*C[num2][((int)(*(m_pImage + j*m_nDibWidth + i)))] )*255;
}
else if (i >= ((block - 1)*width_block + width_block / 2)) {
//线性插值
int num_i = block - 1;
int num_j = (j - height_block / 2) / height_block;
int num1 = num_j*block + num_i;
int num2 = num1 + block;
float p = (j - (num_j*height_block + height_block / 2)) / (1.0f*height_block);
float q = 1 - p;
*(m_pImage2 + j*m_nDibWidth + i) = (q*C[num1][((int)(*(m_pImage + j*m_nDibWidth + i)))] + p*C[num2][((int)(*(m_pImage + j*m_nDibWidth + i)))]) * 255;
}
else if (j <= height_block / 2) {
//线性插值
int num_i = (i - width_block / 2) / width_block;
int num_j = 0;
int num1 = num_j*block + num_i;
int num2 = num1 + 1;
float p = (i - (num_i*width_block + width_block / 2)) / (1.0f*width_block);
float q = 1 - p;
*(m_pImage2 + j*m_nDibWidth + i) = (q*C[num1][((int)(*(m_pImage + j*m_nDibWidth + i)))] + p*C[num2][((int)(*(m_pImage + j*m_nDibWidth + i)))]) * 255;
}
else if (j >= ((block - 1)*height_block + height_block / 2)) {
//线性插值
int num_i = (i - width_block / 2) / width_block;
int num_j = block - 1;
int num1 = num_j*block + num_i;
int num2 = num1 + 1;
float p = (i - (num_i*width_block + width_block / 2)) / (1.0f*width_block);
float q = 1 - p;
*(m_pImage2 + j*m_nDibWidth + i) = (q*C[num1][((int)(*(m_pImage + j*m_nDibWidth + i)))] + p*C[num2][((int)(*(m_pImage + j*m_nDibWidth + i)))]) * 255;
}
//inner area
else {
int num_i = (i - width_block / 2) / width_block;
int num_j = (j - height_block / 2) / height_block;
int num1 = num_j*block + num_i;
int num2 = num1 + 1;
int num3 = num1 + block;
int num4 = num2 + block;
float u = (i - (num_i*width_block + width_block / 2)) / (1.0f*width_block);
float v = (j - (num_j*height_block + height_block / 2)) / (1.0f*height_block);
*(m_pImage2 + j * m_nDibWidth + i) = (int)((u*v*C[num4][((int)(*(m_pImage + j*m_nDibWidth + i)))] +
(1 - v)*(1 - u)*C[num1][((int)(*(m_pImage + j*m_nDibWidth + i)))] +
u*(1 - v)*C[num2][((int)(*(m_pImage + j*m_nDibWidth + i)))] +
v*(1 - u)*C[num3][((int)(*(m_pImage + j*m_nDibWidth + i)))]) * 255);
}
*(m_pImage2 + j*m_nDibWidth + i) = *(m_pImage2 + j*m_nDibWidth + i) + (*(m_pImage2 + j*m_nDibWidth + i) << 8) + (*(m_pImage2 + j*m_nDibWidth + i) << 16);
}
}
只能说:灰度图片的效果对比度是提升了,但其色彩的不丰富性导致看起来可能不咋地。
所以彩色图片的效果会更好些。