【数字图像处理】图像滤波C语言实现(中值,均值,高斯)
(一)均值滤波
用其像素点周围像素的平均值代替元像素值,在滤除噪声的同时也会滤掉图像的边缘信息。
通过编程实现一个3X3的均值滤波器(example/conv/conv.c),滤波效果如下:
图表 4 均值滤波1
图表 5 均值滤波2
分析:均值滤波算法简单能很快的对图像进行平滑处理, 也就是能将由于屏幕显示的分辨率不高的图像出现的锯齿边缘平滑, 均值滤波的缺点是由于对对图像锯齿边缘进行平滑处理而导致图像趋于模糊。
(二) 高斯滤波
• 像均值滤波,是简单的取平均值,模板系数都是1。而图像上的像素实际上是坐标离散但是值却连续的,因为越靠近的点关系越密切,越远离的点关系越疏远。因此,加权平均更合理,距离越近的点权重越大,距离越远的点权重越小。
• 既然是依据距离来加权平均,那么很容易想到高斯函数
如图所示:
从高斯函数来看,离原点距离越近,得到的权重越高,越远离原点,得到的权重越小。
上边是一维的高斯函数,当中心点为原地时,x的均值μ=0,此时
由于图像是二维矩阵,则采用二维高斯函数:
有了这个函数就可以计算滤波模板中每个点的权重了
我采用如下高斯卷积核实现了高斯模糊(example/conv/conv.c):
得到如下滤波结果:
图表 6 高斯滤波1
图表 7 高斯滤波2
分析:高斯滤波对于高斯噪声处理效果特别好, 由于高斯噪声是由中心点开始呈现模糊, 具有一定的模糊半径, 但一般不知道模糊半径和边缘方向。而高斯滤波很好地处理了这一问题, 因为二维高斯函数具有旋转对称性, 所以使用高斯滤波处理图像时不会出现偏向任意一方的情况, 而且邻域内的像素的权重是随离中心点的距离单调递减的, 所以使用高斯滤波处理图像时不会对边缘的影响过大, 不会像均值滤波那样因为过度的平滑处理而导致图像边缘模糊而导致图像失真。但是高斯滤波的缺点也很突出, 高斯滤波这种图像处理方式基本上只能运用于高斯噪声。总的来说, 高斯滤波能很好地滤除高斯噪声, 而且对图像的平滑更好, 不会造成图像的大幅度模糊, 对边缘的处理效果也较好, 缺点是基本只能运用于高斯噪声, 对椒盐噪声等其它噪声很难达到好的滤波效果。
(三) 中值滤波
不同于以上两种滤波方式,中值滤波用测试像素周围邻域像素集中的中值代替原像素。中值滤波去除椒盐噪声和斑块噪声时,效果非常明显。
图表 8 中值滤波1
图表 9 中值滤波2
分析:中值滤波的优点有很多, 首先中值滤波操作方式简单, 其次, 根据中值滤波的算法可知中值滤波可以很好地保护图像的边缘信息不被破坏, 再者中值滤波选择合适的点来替代图像中的污染点, 而保护图像的大部分内容, 中值滤波尤其适用于椒盐噪声的处理, 因为椒盐噪声的图像内有干净点和污染点, 通过处理污染点就被合适的点替换, 所以中值滤波可以很好地滤除椒盐噪声。但中值滤波也有缺点, 一般来说图像中比较明亮和比较暗的区域都通过中值滤波滤除, 但是较大区域的噪声一般会原封不动的保存下来。所以, 经分析中值滤波对于区域较大的噪声的滤除效果不好, 对于点状噪声的滤除效果最好尤其是椒盐噪声。
(四)选择滤波算法
综上我们发现高斯滤波对于图像边缘信息的保存会更加完整,所以我们选择了高斯滤波算法。
(五)代码实现
li_smooth.c
/*
* @Descripttion:
* @version:
* @Author: Yueyang
* @email: aaa@qq.com
* @Date: 2020-11-10 22:01:04
* @LastEditors: Yueyang
* @LastEditTime: 2020-11-10 22:44:07
*/
#ifndef LI_SMOOTH_C
#define LI_SMOOTH_C
#include "cv.h"
#include "li_image_proc.h"
#include <stdio.h>
/**
* @name: Li_Medeum
* @msg: 图像中值滤波
* @param {Li_Image* img}
* @return {Li_Image*}
*/
Li_Image* Li_Medeum(Li_Image* img)
{
if(img->imgdepth==LI_DEP_8U){
BYTE kek=0;
BYTE* ptr[9]={0};
BYTE* ptro;
Li_Image* out=Li_Copy_Image(img);
for(int i=0;i<img->height;i++)
for(int j=0;j<img->width;j++)
{
BYTE sum=0;
for(int k=0;k<9;k++)
ptr[k]=&kek;
if(j-1>=0&&i-1>=0)
ptr[0]=(BYTE*)img->at(img,j-1,i-1);
if(j>=0&&i-1>=0)
ptr[1]=(BYTE*)img->at(img,j+0,i-1);
if(j+1<=img->width&&i-1>=0)
ptr[2]=(BYTE*)img->at(img,j+1,i-1);
if(j-1>=0&&i>=0)
ptr[3]=(BYTE*)img->at(img,j-1,i+0);
if(j>=0&&i>=0)
ptr[4]=(BYTE*)img->at(img,j+0,i+0);
if(j+1<=img->width&&i>=0)
ptr[5]=(BYTE*)img->at(img,j+1,i+0);
if(j-1>=0&&i+1<=img->height)
ptr[6]=(BYTE*)img->at(img,j-1,i+1);
if(j>=0&&i+1<=img->height)
ptr[7]=(BYTE*)img->at(img,j+0,i+1);
if(j+1<=img->width&&i+1<=img->height)
ptr[8]=(BYTE*)img->at(img,j+1,i+1);
BYTE temp[9];
for(int k=0;k<9;k++)
{
if(ptr[k]!=NULL)
{
temp[k]= (BYTE)(*ptr[k]);
}
}
for(int m=0;m<9-1;m++)
for(int n=0;n<9-m-1;n++)
{
if(temp[m]>temp[m+1])
{
BYTE x=temp[m];
temp[m]=temp[n+1];
temp[n+1]=x;
}
}
ptro=(BYTE*)out->at(out,j+0,i+0);
*ptro=temp[4];
}
return out;
}else if (img->imgdepth==LI_DEP_24U||img->imgdepth==LI_DEP_32U)
{
Li_Image* imgH[img->imgdepth+1];
Li_Image* imgL[img->imgdepth+1];
Li_Split(img,imgH);
for(int i=0;i<img->imgdepth+1;i++)
imgL[i]= Li_Medeum(imgH[i]);
Li_Image* out2=Li_Combine(imgL,img->imgdepth);
return out2;
}
}
/**
* @name: Li_Smooth
* @msg: 计算图像滤波
* @param {Li_Image* img 原图像
* BYTE smoothtype( Li_GAUSS, //高斯滤波
Li_AVERAGE, //均值滤波
Li_MEDIUM, //中值滤波)}
* @return {Li_Image*}
*/
LI_API
Li_Image* Li_Smooth(Li_Image* img,BYTE smoothtype)
{
if(img==NULL)return NULL;
Li_Image* out;
Li_Kernel* kernel;
double data1[9]={1.0/9,1.0/9,1.0/9,1.0/9,1.0/9,1.0/9,1.0/9,1.0/9,1.0/9};
double data2[9]={1.0/16,2.0/16,1.0/16,2.0/16,4.0/16,2.0/16,1.0/16,2.0/16,1.0/16};
switch (smoothtype)
{
case Li_AVERAGE:
kernel=Li_GetKernel(data1,3);
out= Li_Convolute(img,kernel);
break;
case Li_GAUSS:
kernel=Li_GetKernel(data2,3);
out= Li_Convolute(img,kernel);
break;
case Li_MEDIUM:
out=Li_Medeum(img);
break;
default:
break;
}
return out;
}
/**
* @name: Li_Salt_Noise
* @msg: 图像椒盐噪声
* @param {Li_Image* img
* LONG num 噪点数量}
* @return {Li_Image*}
*/
LI_API
Li_Image* Li_Salt_Noise(Li_Image* img,LONG num)
{
Li_Image* out=Li_Copy_Image(img);
for(int i=0;i<num;i++){
LONG x=(LONG)(rand()%img->width);
LONG y=(LONG)(rand()%img->height);
BYTE * ptr= out->at(out,x,y);
int r = rand()%2;
if(r)
{
for(int j=0;j<=img->imgdepth;j++)
*ptr++=0;
}
else
{
for(int j=0;j<=img->imgdepth;j++)
*ptr++=0xFF;
}
}
return out;
}
#endif // !LI_SMOOTH_C
main.c
/*
* @Descripttion: 图像卷积常见操作
* @version:
* @Author: Yueyang
* @email: aaa@qq.com
* @Date: 2020-10-26 19:35:49
* @LastEditors: Yueyang
* @LastEditTime: 2020-11-24 21:12:04
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <malloc.h>
#include "bmp.h"
#include "cv.h"
#include "li_image.h"
#include "li_painter.h"
#include "li_image_proc.h"
int main()
{
BYTE* ptr=NULL;
Li_Image* out =Li_Load_Image("./picture/whu_rgb888.bmp",LI_BMP_888);
Li_Image* noise =Li_Salt_Noise(out,1000);//椒盐噪声
Li_Image* med =Li_Smooth(noise,Li_MEDIUM);//中值滤波
Li_Image* conv=Li_Smooth(noise,Li_GAUSS);//高斯滤波
Li_Image* ave=Li_Smooth(noise,Li_AVERAGE);//均值滤波
Li_Save_Image("conv.bmp",conv);
Li_Save_Image("med.bmp",med);
Li_Save_Image("ave.bmp",ave);
Li_Save_Image("noise.bmp",noise);
LILOG("over");
return 0;
}
(六)写在后面
因为LiteCV项目才刚刚写了一个开头,代码中有错误的地方还望指出。我已经将项目同步到了github,我会实时更新这个代码仓库。
项目github地址:
LiteCV
上一篇: 数独问题 解数独