如何对图像进行卷积操作[通俗易懂]

如何对图像进行卷积操作[通俗易懂]1、首先先了解下什么是卷积呢?2、卷积操作:卷积核与原图对应位置相乘再求和;然后将所求和放在被卷积操作的图中心位置。上图表示一个8×8的原图,每个方格代表一个像素点;其中一个包含X的方格是一个5×5的卷积核,核半径等于5/2=2;进行卷积操作后,生成图像为上图中包含Y的方格,可以看出是一个4×4的生成图;通过比较观察可以发现,生成图比原图尺寸要小,为了保证生成…

大家好,又见面了,我是你们的朋友全栈君。

1、首先先了解下什么是卷积呢?
在这里插入图片描述
2、卷积操作:卷积核与原图对应位置相乘再求和;然后将所求和放在被卷积操作的图中心位置。
在这里插入图片描述
上图表示一个 8×8 的原图,每个方格代表一个像素点;其中一个包含 X 的方格是一个 5×5 的卷积核,核半径等于 5/2 = 2;

进行卷积操作后,生成图像为上图中包含 Y 的方格,可以看出是一个 4×4 的生成图;

通过比较观察可以发现,生成图比原图尺寸要小,为了保证生成图与原图保持尺寸大小一样,需要对原图进行边界补充,方法有如下四种:
(1)补零填充;
(2)镜像填充;
(3)块填充;
(4)边界复制填充。

由上图可知,生成图边界与原图边界差2个像素点,这是因为,卷积核半径为2,所以,为了保证图像处理前后尺寸一致,可将原图填充为 12×12 大小。

3、可参考下面程序了解卷积操作:

  • 方法一
//边缘锐化
Mat Kernel_test_3_3 = (Mat_<double>(3,3) << 
    0,-1,0,
    -1,5,-1,
    0,-1,0);
//均值平滑模板(平滑)
Mat Kernel_junzhi=(Mat_<double>(3,3) <<
    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);
Mat Kernel_junzhi_new=(Mat_<double>(5,5))<<
    0.04,0.04,0.04,0.04,0.04,
    0.04,0.04,0.04,0.04,0.04,
    0.04,0.04,0.04,0.04,0.04,
    0.04,0.04,0.04,0.04,0.04,
    0.04,0.04,0.04,0.04,0.04);

//拉普拉斯4邻域模板(锐化)
Mat Kernel_laPuLaSi=(Mat_<double>(3,3)<<
    0,1,0,
    1,-4,1,
    0,1,0);
//sobel算子模板(边缘检测)
Mat Kernel_sobel=(Mat_<double>(3,3)<<
    -1,0,1,
    -2,0,2,
    -1,0,1);


void Convlution(Mat  InputImage,Mat  OutputImage,Mat kernel)
{
    //计算卷积核的半径
    int sub_x = kernel.cols/2;
    int sub_y = kernel.rows/2;
    //遍历图片,除边界以外每个像素
    for (int image_y=0;image_y<InputImage.rows-2*sub_y;image_y++)
    {
        for(int image_x=0;image_x<InputImage.cols-2*sub_x;image_x++)
        {
            int pix_value = 0;//用来累加每个位置的乘积
            for (int kernel_y = 0;kernel_y<kernel.rows;kernel_y++)//对每一个点根据卷积模板进行卷积
            {
                for(int kernel_x = 0;kernel_x<kernel.cols;kernel_x++)
                {
                    double  weihgt = kernel.at<double>(kernel_y,kernel_x);
                    int value =  (int)InputImage.at<uchar>(image_y+kernel_y,image_x+kernel_x); 
                    pix_value +=weihgt*value;
                }
            }
            OutputImage.at<uchar>(image_y+sub_y,image_x+sub_x) = (uchar)pix_value;
            //OutputImage.at<uchar>(image_y+sub_y,image_x+sub_x) = saturate_cast<uchar>((int)pix_value);
            if ((int)pix_value!=(int)saturate_cast<uchar>((int)pix_value))
            {
                //cout<<"没有防溢出"<<(int)pix_value<<endl;
                //cout<<"防溢出"<<(int)saturate_cast<uchar>((int)pix_value)<<endl;
                //cout<<"没有防溢出写入了什么?"<<(int)OutputImage.at<uchar>(image_y+sub_y,image_x+sub_x)<<endl;
                //cout<<endl;
            }
        }
    }
}


