OpenCV成长之路(7):图像滤波

转自:http://blog.csdn.net/cv_ronny/article/details/17756937

图像滤波

滤波实际上是信号处理里的一个概念,而图像本身也可以看成是一个二维的信号。其中像素点灰度值的高低代表信号的强弱。

高频:图像中灰度变化剧烈的点。

低频:图像中平坦的,灰度变化不大的点。

根据图像的高频与低频的特征,我们可以设计相应的高通与低通滤波器,高通滤波可以检测图像中尖锐、变化明显的地方;低通滤波可以让图像变得光滑,滤除图像中的噪声。

下面我们来看一下OpenCV中的一些滤波函数:

一、低通滤波

1,blur函数

这个函数是一个平滑图像的函数,它用一个点邻域内像素的平均灰度值来代替该点的灰度。

cv::blur(image,result,cv::Size(5,5));

2,高斯模糊

上面的blur的平滑原理是用邻域内的平均值来代替当前的灰度值,但是我们往往希望越靠近该像素的点提供越高的权重,这样就产生了高斯模糊滤波。它的滤波器或者叫遮罩是一个高斯分布的二维矩阵。

cv::GaussianBlur(image,result,cv::Size(5,5),1.5);

参数image为输入图像,result为输出图像,Size(5,5)定义了核的大小,最后一个参数说明了高斯核的方差。

3,中值滤波

上面讲到的2个滤波器,都是邻域内的像素按照一个权重相加最后设置为当前点的灰度值,这种操作又称为卷积,这样的滤波器叫线性滤波器,另外还有一种非线性的滤波器,比如中值滤波器,它是取邻域内所有像素的中值作为当前点的灰度值。

中值即排序后中间的那个值:median({1,2,3,3,7,5,1,8})=3。

cv::medianBlur(image,result,5);

其中最后一个参数指定了邻域的大小为5*5。中值滤波也是在实际中应用最多的平滑滤波,它可以有效的去除比如椒盐噪声一类的干扰。

下面我们对比一下上面三种滤波器的效果:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>

int main()
{
    using namespace cv;

    Mat image=imread("../cat.png");
    cvtColor(image,image,CV_BGR2GRAY);

    Mat blurResult;
    Mat gaussianResult;
    Mat medianResult;

    blur(image,blurResult,Size(5,5));
    GaussianBlur(image,gaussianResult,Size(5,5),1.5);
    medianBlur(image,medianResult,5);

    namedWindow("blur");imshow("blur",blurResult);
    namedWindow("Gaussianblur");imshow("Gaussianblur",gaussianResult);
    namedWindow("medianBlur");imshow("medianBlur",medianResult);

    waitKey();
    return 0;
}

image

二、高通滤波:边缘检测

高通滤波器最好的一个应用就是边缘检测,由文章开头分析可知高频是图像中变化剧烈的地方,所以图像的边缘区域恰好符合这一特性,我们可以利用高通滤波让图像的边缘显露出来,进一步计算图像的一些特征。

边缘检测本来打算作为一个单独的主题来写一篇文章,但是由于Canny边缘检测算法比较复杂,篇幅也较大,所以先把Sobel边缘检测在高通滤波这里作为一个实例,以后Canny边缘检测作为单独的一篇文章来写。

实际上OpenCV有提供了Sobel边缘检测的函数,但是一方面阈值好像取的不太好,另一方面没有对最后边缘作细化处理,所以效果并不太让人满意,本文是模仿Matlab中算法来写的,相关的理论可以参考我原来写过的一篇文章《视觉算法:Sobel边缘检测》。

下面是Soble实现的C++代码:

bool Sobel(const Mat& image,Mat& result,int TYPE)
{
    if(image.channels()!=1)
        return false;
    // 系数设置
    int kx(0);
    int ky(0);
    if( TYPE==SOBEL_HORZ ){
        kx=0;ky=1;
    }
    else if( TYPE==SOBEL_VERT ){
        kx=1;ky=0;
    }
    else if( TYPE==SOBEL_BOTH ){
        kx=1;ky=1;
    }
    else
        return false;

    // 设置mask
    float mask[3][3]={{1,2,1},{0,0,0},{-1,-2,-1}};
    Mat y_mask=Mat(3,3,CV_32F,mask)/8;
    Mat x_mask=y_mask.t(); // 转置

    // 计算x方向和y方向上的滤波
    Mat sobelX,sobelY;
    filter2D(image,sobelX,CV_32F,x_mask);
    filter2D(image,sobelY,CV_32F,y_mask);
    sobelX=abs(sobelX);
    sobelY=abs(sobelY);
    // 梯度图
    Mat gradient=kx*sobelX.mul(sobelX)+ky*sobelY.mul(sobelY);

    // 计算阈值
    int scale=4;
    double cutoff=scale*mean(gradient)[0];

    result.create(image.size(),image.type());
    result.setTo(0);
    for(int i=1;i<image.rows-1;i++)
    {
        float* sbxPtr=sobelX.ptr<float>(i);
        float* sbyPtr=sobelY.ptr<float>(i);
        float* prePtr=gradient.ptr<float>(i-1);
        float* curPtr=gradient.ptr<float>(i);
        float* lstPtr=gradient.ptr<float>(i+1);
        uchar* rstPtr=result.ptr<uchar>(i);
        // 阈值化和极大值抑制
        for(int j=1;j<image.cols-1;j++)
        {
            if( curPtr[j]>cutoff && (
                (sbxPtr[j]>kx*sbyPtr[j] && curPtr[j]>curPtr[j-1] && curPtr[j]>curPtr[j+1]) ||
                (sbyPtr[j]>ky*sbxPtr[j] && curPtr[j]>prePtr[j] && curPtr[j]>lstPtr[j]) ))
                rstPtr[j]=255;
        }
    }

    return true;
}

 

image

自己写的程序如下:

// learn_opencv7.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
using namespace std;

bool Sobel(const Mat& image, Mat& result, int TYPE)
{
	if (image.channels() != 1)
		return false;
	// 系数设置
	int kx(0);
	int ky(0);
	if (TYPE == 0){
		kx = 0; ky = 1;
	}
	else if (TYPE == 1){
		kx = 1; ky = 0;
	}
	else if (TYPE == 2){
		kx = 1; ky = 1;
	}
	else
		return false;

	// 设置mask
	float mask[3][3] = { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };
	Mat y_mask = Mat(3, 3, CV_32F, mask) / 8;
	Mat x_mask = y_mask.t(); // 转置

	// 计算x方向和y方向上的滤波
	Mat sobelX, sobelY;
	filter2D(image, sobelX, CV_32F, x_mask);
	filter2D(image, sobelY, CV_32F, y_mask);
	sobelX = abs(sobelX);
	sobelY = abs(sobelY);
	// 梯度图
	Mat gradient = kx*sobelX.mul(sobelX) + ky*sobelY.mul(sobelY);

	// 计算阈值
	int scale = 4;
	double cutoff = scale*mean(gradient)[0];

	result.create(image.size(), image.type());
	result.setTo(0);
	for (int i = 1; i<image.rows - 1; i++)
	{
		float* sbxPtr = sobelX.ptr<float>(i);
		float* sbyPtr = sobelY.ptr<float>(i);
		float* prePtr = gradient.ptr<float>(i - 1);
		float* curPtr = gradient.ptr<float>(i);
		float* lstPtr = gradient.ptr<float>(i + 1);
		uchar* rstPtr = result.ptr<uchar>(i);
		// 阈值化和极大值抑制
		for (int j = 1; j<image.cols - 1; j++)
		{
			if (curPtr[j]>cutoff && (
				(sbxPtr[j]>kx*sbyPtr[j] && curPtr[j]>curPtr[j - 1] && curPtr[j]>curPtr[j + 1]) ||
				(sbyPtr[j]>ky*sbxPtr[j] && curPtr[j]>prePtr[j] && curPtr[j]>lstPtr[j])))
				rstPtr[j] = 255;
		}
	}

	return true;
}

int _tmain(int argc, _TCHAR* argv[])
{
	Mat image(480, 320, CV_8U), result_blur, result_gauss_blur, result_medi_blur,mei;
	image = imread("cat.png");

	blur(image,result_blur,Size(5,5));
	GaussianBlur(image,result_gauss_blur,Size(5,5),1.5);
	medianBlur(image,result_medi_blur,5);
	medianBlur(result_gauss_blur, mei,5);

	Mat result_sobel;
	cvtColor(image, image, CV_BGR2GRAY);
	//Sobel(image,result_sobel,image.depth(),1,1);
	Sobel(image, result_sobel, 2);
	imshow("sobel",result_sobel);

	imshow("ori",image);
	imshow("blur",result_blur);
	imshow("blur_gauss", result_gauss_blur);
	imshow("result_medi_blur", result_medi_blur);
	imshow("mei", mei);

	waitKey();
	return 0;
}


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页