锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

opencv 图像去噪学习总结

时间:2022-09-20 19:00:00 ibp传感器

OpenCV图像处理部分图像平滑


图像平滑算法
程序分析及结果
图像平滑算法


平滑图像和模糊图像是同一个概念,主要用于去噪图像。为了不改变图像的相位信息,应使用滤波器进行平滑,

线性滤波器的统一形式如下:

其中h称为滤波器的核函数,说白了就是权值。不同的核函数代表不同的滤波器,有不同的用途。

在图像处理中,常见的滤波器包括:

归一化滤波器(Homogeneous blur)

也是平均过滤器,用输出像素点核窗口中的平均像素代替输出点像素值。

高斯滤波器(Guassian blur)

它是实际上最常用的滤波器。高斯滤波器是输入数组的每个像素点和 高斯内核 卷积作为输出

素值。高斯核相当于给输出像素的邻域不同的权值,输出像素点的权值最大(对应高斯函数

均值位置)。二维高斯函数为,

中值滤波器(median blur)

中值滤波用邻域(以当前像素为中心的正方形区域)像素的中值代替图像的每个像素。椒盐噪声最有效

去除跳变点的滤波器非常有效。

双边滤波器(Bilatrial blur)

在这种情况下,使用双边滤波器,以避免滤波器平滑图像去噪,模糊边缘。双边滤波器的解释参考

见http://homepages.inf.ed.ac.uk/rbf/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html

下面的程序将先给标准Lena图像添加椒盐噪声,分别使用4种不同的滤波器进行平滑操作,请注意观察不

同滤波器对椒盐噪声的去噪效果!

程序分析及结果

/*
 * FileName : image_smoothing.cpp
 * Author   : xiahouzuoxin @163.com
 * Version  : v1.0
 * Date     : Wed 17 Sep 2014 08:30:25 PM CST
 * Brief    : 
 * 
 * Copyright (C) MICL,USTB
 */
#include "cv.h"
#include "imgproc/imgproc.hpp"
#include "highgui/highgui.hpp"


using namespace std;
using namespace cv;

const int MAX_KERNEL_LENGTH = 10;

const char *wn_name = "Smoothing";


static void salt(Mat &I, int n);
static void disp_caption(const char *wn_name, Mat src, const char *caption);
static void disp_image(const char *wn_name, Mat I);

/*
 * @brief   
 * @inputs  
 * @outputs 
 * @retval  
 */
int main(int argc, char *argv[])
{
    if (argc<2) {
        cout<<"Usage: ./image_smoothing [file name]"<
        return -1;
    }

    Mat I = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
    salt(I, 6000);
    imshow(wn_name, I);
    waitKey(0);

    Mat dst;  // Result

    /* Homogeneous blur */
    disp_caption(wn_name, I, "Homogeneous blur");
    for (int i=1; i
        blur(I, dst, Size(i, i), Point(-1,-1));
        disp_image(wn_name, dst);
    }

    /* Guassian blur */
    disp_caption(wn_name, I, "Gaussian blur");
    for (int i=1; i
        GaussianBlur(I, dst, Size(i, i), 0, 0);
        disp_image(wn_name, dst);
    }

    /* Median blur */
    disp_caption(wn_name, I, "Median blur");
    for (int i=1; i
        medianBlur(I, dst, i);
        disp_image(wn_name, dst);
    }

    /* Bilatrial blur */
    disp_caption(wn_name, I, "Bilatrial blur");
    for (int i=1; i
        bilateralFilter(I, dst, i, i*2, i/2);
        disp_image(wn_name, dst);
    }
    waitKey(0);

    return 0;
}

/*
 * @brief   显示提示文字(滤波方法)
 * @inputs  
 * @outputs 
 * @retval  
 */
static void disp_caption(const char *wn_name, Mat src, const char *caption)
{
    Mat dst = Mat::zeros(src.size(), src.type());

    putText(dst, caption, Point(src.cols/4, src.rows/2), CV_FONT_HERSHEY_COMPLEX, 1, Scalar

(255,255,255));

    imshow(wn_name, dst);
    waitKey(0);
}

