图像处理笔记(二)灰度变换和空间滤波基础
Published in:2024-03-25 | category: 智能车
Words: 1.9k | Reading time: 8min | reading:

灰度变换和空间滤波基础

灰度变换(灰度反转,对数变换,幂律变换)

灰度变换原理

变换原理:通过变换函数T将原图像像素灰度值r映射为灰度值s:

$s=T(r)$

灰度反转

原理:

灰度反转:将图像亮暗对调,可以增强图像中暗色区域细节
$s=T(r)=L-1-r$
其中L为图像灰度级,0~255灰度图像的灰度级为256.

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include<iostream>
#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
Mat image1, output_image, image1_gray; //定义输入图像,输出图像,灰度图像
image1 = imread("fire.jpg"); //读取图像;
if (image1.empty())
{
cout << "读取错误" << endl;
return -1;
}

cvtColor(image1, image1_gray, COLOR_BGR2GRAY); //灰度化
imshow(" image1_gray", image1_gray); //显示灰度图像

output_image = image1_gray.clone();
//遍历像素点
for (int i = 0; i < image1_gray.rows; i++)
{
for (int j = 0; j < image1_gray.cols; j++)
{
output_image.at<uchar>(i, j) = 255 - image1_gray.at<uchar>(i, j); //灰度反转
}
}
imshow("output_image", output_image); //显示反转图像


waitKey(0); //暂停,保持图像显示,等待按键结束
return 0;
}

输出

对数变换

原理

对数变换:扩展图像中的暗像素值,压缩高灰度值。
$s=T(r)=c*log(1+r)$
$log(1+r)$斜率逐渐降低,上升趋势逐渐放缓

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<iostream>
#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
Mat image1, output_image, image1_gray; //定义输入图像,输出图像,灰度图像
image1 = imread("fire.jpg"); //读取图像;
if (image1.empty())
{
cout << "读取错误" << endl;
return -1;
}

cvtColor(image1, image1_gray, COLOR_BGR2GRAY); //灰度化
imshow(" image1_gray", image1_gray); //显示灰度图像

output_image = image1_gray.clone();
for (int i = 0; i < image1_gray.rows; i++)
{
for (int j = 0; j < image1_gray.cols; j++)
{
output_image.at<uchar>(i, j) = 6 * log((double)(image1_gray.at<uchar>(i, j)) + 1); //对数变换 s=6*log(r+1)
}
}
normalize(output_image, output_image, 0, 255, NORM_MINMAX); //图像归一化,转到0~255范围内
convertScaleAbs(output_image, output_image); //数据类型转换到CV_8U
imshow(" output_image", output_image); //显示变换图像


waitKey(0); //暂停,保持图像显示,等待按键结束
return 0;
}

输出

幂律(伽马变换)

原理

幂律变换与对数变换类似:
$s=T(r)=c*r^γ$

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<iostream>
#include<opencv2/opencv.hpp>

using namespace cv;
using namespace std;

int main()
{
Mat image1, output_image, image1_gray; //定义输入图像,输出图像,灰度图像
image1 = imread("lena.png"); //读取图像;
if (image1.empty())
{
cout << "读取错误" << endl;
return -1;
}

cvtColor(image1, image1_gray, COLOR_BGR2GRAY); //灰度化
imshow(" image1_gray", image1_gray); //显示灰度图像

output_image = image1_gray.clone();
for (int i = 0; i < image1_gray.rows; i++)
{
for (int j = 0; j < image1_gray.cols; j++)
{
output_image.at<uchar>(i, j) =6*pow((double)image1_gray.at<uchar>(i, j),0.5); //幂律变换 s=6*r^0.5
}
}
normalize(output_image, output_image, 0, 255, NORM_MINMAX); //图像归一化,转到0~255范围内
convertScaleAbs(output_image, output_image); //数据类型转换到CV_8U
imshow(" output_image", output_image); //显示变换图像


waitKey(0); //暂停,保持图像显示,等待按键结束
return 0;
}

输出

直方图处理(直方图均衡化,直方图匹配(规定化))

直方图显示了每个像素值在整幅图像中出现的频率或数量,从而帮助我们理解图像的亮度分布情况。

图像直方图

非归一化直方图:
$h(r_k)=n_k$
归一化直方图:
$p(r_k)={n_k \over MN}$
其中MN为图像行数和列数,常说的图像直方图就是归一化直方图。

获取图像直方图示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include<iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;