int main()
{
    Mat srcImage = imread("1.jpg",0);//创建一个图形矩阵
    namedWindow("srcImage", WINDOW_AUTOSIZE);
    imshow("原图", srcImage);

    //filter2D卷积
    Mat dstImage_oprncv(srcImage.rows,srcImage.cols,CV_8UC1,Scalar(0));
    filter2D(srcImage,dstImage_oprncv,srcImage.depth(),Kernel_test_3_3);
    imshow("filter2D卷积图",dstImage_oprncv);
    imwrite("0.jpg",dstImage_oprncv);

    //自定义卷积1
    Mat dstImage_mycov(srcImage.rows,srcImage.cols,CV_8UC1,Scalar(0));
    Convlution(srcImage,dstImage_mycov,Kernel_test_3_3);
    imshow("卷积图1",dstImage_mycov);
    imwrite("1.jpg",dstImage_mycov);

    //均值平滑
    Mat dstImage_junzhi(srcImage.rows,srcImage.cols,CV_8UC1,Scalar(0));
    Convlution(srcImage,dstImage_junzhi,Kernel_junzhi);
    imshow("平滑图",dstImage_junzhi);
    imwrite("2.jpg",dstImage_junzhi);

    //拉普拉斯锐化
    Mat dstImage_lapulasi(srcImage.rows,srcImage.cols,CV_8UC1,Scalar(0));
    Convlution(srcImage,dstImage_lapulasi,Kernel_laPuLaSi);
    imshow("锐化图",dstImage_lapulasi);
    imwrite("3.jpg",dstImage_lapulasi);

    //sobel边缘检测
    Mat dstImage_sobel(srcImage.rows,srcImage.cols,CV_8UC1,Scalar(0));
    Convlution(srcImage,dstImage_sobel,Kernel_sobel);
    imshow("边缘检测图",dstImage_sobel);
    imwrite("4.jpg",dstImage_sobel);

    waitKey(0);
    return 0;

}
  • 方法二
/convolution卷积
//inputeImage图片矩阵 mytemplate模板矩阵
Mat convolution(Mat inputImage, Mat myTemplate) 
{
	int inputImageWidth = inputImage.size().width;//图片矩阵的宽度
	int inputImageHeigh = inputImage.size().height;//图片矩阵的高度
	int myTemplateWidth = myTemplate.size().width;//模板矩阵的宽度
	int myTemplateHeigh = myTemplate.size().height;//模板矩阵的高度
	Mat result(inputImageHeigh, inputImageWidth,CV_8UC1);//返回结果
	float temp;//卷积所用临时变量

	//两层for循环遍历除边界以外每一个像素
	for (int i = 1; i<inputImageHeigh - 1; i++)
	{
		for (int j = 1; j<inputImageWidth - 1; j++)
		{
			//对每一个点进行卷积
			temp = 0;//累加每一个位置的乘积
			for (int m = -1; m<myTemplateHeigh - 1; m++)
				for (int n = -1; n<myTemplateWidth - 1; n++)
					temp += myTemplate.at<float>((m + 1), (n + 1)) * inputImage.at<uchar>((i + m), (j + n));
			temp = (temp >= 0) ? temp : 0;//如果结果小于0置0
			temp = (temp <= 255) ? temp : 255;//如果结果大于255置255
			result.at<uchar>(i, j) = temp;//为结果矩阵对应位置赋值
		}
	}
	//边界不进行修改
	for (int i = 0; i < inputImageWidth; i++)//复制第一行和最后一行
	{
		result.at<uchar>(0, i) = inputImage.at<uchar>(0, i);
		result.at<uchar>((inputImageHeigh - 1), i) = inputImage.at<uchar>((inputImageHeigh - 1), i);
	}
	for (int i = 0; i < inputImageHeigh; i++)//复制第一列和最后一列
	{
		result.at<uchar>(i, 0) = inputImage.at<uchar>(i, 0);
		result.at<uchar>(i, (inputImageWidth - 1)) = inputImage.at<uchar>(i, (inputImageWidth - 1));
	}
	return result;
}

Mat matAddAndSqrt(Mat mat1,Mat mat2) //两个矩阵平方求和再开方
{
	int temp1 = 0;
	int temp2 = 0;
	for (int i = 0; i < mat1.size().height; i++)
		for (int j = 0; j < mat1.size().width; j++)
		{
			temp1 = mat1.at<uchar>(i, j);
			temp2 = mat2.at<uchar>(i, j);
			temp1 = sqrt(temp1*temp1 + temp2*temp2);
			temp1 = (temp1 <= 255) ? temp1 : 255;
			mat1.at<uchar>(i, j) = temp1;
		}
	return mat1;
}