/*
 * @brief   显示图像
 * @inputs  
 * @outputs 
 * @retval  
 */
static void disp_image(const char *wn_name, Mat I)
{
    imshow(wn_name, I);
    waitKey(1000);
}

/*
 * @brief   添加椒盐噪声
 * @inputs  
 * @outputs 
 * @retval  
 */
static void salt(Mat &I, int n=3000)
{
    for (int k=0; k
        int i = rand() % I.cols;
        int j = rand() % I.rows;


        if (I.channels()) {
            I.at(j,i) = 255;
        } else {
            I.at(j,i)[0] = 255;
            I.at(j,i)[1] = 255;
            I.at(j,i)[2] = 255;
        }
    }
}
上面程序的逻辑非常清晰:

读入灰度图,并添加椒盐噪声(6000个噪声点):

Mat I = imread(argv[1], CV_LOAD_IMAGE_GRAYSCALE);
salt(I, 6000);
LenaNoise

disp_caption和disp_image函数分别是用于显示提示文字和平滑过程中的变化图像的,平滑过程中图像的

变化如下图:

blur

注意观察上面的图,中值滤波(Median Blur)对椒盐噪声的效果最好!

四种滤波方法分别使用到4个OpenCV函数,这些函数的声明都在imgproc.hpp中,这些函数的前2个参数都

是原图像和滤波后图像。

归一化滤波器blur的第3个参数为滤波核窗口的大小,Size(i,i)表示ixi大小的窗口。

高斯滤波器GaussianBlur第3个参数也是滤波核窗口的大小,第4、第5个参数分辨表示x方向和y方向的δ


中值滤波器medianBlur第3个参数是滤波器的长度,该滤波器的窗口为正方形。

双边滤波器的函数原型如下:

//! smooths the image using bilateral filter
CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d,
                             double sigmaColor, double sigmaSpace,
                             int borderType=BORDER_DEFAULT );
本程序使用的Makefile文件为:

 TARG=image_smoothing
 SRC=image_smoothing.cpp
 LIB=-L/usr/local/lib/
 INC=-I/usr/local/include/opencv/ -I/usr/local/include/opencv2
 CFLAGS=

 $(TARG):$(SRC)
     g++ -g -o $@ ${CFLAGS} $(LIB) $(INC) \
         -lopencv_core -lopencv_highgui -lopencv_imgproc \
         $^

 .PHONY:clean

 clean:
     -rm $(TARG) tags -f

========

图像代数运算:平均值去噪,减去背景



代数运算,就是对两幅图像的点之间进行加、减、乘、除的运算。四种运算相应的公式为:

代数运算中比较常用的是图像相加和相减。图像相加常用来求平均值去除addtive噪声或者实现二次曝光

(double-exposure)。图像相减用于减去背景或周期噪声,污染等。

图像相加

OpenCV中提供了相加的函数

void cvAcc(   
           const CvArr* image,//输入图像  
           CvArr* sum,  //累积图像   
           const CvArr* mask=NULL//可选的运算  
 );  
我们还需要用到一个线性变换转换函数来对相加的结果求平均

void cvConvertScale(   
        const CvArr* src, //输入数组  
        CvArr* dst,//输出数组  
        double scale=1,//比例  
        double shift=0 //缩放比例,可选  
);  
#define cvCvtScale cvConvertScale  
#define cvScale  cvConvertScale  
#define cvConvert( src, dst )  cvConvertScale( (src), (dst), 1, 0 )  

实践:平均值去噪
我们用NASA的一段幸运团的视频做实验,截取视频的某几个连续帧求平均值:

