zoukankan      html  css  js  c++  java
  • OpenCV(4)-图像掩码操作(卷积)--平滑处理

    卷积定义

    矩阵的掩码操作即对图像进行卷积。对图像卷积操作的意义为:邻近像素对(包括该像素自身)对新像素的影响;影响大小取决于卷积核对应位置值得大小。

    例如:图像增强可以使用

    [I(i,j)=5*I(i,j)-[I(i-1,j) + I(i+1,j) + I(i, j-1) + I(i, j+1)] ]

    用代码实现

     void Sharpen(const Mat& myImage, Mat& Result)
    {
    	CV_Assert(myImage.depth() == CV_8U);  // 仅接受uchar图像
    	Result.create(myImage.size(), myImage.type());
    	const int nChannels = myImage.channels();
    
    	for (int j = 1; j < myImage.rows - 1; ++j)//从第一行开始,而不是0;到rows-2行结束
    	{
    		const uchar* previous = myImage.ptr<uchar>(j - 1);//上一行
    		const uchar* current = myImage.ptr<uchar>(j);
    		const uchar* next = myImage.ptr<uchar>(j + 1);//下一行
    
    		uchar* output = Result.ptr<uchar>(j);
    
    		for (int i = nChannels; i < nChannels*(myImage.cols - 1); ++i)
    		{
    			*output++ = saturate_cast<uchar>(5 * current[i]
    				- current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
    		}
    	}
    
    	//边缘处理。边缘没有卷积,所以边缘赋值为原图像对应像素的值。
    
    	//先处理上和下
    	uchar* output_top = Result.ptr<uchar>(0);
    	const uchar* origin_top = myImage.ptr<uchar>(0);
    	uchar* output_bottom = Result.ptr<uchar>(myImage.rows - 1);
    	const uchar* origin_bottom = Result.ptr<uchar>(myImage.rows - 1);
    	for (int i = 0; i < nChannels * myImage.cols - 1; ++i){
    		*output_top++ = *origin_top++;
    		*output_bottom++ = *origin_bottom++;
    	}
    
    	//左和右 分别有nChannel
    	for (int i = 0; i < myImage.rows; ++i){
    		const uchar* origin_left = myImage.ptr<uchar>(i);
    		uchar* output_left = Result.ptr<uchar>(i);
    		const uchar* origin_right = origin_left + nChannels*(myImage.cols - 1);
    		uchar* output_right = output_left + nChannels*(myImage.cols - 1);
    		for (int j = 0; j < nChannels; ++j){
    			*output_left++ = *origin_left++;
    			*output_right++ = *origin_right++;
    		}
    	}
    }
    
    

    卷积在图像处理中应用很广泛,可以使用OpenCV自带函数

    void filter2D(InputArray src, OutputArray dst, int ddepth, InputArray kernel, Point anchor=Point(-1,-1), double delta=0, int borderType=BORDER_DEFAULT )
    

    image

    滤波器

    滤波器中,所有元素和相加后应该等于1。如果大于1,则图像会变亮,如果小于1,图像变暗。

    平滑滤波器

    平滑滤波器的作用是去除图像中一些不重要的细节,减小噪声。可以分为线性和非线性2大类:
    1、线性平滑滤波器:均值波滤器
    2、非线性平滑滤波器:最大值滤波、中值滤波、最小值滤波。

    线性平滑滤波

    线性平滑滤波可以减小“尖锐”,减小噪声;因为图像边缘是有“尖锐”变换引起的,所以也就模糊了边缘。

    [ frac{1}{9}left[ egin{matrix} 1 & 1 & 1 \ 1 & 1 & 1 \ 1 & 1 & 1 end{matrix} ight] ag{3} ]

    上面这个滤波器,各个像素的值贡献相同,是均值滤波,在OpenCV有对应函数。

    void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
    

    有时各个像素的重要性不同,可以使用:

    [ frac{1}{16}left[ egin{matrix} 1 & 2 & 1 \ 2 & 4 & 2 \ 1 & 2 & 1 end{matrix} ight] ag{3} ]

    上面的滤波器表面中间和其挨着的像素贡献大一些。

    非线性滤波

    线性滤波是对滤波器区域内各个像素点加权求和,结果和原图像滤波区域有一个线性关系。非线性滤波则不然;基于统计排序的非线性滤波分为:最大值滤波、中值滤波、最小值滤波。顾名思义,上述三种滤波器是分别取卷积核区域内最大像素点值、中值像素点值、最小像素点值。

    中值滤波:

    中值滤波是对卷积核区域内像素排序,取中位数,只有奇数才有中位数,所有卷积核只能为3、5、7……这样的奇数。中值滤波在去除噪声的同时,可以比较好的保留图像细节(尖锐等),能够有效去除脉冲噪声(椒盐噪声)。

    void medianBlur(InputArray src, OutputArray dst, int ksize)
    

    高斯滤波:卷积核是按照高斯分布的

    [G(x,y)=Ae^{frac{-(x-mu_x)^2}{2sigma_x^2}+frac{-(x-mu_y)^2}{2sigma_y^2}} ]

    和均值像素差值大的像素,对结果贡献较小;离均值“近”的像素,对结果贡献较大。
    函数为:

    void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT )
    

    双边滤波:

    前面提高的滤波都是知识考虑了图像的空间关系,即卷积核区域内的空域关系,没有考虑像素间的关系。因此在平滑时,会模糊整幅图像;双边滤波不仅考虑了图像的空域关系,还考虑了图像间像素值的差异关系。可以参考这里
    OpenCV对应函数

    void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT )
    

    实验代码

    #include <iostream>
    #include <opencv2/opencv.hpp>
    
    using namespace std;
    using namespace cv;
    
    void Sharpen(const Mat& myImage, Mat& Result)
    {
    	CV_Assert(myImage.depth() == CV_8U);  // 仅接受uchar图像
    
    	Result.create(myImage.size(), myImage.type());
    	const int nChannels = myImage.channels();
    
    	for (int j = 1; j < myImage.rows - 1; ++j)//从第一行开始,而不是0;到rows-2行结束
    	{
    		const uchar* previous = myImage.ptr<uchar>(j - 1);//上一行
    		const uchar* current = myImage.ptr<uchar>(j);
    		const uchar* next = myImage.ptr<uchar>(j + 1);//下一行
    
    		uchar* output = Result.ptr<uchar>(j);
    
    		for (int i = nChannels; i < nChannels*(myImage.cols - 1); ++i)
    		{
    			*output++ = saturate_cast<uchar>(5 * current[i]
    				- current[i - nChannels] - current[i + nChannels] - previous[i] - next[i]);
    		}
    	}
    
    	//边缘处理。边缘没有卷积,所以边缘赋值为原图像对应像素的值。
    
    	//先处理上和下
    	uchar* output_top = Result.ptr<uchar>(0);
    	const uchar* origin_top = myImage.ptr<uchar>(0);
    	uchar* output_bottom = Result.ptr<uchar>(myImage.rows - 1);
    	const uchar* origin_bottom = Result.ptr<uchar>(myImage.rows - 1);
    	for (int i = 0; i < nChannels * myImage.cols - 1; ++i){
    		*output_top++ = *origin_top++;
    		*output_bottom++ = *origin_bottom++;
    	}
    
    	//左和右 分别有nChannel
    	for (int i = 0; i < myImage.rows; ++i){
    		const uchar* origin_left = myImage.ptr<uchar>(i);
    		uchar* output_left = Result.ptr<uchar>(i);
    		const uchar* origin_right = origin_left + nChannels*(myImage.cols - 1);
    		uchar* output_right = output_left + nChannels*(myImage.cols - 1);
    		for (int j = 0; j < nChannels; ++j){
    			*output_left++ = *origin_left++;
    			*output_right++ = *origin_right++;
    		}
    	}
    }
    
    int main(int argc, char* argv[]){
    	const char* path = "";
    	Mat img = imread(path);
    	if (img.empty())
    	{
    		cout << "error";
    		return -1;
    	}
    	imshow("原图像", img);
    
    	//均值滤波
    	Mat blur_mat;
    	blur(img, blur_mat, Size(3, 3));
    	imshow("均值滤波", blur_mat);
    
    	//线性非均值滤波
    	Mat kern = (Mat_<float>(3, 3) << 1, 2, 1,
    		2, 4, 2,
    		1, 2, 1) / 16;
    	Mat blur2_mat;
    	filter2D(img, blur2_mat, img.depth(), kern);
    	imshow("线性非均值滤波", blur2_mat);
    
    	//中值滤波
    	Mat memedian_mat;
    	medianBlur(img, memedian_mat, 3);
    	imshow("中值滤波", memedian_mat);
    
    	//高斯滤波
    	Mat Gaussian_mat;
    	GaussianBlur(img, Gaussian_mat, Size(3, 3), 0, 0);
    	imshow("高斯滤波", Gaussian_mat);
    
    	//双边滤波
    
    	Mat bilateral_mat;
    	bilateralFilter(img, bilateral_mat, 25, 25 * 2, 25 / 2);
    	imshow("双边滤波", bilateral_mat);
    	waitKey();
    	return 0;
    
    }
    
    
  • 相关阅读:
    IE下全局对象报 脚本错误提示“对象不支持此属性或方法”解决方案
    IE6、7下inline-block不起作用
    IE下图片切换的时候,图片总是切换不成功---根本问题是IE缓存图片
    Fiddler 跟踪 手机页面数据包
    Regular Expression Matching——没理解的动态规划
    常见排序算法分析
    Flyweight模式(亨元模式)
    组合模式(Composite Pattern)
    装饰者模式(不太理解的设计模式)
    适配器模式
  • 原文地址:https://www.cnblogs.com/korbin/p/5612438.html
Copyright © 2011-2022 走看看