赞
踩
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; }
附:上述为个人理解,可能存在考虑不全面的情况,如果哪里有问题,还望大家留言指出~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。