OpenCV学习笔记——卷积运算

卷积运算

卷积算子介绍

1、卷积核的大小一般是奇数,这样子它才是和图像中心对称的。
2、卷积核所有元素之和一般应该等于一。此处是为了维护图像的能量守恒(亮度)
3、有时候我们的卷积核也可以不为一,如果大于一的话,那么图像会比原来更亮,如果小于一的话会比原来更暗。
4、卷积后的图像结果可能会出现负数或者超出255的情况,这种时候我们直接截断就可以了,对于负数可以直接取绝对值。
平滑,模糊,去噪,锐化,边缘提取其实都可以用卷积来实现。

代码实现1(for循环卷积遍历,我也称其为手搓法)


#include<iostream>
#include<opencv2\opencv.hpp>
using namespace cv;
using namespace std;
int main(void)
{
	Mat src, dst;
	src = imread("D:/360/show.jpg");
	if (!src.data) {
		cout << "open picture error!!" << endl;
	}
	CV_Assert(src.depth() == CV_8U);
	imshow("src", src);//显示原图像
	int cols = (src.cols - 1) * src.channels();//由于最外围的一圈像素点没办法进行图像掩模,所以减1
	int rows = src.rows;//实际行数
	int offsets = src.channels();//即通道数
	dst = Mat(src.size(), src.type());//与原图像等同
	for (int row = 1; row < (rows - 1); row++) {
		const uchar* pre = src.ptr<uchar>(row - 1);//上一行,指向一个数组
		const uchar* cur = src.ptr<uchar>(row);//当前行,同上
		const uchar* next = src.ptr<uchar>(row + 1);//下一行,同上
		uchar* output = dst.ptr<uchar>(row);
		for (int col = offsets; col < cols; col++) {
			output[col] = saturate_cast<uchar>(5 * cur[col] - (cur[col - offsets] + cur[col + offsets] + pre[col] + next[col]));
		}
	}
	imshow("dst", dst);
	waitKey(0);
	return 0;
}

上面的ptr表示Mat图像对象的行指针。

	cv::Mat img = cv::Mat(400, 600, CV_8UC1); 
	uchar* data00 = img.ptr<uchar>(0);
	uchar* data01 = img.ptr<uchar>(0)[1];

上述代码作用如下:

1、定义一个Mat变量img。

2、data00是指向img第一行第一个元素的指针。

3、data01是指向img第一行第二个元素的指针。

代码实现2(OpenCv函数实现)

可以利用OpenCV提供的filter2D函数完成图像卷积操作(也叫掩模操作),其函数定义为:

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

InputArray src:输入图像

OutputArray dst:输出图像。输出图像和输入图像具有相同尺寸与通道数量。

int ddepth:目标图像深度。当输入值为-1时,目标图像深度和源图像深度保持一致。

InputArray kernel:卷积核,是一个矩阵。

Point anchor=Point(-1,1):内核的基准点。其默认值为(-1,1),说明基准点即核中与进行处理的像素点重合的点。

double delta=0:在储存目标图像前可选的添加到像素的值,默认值为0。

int borderType=BORDER_DEFAULT:像素向外逼近的方法,默认的值是BORDER_DEFAULT

具体代码实现:

#include <opencv.hpp>
using namespace cv;
int main()
{
    Mat srcImage = imread("D:/360/show.jpg");
    imshow("origin image", srcImage);
    Mat kernel = (Mat_<double>(3,3) <<
        -1, 0 ,1,
        -2, 0, 2,
        -1, 0, 1);
    Mat dstImage;
    filter2D(srcImage,dstImage,srcImage.depth(),kernel);
    imshow("conv image",dstImage);
    waitKey(0);
    return 0;
}

代码中通过Mat_类代替Mat类来简化代码,Mat_类和Mat类的使用方法类似,具体如下:

Mat_<float>cMatrix=Mat::eye(3,3,CV_32F);
//“3,3”表示三行三列,且对角为1的矩阵;"CV_32F"表示0~1.0之间的32位浮点数。
cMatrix(0,0)=2.5;
cout<<cMatrix(0,0)<<endl;//输出结果2.5

如果定义的是Mat类,则需要进行如下操作:

Mat testM=Mat::eye(3,3,CV_32F);
test_M.at<float>(0,0)=2.5;
cout<<test_M.at<float>(0,0)<<endl;//输出结果2.5

卷积操作代码实现比较简单,不同的卷积操作只需要改变卷积核jike。下面是几个常见的几种卷积核:

1、平滑均值滤波卷积核。

数值数值数值
1/91/91/9
1/91/91/9
1/91/91/9
(注意代码中应该是1.0/9,否则计算结果为0)

2、高斯平滑滤波卷积核,高斯平滑水平和垂直方向呈现高斯分布,更突出了中心点在像素平滑后的权重,相比于均值滤波有着更好的平滑效果。

数值数值数值
1/162/161/16
2/164/162/9
1/162/161/16
(注意代码中应该是1.0/16,否则计算结果为0)

3、图像锐化卷积核,该卷积利用了图像中边缘信息有着比周围像素更高的对比度这一特点。经过卷积后这种对比度进一步增强,从而使得图像显得棱角分明,起到锐化图像的效果。

-1-1-1
-19-1
-1-1-1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值