大家好,又见面了,我是你们的朋友全栈君。
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;
}
附:上述为个人理解,可能存在考虑不全面的情况,如果哪里有问题,还望大家留言指出~
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/144019.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...