本章说明
一些最为基础的图像属性的计算公式 1 ~ 3
一些图像降噪公式 4 ~ 7
一些图像拉伸对比度公式 8 ~ 12
1.噪声的计算
/*
* 灰度图像噪声的计算
* 1.遍历图像的每一个像素,将像素灰度值的和累加起来。
* 2.计算像素灰度值的平均值(即算术平均数)。
* 3.再次遍历图像的每一个像素,将像素灰度值的平方和累加起来。
* 4.计算像素灰度值的方差,即平均值和平方平均值的差,其实就是图像的能量分布情况。
* 5.标准差是方差的平方根,表示图像的分布集中程度,可以作为评估图像噪声的指标。
* 因此,计算灰度图像噪声的函数就是根据这个原理来实现的,遍历图像所有像素求和,计算平均值和方差标准差,最后返回标准差作为图像噪声的值。
*/
double calculateNoise(unsigned char *image, int width, int height)
{
unsigned int sum = 0, sum_sq = 0, count = 0;
double mean, var, noise;
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
int pixel = *(image + i * width + j);
sum += pixel;
sum_sq += pixel * pixel;
count++;
}
}
mean = (double)sum / count;
var = (double)sum_sq / count - mean * mean;
noise = sqrt(var);
return noise;
}
2.计算平均灰度
int calcAverageGrayValue(unsigned char *image, int width, int height)
{
int gray_sum = 0;
int pixel_num = width * height;
for(int i = 0; i < pixel_num; i++)
{
gray_sum += image[i];
}
return gray_sum / pixel_num;
}
3.计算对比度
/*
* 计算对比度
* 对比度代表了图像中不同区域亮度差异的程度,是指图像中最亮区域和最暗区域之间灰度级的分布范围。
* 计算灰度图像的对比度,可以通过比较每个像素的灰度值与平均灰度值之间的差距来衡量图像的对比度大小。
* 图像的对比度越大,表示图像中不同区域间的灰度值差异越大,图像看起来越清晰。
* 反之,图像的对比度越小,表示图像中不同区域间的灰度值差异越小,图像看起来越模糊。
*/
int calcGrayContrast(unsigned char *image, int width, int height)
{
int pixel_num = width * height;
int averageValue = calcAverageGrayValue(image, width, height); //计算平均灰度值
double contrast_sum = 0.0;
for(int i = 0; i < pixel_num; i++)
{
contrast_sum += abs(image[i] - averageValue);
}
return (int)(contrast_sum / pixel_num);
}
4.均值滤波
/*
* 均值滤波。
* 其中,input是输入图像的指针,output是输出图像的指针,width和height分别是图像的宽和高,kernel_size是均值滤波的核大小。
* 在函数中,我们首先计算kernel_size的一半,并遍历输出图像的每个像素(注意,我们从kernel_size/2开始遍历,避免边界问题)。
* 对于每个像素,我们计算在当前像素周围kernel_size大小的区域内像素的平均值,并将该平均值存储在输出图像中的对应位置,完成均值滤波。
* 需要注意的是,对于图像的边缘像素,在进行均值滤波时需要进行特殊处理,可以将边缘像素直接赋值为输入图像中的对应像素值。
*/
void meanFilter(unsigned char *input, unsigned char *output, int width, int height, int kernelSize)
{
int kernel = kernelSize / 2;
int sum, count, row, col, i, j;
for(row = kernel; row < height - kernel; row++)
{
for(col = kernel; col < width - kernel; col++)
{
sum = 0;
count = 0;
for(i =- kernel; i <= kernel; i++)
{
for(j =- kernel; j <= kernel; j++)
{
sum += input[(row+i)*width+(col+j)];
count++;
}
}
output[row*width+col] = sum / count;
}
}
}
5.中值滤波
/*
* 中值滤波
* kernel_size: 中值滤波核的大小,应为奇数。如传入 3 表示使用 3x3 的中值滤波核。
* 值滤波是一种非线性滤波算法,其基本思想是将当前像素邻域内所有像素的灰度值排序,然后取其中位数作为中心像素的新灰度值。
* 对每个像素,我们使用一个大小为 kernel_size × kernel_size 的滤波核,
* 将其邻域内的像素灰度值取出,进行从小到大排序,然后取其中间值,赋值给当前像素。
* 由于滤波核大小固定,且只涉及到相邻像素的灰度值比较和排序操作,时间复杂度为 O(n^2 log n),其中 n 表示图像的宽高之和。
*/
void medianFilter(unsigned char *input, unsigned char *output, int width, int height, int kernel_size)
{
int half_kernel_size = kernel_size / 2;
unsigned char *buffer = (unsigned char *)malloc(sizeof(unsigned char) * kernel_size * kernel_size);
for (int y = half_kernel_size; y < height - half_kernel_size; y++)
{
for (int x = half_kernel_size; x < width - half_kernel_size; x++)
{
int index = 0;
for (int j = -half_kernel_size; j <= half_kernel_size; j++)
{
for (int i = -half_kernel_size; i <= half_kernel_size; i++)
{
buffer[index++] = input[(y + j) * width + (x + i)];
}
}
// 使用快速排序算法求中值
int left = 0, right = kernel_size * kernel_size - 1;
int mid = (left + right) >> 1;
while (left < right) {
int pivot = buffer[mid], i = left, j = right;
buffer[mid] = buffer[right];
buffer[right] = pivot;
while (i < j)
{
while (i < j && buffer[i] <= pivot)
i++;
while (i < j && buffer[j] >= pivot)
j--;
if (i < j)
{
int temp = buffer[i];
buffer[i] = buffer[j];
buffer[j] = temp;
}
}
buffer[right] = buffer[i];
buffer[i] = pivot;
if (i == mid) break;
else if (i < mid) left = i + 1;
else right = i - 1;
mid = (left + right) >> 1;
}
output[y * width + x] = buffer[mid];
}
}
free(buffer);
}
6.双边滤波
/*
* 双边滤波。
* 双边滤波是一种非线性滤波方法,能够在去噪的同时保留图像的边缘信息。
* 定义双边滤波核函数:双边滤波核函数包括两个部分,空间部分和灰度部分。
* 空间部分用于计算像素之间的距离,灰度部分用于计算像素之间的相似度。
* 核函数通常采用高斯函数和指数函数的乘积形式。
* 对于每个像素,遍历其周围的像素,计算它们之间的距离和相似度。根据计算结果对像素进行加权平均,得到滤波后的像素值。
* sigma_d的取值应该大于0,通常在1~10之间。
* sigma_r的取值应该大于0,通常在10~100之间。
*/
void bilateralFilter(unsigned char* src, unsigned char* dst, int width, int height, int radius, float sigma_d, float sigma_r)
{
int i, j, k, x, y;
float w, d, r, sum, weight;
float *distance = (float*)calloc(width * height, sizeof(float));
float *gaussian_d = (float*)calloc(2 * radius + 1, sizeof(float));
float *gaussian_r = (float*)calloc(256, sizeof(float));
// 计算空间高斯权重
for (i = -radius; i <= radius; i++)
{
gaussian_d[i + radius] = exp(-(float)(i * i) / (2 * sigma_d * sigma_d));
}
// 计算灰度值高斯权重
for (i = 0; i < 256; i++)
{
gaussian_r[i] = exp(-(float)(i * i) / (2 * sigma_r * sigma_r));
}
// 遍历图像每个像素
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
sum = 0;
weight = 0;
// 遍历核函数范围内的像素
for (x = -radius; x <= radius; x++)
{
if (i + x < 0 || i + x >= height)
continue;
for (y = -radius; y <= radius; y++)
{
if (j + y < 0 || j + y >= width)
continue;
// 计算像素之间的距离和相似度
d = sqrt((float)(x * x + y * y));
r = abs(src[(i + x) * width + (j + y)] - src[i * width + j]);
w = gaussian_d[(int)d] * gaussian_r[(int)r];
// 加权平均
sum += w * src[(i + x) * width + (j + y)];
weight += w;
}
}
// 更新像素值
dst[i * width + j] = (unsigned char)(sum / weight);
}
}
free(distance);
free(gaussian_d);
free(gaussian_r);
}
7.高斯滤波
/*
* 高斯滤波
* src:灰度图像数据缓存区的地址,数据格式为unsigned char(即单通道灰度图像,每个像素占1字节)
* dst:输出缓存区的地址,也是灰度图像数据缓存区的地址,但数据是通过高斯滤波计算后得到的平滑后的值。
* width:图像的宽度,以像素为单位。
* height:图像的高度,以像素为单位。
* sigma:高斯滤波的标准差。
*/
void gaussianFilter(unsigned char *input, unsigned char *output, int width, int height, float sigma)
{
int r = ceil(3 * sigma); // 根据sigma计算高斯核的半径
float* kernel = (float*)malloc((2 * r + 1) * sizeof(float)); // 定义高斯核
float sum = 0.0f;
for (int i = -r; i <= r; i++)
{
kernel[i + r] = exp(-i * i / (2 * sigma * sigma));
sum += kernel[i + r];
}
for (int i = -r; i <= r; i++)
{
kernel[i + r] /= sum; // 归一化高斯核
}
unsigned char* temp = (unsigned char*)malloc(width * height * sizeof(unsigned char)); // 定义临时数组
for (int y = 0; y < height; y++)
{
/* 以行为基本处理单位 */
for (int x = 0; x < width; x++)
{
float pixel = 0.0f;
for (int i = -r; i <= r; i++)
{
if (x + i >= 0 && x + i < width)
{
pixel += kernel[i + r] * input[y * width + x + i];
}
}
temp[y * width + x] = pixel;
}
}
for (int y = 0; y < height; y++)
{
/* 以列为基本处理单位 */
for (int x = 0; x < width; x++)
{
float pixel = 0.0f;
for (int i = -r; i <= r; i++)
{
if (y + i >= 0 && y + i < height)
{
pixel += kernel[i + r] * temp[(y + i) * width + x];
}
}
output[y * width + x] = (unsigned char)pixel;
}
}
free(kernel);
free(temp);
}
8.线性拉伸
void linearStretching(unsigned char *input, unsigned char *output, int width, int height)
{
int i, j, k;
unsigned char pixelValue;
int minVal = 255, maxVal = 0;
float new_minVal = 0.0, new_maxVal = 255.0;
/* 遍历整个图像,找到最小和最大灰度值 */
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
pixelValue = input[i * width + j];
if (pixelValue < minVal)
{
minVal = pixelValue;
}
if (pixelValue > maxVal)
{
maxVal = pixelValue;
}
}
}
/* 将像素值进行线性变换 */
for (i = 0; i < height; i++)
{
for (j = 0; j < width; j++)
{
pixelValue = input[i * width + j];
output[i * width + j] = (unsigned char)((pixelValue - minVal) * (new_maxVal - new_minVal) / (maxVal - minVal) + new_minVal);
}
}
}
9.直方图均衡化
void histEqualization(unsigned char *input, unsigned char *output, int width, int height)
{
// 计算每个灰度级别的像素数目
int hist[256] = { 0 };
for(int i = 0; i < (width * height); i++)
{
int gray = input[i];
hist[gray]++;
}
// 计算每个灰度级别在图像中出现的概率
float prob[256] = { 0 };
for(int i = 0; i < 256; i++)
{
prob[i] = (float)hist[i] / (width * height);
}
// 计算累计分布函数
float cdf[256] = { 0 };
cdf[0] = prob[0];
for(int i = 1; i < 256; i++)
{
cdf[i] = cdf[i - 1] + prob[i];
}
// 计算映射表
int map[256];
for(int i = 0; i < 256; i++)
{
map[i] = (int)(255.0 * cdf[i] + 0.5);
}
// 将像素灰度值替换为经过映射后的灰度值
for (int i = 0; i < (width*height); i++)
{
int gray = input[i];
int new_gray = map[gray];
output[i] = new_gray;
}
}
10.自适应直方图均衡化
/*
* 自适应直方图均衡化主要分为以下几个步骤:
* 对原图像进行分块,通常可选择16x16的大小(可以根据实际情况进行调整),对每个块进行处理。
* 对每个块进行直方图计算。
* 对每个块的直方图进行归一化。
* 对每个块进行直方图累积,得到累积直方图。
* 对块内每个像素进行灰度级映射,将其映射到累积直方图上的灰度级。
* 将处理后的块合并成一张图像。
*/
/* 计算像素值的直方图 */
static void calculate_histogram(unsigned char *image, int *histogram, int x, int y, int w, int h, int w_size)
{
int i, j;
for (i = 0; i < 256; i++)
{
histogram[i] = 0;
}
for (i = x - w_size / 2; i <= x + w_size / 2; i++)
{
for (j = y - w_size / 2; j <= y + w_size / 2; j++)
{
if (i >= 0 && i < h && j >= 0 && j < w)
{
histogram[image[i * w + j]]++;
}
}
}
}
/* 计算直方图的累积分布函数 */
static void calculate_cdf(int *histogram, float *cdf, int w_size)
{
int i, sum = 0;
for (i = 0; i < 256; i++)
{
sum += histogram[i];
cdf[i] = (float)sum / (w_size * w_size);
}
}
/* 映射像素值到0到255的范围内 */
static unsigned char map_pixel_value(unsigned char pixel, float *cdf)
{
return (unsigned char)(cdf[pixel] * 255.0);
}
/* 自适应直方图均衡化 */
void adaptiveHistogramEqualization(unsigned char *input, unsigned char *output, int width, int height, int blockSize)
{
int x, y;
int histogram[256];
float cdf[256];
for (x = 0; x < height; x++)
{
for (y = 0; y < width; y++)
{
calculate_histogram(input, histogram, x, y, width, height, blockSize);
calculate_cdf(histogram, cdf, blockSize);
output[x * width + y] = map_pixel_value(input[x * width + y], cdf);
}
}
}
11.对数变化
/*
* 对数变换
* 对数变换适用于图像整体较暗,需要增强图像中暗色区域细节的情况
*/
void logarithmTransformation(unsigned char *input, unsigned char *output, int width, int height)
{
double c = 255 / log10(256); // 计算调节系数
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
int gray = input[i*width + j];
double new_gray = c * log10(1.0 + gray);
output[i*width+j] = (unsigned char)new_gray; // 将变换后的灰度值写回数组中
}
}
}
12.幂次变化
/* 幂次变换 */
void powerTransformation(unsigned char *input, unsigned char *output, int width, int height, double gamma)
{
// 对每个像素值进行幂次运算,并更新像素值
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++)
{
float pixel_value = pow(input[i * width + j] / 255.0, gamma);
output[i * width + j] = (unsigned char)(pixel_value * 255.0);
}
}
}