int main()
{
Mat image, image_gray, hist; //定义输入图像,灰度图像, 直方图
image = imread("fire.jpg"); //读取图像;
if (image.empty())
{
cout << "读取错误" << endl;
return -1;
}

cvtColor(image, image_gray, COLOR_BGR2GRAY); //灰度化
imshow(" image_gray", image_gray); //显示灰度图像
1
2
3
4
5
//获取图像直方图
int histsize = 256;
float ranges[] = { 0,256 };
const float* histRanges = { ranges };
calcHist(&image_gray, 1, 0, Mat(), hist, 1, &histsize, &histRanges, true, false);

这段代码用于计算图像的直方图,下面是对这段代码的详细解释:

  1. histsize = 256: 这里定义了直方图的大小为256,表示直方图的横轴范围,通常对应着图像可能的像素值范围(0到255)。

  2. ranges[] = {0, 256}: 这里定义了直方图可能的像素值范围为0到256,用于指定直方图中每个像素值的范围。

  3. const float histRanges = {ranges};* 这一行用于将ranges数组转换为指向常量浮点数的指针,供calcHist函数使用。

  4. calcHist(&image_gray, 1, 0, Mat(), hist, 1, &histsize, &histRanges, true, false):

    • &image_gray: 这是输入的灰度图像,表示要计算直方图的原始图像。
    • 1: 表示要计算的图像数量。
    • 0: 表示直方图要计算的通道索引,0表示灰度图像。
    • Mat(): 这里是一个掩码图像,用于指定要计算直方图的区域;在这里没有使用,因此为空。
    • hist: 这是用于存储计算后直方图的Mat对象。
    • 1: 直方图的维数,对于灰度图像是1维。
    • &histsize: 这是包含直方图大小(256)的指针。
    • &histRanges: 指向直方图范围的指针。
    • true: 指示直方图是否均一化,即在范围0到255内对直方图进行归一化处理。
    • false: 指示直方图是否累积,即表示直方图是否累积计算。

通过这段代码,可以通过calcHist函数计算图像image_gray的直方图,并将结果存储在hist对象中。直方图的范围是0到255之间,且进行了归一化处理。这能够帮助我们了解图像的像素值分布情况,为后续的图像处理和分析提供数据支持。希望这个解释对您有帮助,如果您有任何其他问题,请随时告诉我!

1
2
3
4
5
//创建直方图显示图像
int hist_h = 300;//直方图的图像的高
int hist_w = 512; //直方图的图像的宽
int bin_w = hist_w / histsize;//直方图的等级
Mat histImage(hist_h, hist_w, CV_8UC3, Scalar(0, 0, 0));//绘制直方图显示的图像
1
2
3
4
5
6
7
8
9
10
11
12
	//绘制并显示直方图
normalize(hist, hist, 0, hist_h, NORM_MINMAX, -1, Mat());//归一化直方图
for (int i = 1; i < histsize; i++)
{
line(histImage, Point((i - 1) * bin_w, hist_h - cvRound(hist.at<float>(i - 1))),
Point((i)*bin_w, hist_h - cvRound(hist.at<float>(i))), Scalar(255, 0, 0), 2, 8, 0);
}
imshow("histImage", histImage);

waitKey(0); //暂停,保持图像显示,等待按键结束
return 0;
}

这段代码是用于将归一化后的直方图数据绘制成直方图图像的过程。下面是对代码的详细解释:

  1. for循环:

    • 这段代码使用for循环遍历直方图的每个条,从第2个条开始到最后一个条。
    • i从1开始,因为第0个条通常代表背景,可以跳过不绘制。
  2. line()函数:

    • line()函数用于在histImage上绘制直方图的条。
    • Point((i - 1) * bin_w, hist_h - cvRound(hist.at<float>(i - 1)))表示当前条的起始点位置,前一个条的结束点位置。
    • Point((i)*bin_w, hist_h - cvRound(hist.at<float>(i))) 表示当前条的终点位置。
    • Scalar(255, 0, 0)指定绘制直线的颜色为蓝色。
    • 2表示线的粗细为2个像素。
    • 8表示线的类型为8-connected。
    • 0表示线的偏移值为0。
  3. 绘制直方图条:

    • 通过计算每个直方图条的起始和终止点,使用line()函数在histImage上绘制直方图条。
    • cvRound()用于将浮点值四舍五入为最接近的整数,确保直方图能够正确映射到图像上。

直方图均衡化

(我感觉差不多了,我去做考核咯~)

Prev:
摄像头考核
Next:
图像处理笔记(一)基础操作