int main()  
{  
    CvCapture* capture=cvCaptureFromFile("media.avi");  
    IplImage* frame=  NULL;  
    IplImage * imgsum =NULL;  
      
    int start=301;  
    int end=304;  
    cvSetCaptureProperty(capture, CV_CAP_PROP_POS_FRAMES, start);  
      
    int count = start;  
    while( cvGrabFrame(capture) && count <= end )  
    {  
        frame = cvRetrieveFrame(capture);// 获取当前帧  
        if(imgsum==NULL){  
            imgsum=cvCreateImage(cvGetSize(frame),IPL_DEPTH_32F,3);  
            cvZero(imgsum);  
        }  
        cvAcc(frame,imgsum);  
  
        char testname[100];  
        sprintf(testname,"%s%d%s","image",count,".jpg");  
        cvShowImage(testname,frame);  
        cvSaveImage(testname,frame);  
          
        count++;  
    }  
    IplImage * imgavg = cvCreateImage(cvGetSize(frame),IPL_DEPTH_8U,3);  
    cvConvertScale(imgsum,imgavg,1.0/4.0);  
      
    cvShowImage("imageavg",imgavg);  
    cvSaveImage("imageavg_4.jpg",imgavg);  
      
    cvWaitKey(0);  
    cvReleaseCapture(&capture);  
    return 0;  
}  
以下从左到右分别是连续两帧、四帧、八帧、十六帧求均值的结果:
   


实践:图像二次曝光
曝光和去噪是一样的,也是对几幅图像求平均


//通过求平均二次曝光  
int main()  
{  
    IplImage* image1=  cvLoadImage("psu3.jpg");  
    IplImage* image2=  cvLoadImage("psu4.jpg");  
      
    IplImage * imgsum =cvCreateImage(cvGetSize(image1),IPL_DEPTH_32F,3);  
    cvZero(imgsum);  
    cvAcc(image1,imgsum);  
    cvAcc(image2,imgsum);  
  
    IplImage * imgavg = cvCreateImage(cvGetSize(image1),IPL_DEPTH_8U,3);  
    cvConvertScale(imgsum,imgavg,1.0/2.0);  
  
    cvShowImage("imageavg",imgavg);  
    cvSaveImage("avg.jpg",imgavg);  
  
    cvWaitKey(0);  
    cvReleaseImage(&image1);  
    cvReleaseImage(&image2);  
    cvReleaseImage(&imgsum);  
    cvReleaseImage(&imgavg);  
    return 0;  
}  
下图是对同学街舞截图的“二次曝光”效果:


图像相减

OpenCV中用cvAbsDiff函数计算两数组的差的绝对值

void cvAbsDiff(   
        const CvArr* src1,//第一个输入数组  
        const CvArr* src2,//第二个输入数组  
        CvArr* dst//输出数组  
);  

实践:减去背景
减去背景是通过两幅图像代数相减,可以判断出前景区域和运动区域,这是最简单(很多时候也是效果很
好的)运动检测方法。

//减去背景  
int main()  
{  
    IplImage* pFrame = NULL;   
    IplImage* pFrImg = NULL;  
    IplImage* pBkImg = NULL;  
  
    CvMat* pFrameMat = NULL;  
    CvMat* pFrMat = NULL;  
    CvMat* pBkMat = NULL;  
  
    CvCapture* pCapture = NULL;  
  
    int nFrmNum = 0;  
  
    //创建窗口  
    cvNamedWindow("video", 1);  
    cvNamedWindow("background",1);  
    cvNamedWindow("foreground",1);  
  
    pCapture = cvCaptureFromFile("media.avi");  
    while(pFrame = cvQueryFrame( pCapture ))  
    {  
        nFrmNum++;  
  
        //如果是第一帧,需要申请内存,并初始化  
        if(nFrmNum == 1)  
        {  
            pBkImg = cvCreateImage(cvSize(pFrame->width, pFrame->height),  IPL_DEPTH_8U,1);  
            pFrImg = cvCreateImage(cvSize(pFrame->width, pFrame->height),  IPL_DEPTH_8U,1);  
  
            pBkMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);  
            pFrMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);  
            pFrameMat = cvCreateMat(pFrame->height, pFrame->width, CV_32FC1);  
  
            //转化成单通道图像再处理  
            cvCvtColor(pFrame, pBkImg, CV_BGR2GRAY);  
            cvCvtColor(pFrame, pFrImg, CV_BGR2GRAY);  

相关文章