int main()
{
	Mat image = imread("D:\\7.jpg", 0);
	Mat newImage1(image.size().height, image.size().width, CV_8UC1, Scalar(0));
	Mat newImage2(image.size().height, image.size().width, CV_8UC1, Scalar(0));
	Mat newImage3(image.size().height, image.size().width, CV_8UC1, Scalar(0));
	Mat newImage4(image.size().height, image.size().width, CV_8UC1, Scalar(0));

	//均值平滑模板
	Mat mat1 = (Mat_<float>(3, 3) << 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);
	newImage1 = convolution(image, mat1);
	//拉普拉斯4邻域锐化模板
	Mat mat2 = (Mat_<float>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
	newImage2 = convolution(image, mat2);
	newImage2 = image + newImage2;//锐化图像=原图像+加重的边缘
	//sobel算子边缘检测模板
	Mat mat3 = (Mat_<float>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);//横向边缘检测
	newImage3 = convolution(image, mat3);
	Mat mat4 = (Mat_<float>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);//纵向边缘检测
	newImage4 = convolution(image, mat4);
	//newImage3 = abs(newImage3) + abs(newImage4);//为了提高效率,使用绝对值相加为近似值
	newImage3 = matAddAndSqrt(newImage3, newImage4);//综合横向与纵向

	namedWindow("原图");//添加" , CV_WINDOW_NORMAL "可自由拖动鼠标调整窗口大小
	imshow("原图", image);
	namedWindow("均值平滑");
	imshow("均值平滑", newImage1);
	namedWindow("拉普拉斯4邻域锐化");
	imshow("拉普拉斯4邻域锐化", newImage2);
	namedWindow("sobel算子边缘检测");
	imshow("sobel算子边缘检测", newImage3);

	waitKey(0);
    return 0;
}

附:上述为个人理解,可能存在考虑不全面的情况,如果哪里有问题,还望大家留言指出~

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/144019.html原文链接:https://javaforall.net

(0)
上一篇 2022年5月11日 下午8:40
下一篇 2022年5月11日 下午9:00


相关推荐

  • 拓端tecdat|python卷积神经网络人体图像识别[通俗易懂]

    拓端tecdat|python卷积神经网络人体图像识别[通俗易懂]原文链接:http://tecdat.cn/?p=7563二十多年来,自动发现裸体图片一直是计算机视觉中的中心问题,并且由于其悠久的历史和直接的目标,它成为该领域如何发展的一个很好的例子。在这篇博文中,我将使用裸露检测问题来说明现代卷积神经网络(convnets)的训练与过去的研究有何不同。(警告和免责声明:这篇文章包含了裸露的可视化效果,以用于科学目的。如果您未满18岁…

    2022年5月8日
    46
  • 运行出现Server Tomcat v8.5 Server at localhost failed to start.和A child container failed during start…[通俗易懂]

    出现问题:解决方法:1、看servlet文件中的@WebServlet“()”,里面是否少了/字符,如图:加上即可,有问题随时留言,欢迎您的咨询!转载于:https://www.cnblogs.com/weibanggang/p/9303015.html…

    2022年4月8日
    50
  • pycharm 配置jupyter

    pycharm 配置jupyterPycharm 安装 jupyter 教程 是筱倩阿的博客 CSDN 博客 pycharm 安装 jupyter

    2026年3月17日
    2
  • 理解mipi协议

    理解mipi协议完成 mipi 信号通道分配后 需要生成与物理层对接的时序 同步信号 MIPI 规定 传输过程中 包内是 200mV 包间以及包启动和包结束时是 1 2V 两种不同的电压摆幅 需要两组不同的 LVDS 驱动电路在轮流切换工作 为了传输过程中各数据包之间的安全可靠过渡 从启动到数据开始传输 MIPI 定义了比较长的可靠过渡时间 加起来最少也有 600 多 ns 而且规定各个时间参数是可调的 所以需要一定等待时间 需要缓

    2026年3月16日
    2
  • 海明校验码详解

    海明校验码详解什么是海明码 提出者 RichardHammi 海明码是一种多重 复式 奇偶检错系统 用于检错和纠错 如何确定校验码其实计算海明校验码的值只需三步 对于原始数据信息位 m 计算得出需要的 k 位校验码如何确定最小的校验位数 kk 假设原有信息为 mm 位 则和校验位加起来长度有 m km k 校验位数 k 可以确定 2k2 k 个状态 这些状态中必有一个其所有奇偶测试都是真的 于是

    2026年3月26日
    2
  • Python调用CANoe(1)(启动与停止,变量和信号读写)

    Python调用CANoe(1)(启动与停止,变量和信号读写)CANoe 因为其封闭性 Vector 独有 开发的时候 有很多局限性 Python 因为其开源 容易上手 功能强大 那么 Python 和 CANoe 结合会产生怎样的火花呢

    2026年3月16日
    1

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号