图片处理 02分段线性变换+均值滤波+中值滤波
对比度拉伸
为了生成有更高对比度的图片,将输入图像 (Input Image) 中低于L的灰度值,在输出图像 (Output Image) 中全部设为黑(灰度值为0);将输入图像 (Input Image) 中高于H的灰度值,在输出图像 (Output Image) 中全部设为白(灰度值为255);将输入图像 (Input Image) 中L到H之间的灰度值,进行线性缩放 (Linearly Scale);
而分段对比度拉伸这一操作的具体形式实际上和上面的操作类似,我们从曲线图就可以很直观的了解:
可以采用如下分段函数:
0<=x<90:f(x)=0.25*x
90<=x<160:f(x)=1.25*x - 0.75
60<=x<250:f(X)=0.25*x + 40
代码如下:
//对比度拉伸
void CBmp::contrastStretching(unsigned char* img_data,
int bmpWidth, int bmpHeight, int lineByte)
{
// 定义分段线性函数的参数
const double a1 = 0.25;
const double b1 = 0.0;
const double a2 = 1.25;
const double b2 = -75.0;
const double a3 = 0.25;
const double b3 = 4.0;
for (int i = 0; i < bmpHeight; i++) {
for (int k = 0; k < bmpWidth; k++) {
unsigned char pixelValue = img_data[3 * k];
// 分段线性函数
if (pixelValue >= 0 && pixelValue < 90) {
pixelValue = static_cast<unsigned char>(a1 * pixelValue+b1);
}
else if (pixelValue >= 90 && pixelValue < 160) {
pixelValue = static_cast<unsigned char>(a2 * pixelValue+b2);
}
else if (pixelValue >= 160 && pixelValue < 250) {
pixelValue = static_cast<unsigned char>(a3 * pixelValue+b3);
}
img_data[3 * k] = pixelValue;
img_data[3 * k + 1] = pixelValue;
img_data[3 * k + 2] = pixelValue;
}
img_data += lineByte;
}
}
均值滤波
原理:均值滤波是典型的 线性滤波算法
,是指用当前像素点周围nxn个像素值的均值来代替当前像素值。使用该方法遍历处理图像内的每一个像素点,可完成整幅图像的均值滤波。
举个例子:
计算方法如下:
像素点新值=
[(197+25+106+156+159)
(149+40+107+5+71)+
(163+198+226+223+156) +
(222+37+68+193+157)+
(42+72+250+41+75)]/25 = 126
代码:
// 均值滤波
void CBmp::meanFilter(unsigned char* img_data, int bmpWidth, int bmpHeight, int lineByte, int filterSize)
{
int halfFilter = filterSize / 2; //滤波器一般为正方形,/2就为滤波器的半径
for (int i = halfFilter; i < bmpHeight - halfFilter; i++) { //遍历图像每一个像素,在这里避免了边界像素值
for (int k = halfFilter; k < bmpWidth - halfFilter; k++) {
double sumR = 0.0, sumG = 0.0, sumB = 0.0;
// 计算均值
for (int m = -halfFilter; m <= halfFilter; m++) { //遍历滤波器每一个像素
for (int n = -halfFilter; n <= halfFilter; n++) {
sumB += img_data[3 * (i + m) * bmpWidth + 3 * (k + n) + 0]; // 蓝色通道 3 * (i + m) * bmpWidth为行,3 * (k + n) + 0为列
sumG += img_data[3 * (i + m) * bmpWidth + 3 * (k + n) + 1]; // 绿色通道
sumR += img_data[3 * (i + m) * bmpWidth + 3 * (k + n) + 2]; // 红色通道
}
}
// 计算均值并更新像素值
double pixelValueR = sumR / (filterSize * filterSize); //filterSize * filterSize为滤波器边长*编程,结果是滤波器像素个数总和
double pixelValueG = sumG / (filterSize * filterSize);
double pixelValueB = sumB / (filterSize * filterSize);
img_data[3 * (i * bmpWidth + k) + 0] = static_cast<unsigned char>(pixelValueB);//将double型转换为unsigned char型
img_data[3 * (i * bmpWidth + k) + 1] = static_cast<unsigned char>(pixelValueG);
img_data[3 * (i * bmpWidth + k) + 2] = static_cast<unsigned char>(pixelValueR);
}
}
实现效果:
中值滤波
原理:以某像素的领域图像区域中的像素值的排序为基础,将像素领域内灰度的中值代替该像素的值,使用该方法遍历处理图像内的每一个像素点,可完成整幅图像的均值滤波。
中值滤波对处理椒盐噪声非常有效。
代码:
//中值滤波
void CBmp::medianFilter(unsigned char* img_data,
int bmpWidth, int bmpHeight, int lineByte)
{
// 图像数据副本,确保在计算当前像素的中值时,不会受到已经计算过的像素值的影响
unsigned char* img_copy = new unsigned char[bmpWidth * bmpHeight * 3];
memcpy(img_copy, img_data, bmpWidth * bmpHeight * 3);
for (int i = 3; i < bmpHeight - 3; i++) {
for (int k = 3; k < bmpWidth - 3; k++) {
// 7x7邻域内的像素值数组
unsigned char valuesR[49], valuesG[49], valuesB[49];
// 获取7x7邻域内的像素值
for (int m = -3, idx = 0; m <= 3; m++) {
for (int n = -3; n <= 3; n++, idx++) {
valuesR[idx] = img_copy[3 * ((i + m) * bmpWidth + (k + n))];
valuesG[idx] = img_copy[3 * ((i + m) * bmpWidth + (k + n)) + 1];
valuesB[idx] = img_copy[3 * ((i + m) * bmpWidth + (k + n)) + 2];
}
}
//中值查找
int middleIndex = 24; // (7 * 7) / 2 = 24
img_data[3 * (i * bmpWidth + k)] = findMedian(valuesR, 49, middleIndex);
img_data[3 * (i * bmpWidth + k) + 1] = findMedian(valuesG, 49, middleIndex);
img_data[3 * (i * bmpWidth + k) + 2] = findMedian(valuesB, 49, middleIndex);
}
}
delete[] img_copy;
}
// 中值查找函数
unsigned char CBmp::findMedian(unsigned char* arr, int size, int middleIndex)
{
// 使用选择排序找到中值
for (int i = 0; i < size - 1; i++) {
for (int j = i + 1; j < size; j++) {
if (arr[j] < arr[i]) {
// 交换 arr[i] 和 arr[j]
unsigned char temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
return arr[middleIndex];
}
实现效果:
参考博文:
Computer Vision笔记01:图像处理_丢弃的原因 对比度阈值-CSDN博客
均值滤波(Mean filtering)-CSDN博客