1.首先将彩色图像转化为灰度图像:
(cv库中有直接读灰度图的操作,下面是算法思想,毕竟搞懂原理还是好一点)
灰度图像是指在RGB模型中,当R=G=B时,彩色表示一种灰度颜色,其中R(或G、B)的值叫做灰度值[1],灰度值的取值范围为0~255,其中灰度值为0时表示黑色,为255时表示白色,中间的值代表不同程度的灰色。将彩色图像灰度化的方法有以下四种:
1.分量法
将彩色图像中的三分量的亮度作为三个灰度图像的灰度值,可根据应用需要选取一种灰度图像。
f1(i,j) =R(i,j)/f2(i,j)=G(i,j)/f3(i,j)=B(i,j)
其中fk(i,j) (k=1,2,3)为转换后的灰度图像在(i,j)处的灰度值。
2.最大值法
将彩色图像中的三分量亮度的最大值作为灰度图的灰度值。
f(i,j) =max(R(i,j)+G(i,j)+B(i,j))
3.平均值法
将彩色图像中的三分量亮度求平均得到一个灰度图。
f(i,j) =(R(i,j)+G(i,j)+B(i,j))/3
4.加权平均值法
根据重要性及其他指标,将三个分量以不同的权值进行加权平均,由于人眼对绿色的敏感度最高,对蓝色的敏感度最低,因此,按下式对RGB三分量进行加权平均能得到较合理的灰度图像。
f(i,j) =0.30R(i,j)+0.59G(i,j)+0.11B(i,j)
根据四种方法的作用差别和实现简易程度,我选择了第三种方法平均值法,下面是实现的简要步骤:
首先使用opencv[2]库读出图片的R、G、B三通道的值,然后对三通道的值求平均值,最后把得到的值赋给该元素的三个通道。
主要代码:
//彩色图像转化为灰度图像
/*
* img.at(i, j)[0]的作用是读出图片(i,j)点的第0个通道的值
*/
void colorToGray(Mat &img){
for(int i = 0;i < img.rows;i++){
for(int j = 0;j < img.cols;j++){
int sum = img.at(i, j)[0] + img.at(i, j)[1] + img.at(i, j)[2];//求出三个通道的R、G、B值的和
sum/=3;//求平均
for(int k = 0;k < 3;k++){
img.at(i,j)[k] = sum;//把平均值赋给当前像素点的三个通道值
}
}
}
}
原图:
转化成灰度图后:
2.拉普拉斯算子锐化
1.锐化及laplace锐化的概念
图像锐化是补偿图像的轮廓,增强图像的边缘及灰度跳变的部分,使图像变得清晰,分为空域处理和频域处理两类。图像锐化是为了突出图像上地物的边缘、轮廓,或某些线性目标要素的特征。这种滤波方法提高了地物边缘与周围像元之间的反差,因此也被称为边缘增强。
图像的模糊实质就是图像受到平均或积分运算造成的,因此可以对图像进行逆运算如微分运算来使图像清晰化。从频谱角度来分析,图像模糊的实质是其高频分量被衰减,因而可以通过高通滤波操作来清晰图像。但要注意.能够进行锐化处理的图像必须有较高的性噪比,否则锐化后图像性噪比反而更低,从而使得噪声增加的比信号还要多,因此一般是先去除或减轻噪声后再进行锐化处理。 图像的锐化一般有两种方法:一种是微分法,另外一种是高通滤波法。拉普拉斯锐化法是属于常用的微分锐化法。
2.锐化算法流程图
程序开始 |
打开图像 |
调用opencv库将读入图片 |
调用colorToGray(Mat &img)函数将图片转化为灰度图 |
选择锐化算子进行运算 |
显示处理后的图片 |
程序结束 |
3.图像内部像素的处理
先将自身与周围的8个像素相减,表示自身与周围像素的差别,再将这个差别加上自身作为新像素的灰度。在图像边缘过渡处,锐化处理的结果使边缘更加突出,但是如果一片暗区出现了一个亮点,那么锐化处理的结果是这个亮点变得更亮,反而增加了图象的噪声。
模板卷积:
模板运算的基本思路是将赋予某个像素的值作为它本身灰度值和其相邻像素灰度值的函数。模板可以看作一幅尺寸为一般为奇数,远小于常见图像尺寸)的小图像。当N为奇数时,可以定义模板的半径r为(N-1)/2。模板卷积在空域实现的主要步骤为:
a.将模板在图中漫游,并将模板中心与图中某个像素位置重合;
b.将模板上的各个系数与模板下各对应像素的灰度值相乘;
c.将所有乘积相加(为保持灰度范围,常对结果再除以模板的系数个数);
d.将上述运算结果(模板的输出响应)赋予图中对应模板中心位置的像素。
4.图像边缘像素的处理
图1
如图1所示,当模板算子位于边缘时,由于红色部分的图片像素不存在,所以进行卷积运算要把红色部分排除掉,只将绿色和蓝色部分的模板矩阵的值和图片像素矩阵的值对应相乘,将结果赋给图片像素的中心值,由于图片是三通道的,要将每一个值都赋成计算的结果。
5.根据上述算法写出程序实现
//拉普拉斯算子锐化
void laplaceSharpen(Mat &img){
for(int i = 0;i < img.rows;i++){
for(int j = 0;j < img.cols;j++){
int temp = 0;//用来计算新的中心像素点值
int oper[3][3] = {{-1,-1,-1},{-1,10,-1},{-1,-1,-1}};//拉普拉斯算子
for(int k = 0;k < 3;k++){
for(int l = 0;l < 3;l++){
//当算子位于边缘区域时,排除图像中不存在的点
if((i-1+k < 0) || (j-1+l < 0) || (i-1+k >= img.rows) || (j-1+l >= img.cols))
continue;
temp += oper[k][l]*img.at(i-1+k,j-1+l)[0];
}
}
//当计算结果超出255,就把它置为255
if(temp > 255){
temp = 255;
}
//当计算结果小于0,就把它置为0
if(temp < 0){
temp = 0;
}
//将计算结果赋给中心像素点的三通道值
for(int m = 0;m < 3;m++){
img.at(i,j)[m] = temp;
}
}
}
}
注:该函数的参数为读入的Mat型待锐化图片。
6.锐化结果
3.平滑算法的实现
1.平滑滤波的概念
平滑滤波是低频增强的空间域滤波技术。它的目的有两类:一类是模糊;另一类是消除噪音。空间域的平滑滤波一般采用简单平均法进行,就是求邻近像元点的平均亮度值。邻域的大小与平滑的效果直接相关,邻域越大平滑的效果越好,但邻域过大,平滑会使边缘信息损失的越大,从而使输出的图像变得模糊,因此需合理选择邻域的大小。
2.平滑滤波的方法
经过查找平滑滤波的论文和博客,我发现平滑滤波算法有很多种,大致分为两类,一类是线性滤波,常见的如邻域平滑滤波;另一类时非线性滤波,常见的如中值滤波,下面介绍两种常见的滤波方法:
(1)邻域平均法
邻域平均法[6]是一种利用Box模版对图像进行模版操作(卷积运算)的图像平滑方法,所谓Box模版是指模版中所有系数都取相同值的模版,常用的3×3和5×5模版如下:
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
表1:3x3
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
1 |
表2:5x5
邻域平均法的思想是通过一点和邻域内像素点求平均来去除突变的像素点,从而滤掉一定噪声,其优点是算法简单,计算速度快,其代价会造成图像在一定程度上的模糊。例如3 x 3的模板中,在图片像素矩阵中的每一个元素和该模板求卷积和,然后再用结果除以模板的元素个数,即sum/9,然后将该结果赋给模板中心对应的图片像素点的三个通道。
(2)中值滤波法
中值滤波是将像素(中值计算中包括的原像素值)邻域内灰度的中值代替该像素的值[7]。中值滤波器的使用非常普遍,这是因为对于一定类型的随机噪声,它提供了优秀的去噪能力,比小尺寸的线性平滑滤波器的模糊程度明显要低。虽然说中值滤波器对脉冲信号很有效,但是仅采用简单的中值滤波将不可避免的丢失图像的细节,造成视觉效果的模糊。
中值滤波就是用一个奇数点的移动窗口,将窗口的中心点的值用窗口内的各点中值代替。假设窗口内有五点,其值为80、90、200、110和120,那么此窗口内各点的中值及为110。
3.根据上述算法写出程序实现
(1)邻域平均法
void avrSmooth(Mat &img,int n){//n越大表示平滑程度越高
if(n % 2 == 0){
n += 1; //算子模板的边长应为奇数
}
int x = (n-1)/2;
int oper[n][n];//平滑算子
memset(oper,0,sizeof(oper));//全部初始化为0
for(int i = 0;i < img.rows;i++){
for(int j = 0;j < img.cols;j++){
int temp = 0;//用来计算新的中心像素点值
for(int k = 0;k < n;k++){
for(int l = 0;l < n;l++){
//当算子位于边缘区域时,排除图像中不存在的点
if((i-x+k < 0) || (j-x+l < 0) || (i-x+k >= img.rows) || (j-x+l >= img.cols))
continue;
temp += (oper[k][l]+1)*img.at(i-x+k,j-x+l)[0];
}
}
temp/=(n*n);
//将计算结果赋给中心像素点的三通道值
for(int m = 0;m < 3;m++){
img.at(i,j)[m] = temp;
}
}
}
}
(2)中值滤波法
void midSmooth(Mat &img,int n){
if(n % 2 == 0){
n += 1; //算子模板的边长应为奇数
}
int temp[n*n];//用来存放模板对应的9个像素点的值
memset(temp,0,sizeof(temp));//初始化为0
int x = (n-1)/2;
for(int i = 0;i < img.rows;i++){
for(int j = 0;j < img.cols;j++){
int count = 0;//标记共有多少个合法点
for(int k = 0;k < n;k++){
for(int l = 0;l < n;l++){
//当算子位于边缘区域时,排除图像中不存在的点
if((i-x+k < 0) || (j-x+l < 0) || (i-x+k >= img.rows) || (j-x+l >= img.cols))
continue;
temp[count] = img.at(i-x+k,j-x+l)[0];
count++;
}
}
//将temp数组中的像素值从小到大排序
for(int p = 0;p < count;p++){
for(int q = count-1;q > p;q--){
if(temp[q] < temp[q-1]){
swap(temp[q],temp[q-1]);
}
}
}
//将temp数组的中值赋给中心像素点的三通道
for(int m = 0;m < 3;m++){
img.at(i,j)[m] = temp[count/2+1];
}
}
}
}
注:这两个函数的参数为读入的Mat型待锐化图片,n为平滑模板的边长,n的值越大,平滑程度越高。
4.平滑结果
(1)邻域平均法5 x 5
(2)邻域平均法9 x 9
(3)中值滤波法3 x 3
(4)中值滤波法7 x 7
如果你还遇到了其他各种各样奇奇怪怪的问题,欢迎留言讨论!