C++ OpenCV4.5环境搭建(一)
C++ OpenCV4.5常用API查询手册(二)
C++ OpenCV4.5 图像处理(三)
C++ OpenCV4.5 绘制形状与文字(四)
C++ OpenCV4.5 图像模糊(五)
C++ OpenCV4.5 项目实战一(六)
C++ OpenCV4.5 形态学操作(七)
C++ OpenCV4.5 调整图像亮度的几种方法(八)
C++ OpenCV4.5 图像金字塔和图像阈值(九)
C++ OpenCV4.5 图像的部分处理操作(十)
本篇讲解卷积概念、常见算子和卷积边缘处理等
卷积是图像处理中一个操作,是kernel在图像的每个像素上的操作。
Kernel本质上一个固定大小的矩阵数组,其中心点称为锚点(anchor point)
把kernel放到像素数组之上,求锚点周围覆盖的像素乘积之和(包括锚点),用来替换锚点覆盖下像素点值称为卷积处理,从左往右,从上往下,挨个计算每个点的像素值;
假设Kernel是个3×3的矩阵,并且每个点的像素值为1,则卷积运算公式如下:
Sum = 8x1+6x1+6x1+2x1+8x1+6x1+2x1+2x1+8x1
New pixel = sum / (m*n)
Robert算子(分别是 X 方向和 Y 方向)
Sobel算子(分别是 X 方向和 Y 方向)
拉普拉斯算子
filter2D方法
filter2D(
- Mat src, // 输入图像
- Mat dst, // 模糊图像
- int depth, // 图像深度32/8
- Mat kernel, // 卷积核/模板
- Point anchor, // 锚点位置
- double delta // 计算出来的像素+delta )
其中 kernel是可以自定义的卷积核
代码如下(示例):
#include
#include
using namespace std;
using namespace cv;
int g_iCount = 6;
char g_szOutputWnd[] = "输出图像";
void CallbackDemo(int pos, void* userdata)
{
Mat srcImg = *((Mat*)userdata);
Mat dstImg;
switch (pos % g_iCount)
{
case 0:
{
// Robert X 方向
Mat kernelX = (Mat_<int>(2, 2) << 1, 0, 0, -1);
filter2D(srcImg, dstImg, -1, kernelX, Point(-1,-1), 0.0);
break;
}
case 1:
{
// Robert Y 方向
Mat kernelY = (Mat_<int>(2, 2) << 0, 1, -1, 0);
filter2D(srcImg, dstImg, -1, kernelY);
break;
}
case 2:
{
// Sobel X 方向
Mat kernelX = (Mat_<int>(3, 3) << -1, 0, 1, -2, 0, 2, -1, 0, 1);
filter2D(srcImg, dstImg, -1, kernelX);
break;
}
case 3:
{
// Sobel Y 方向
Mat kernelY = (Mat_<int>(3, 3) << -1, -2, -1, 0, 0, 0, 1, 2, 1);
filter2D(srcImg, dstImg, -1, kernelY);
break;
}
case 4:
{
// 拉普拉斯算子
Mat kernel = (Mat_<int>(3, 3) << 0, -1, 0, -1, 4, -1, 0, -1, 0);
filter2D(srcImg, dstImg, -1, kernel);
break;
}
case 5:
{
// 自定义卷积模糊
int iKSize = 3; // 大于1的奇数
Mat kernel = Mat::ones(Size(iKSize, iKSize), CV_32F) / (float)(iKSize * iKSize);
filter2D(srcImg, dstImg, -1, kernel);
}
default:
return;
}
imshow(g_szOutputWnd, dstImg);
}
int main(void)
{
Mat srcImg;
srcImg = imread("E:\\1.png");
if (srcImg.empty())
{
cout << "load image fail..." << endl;
return -1;
}
imshow("输入图像", srcImg);
int iValue = 0;
namedWindow(g_szOutputWnd);
createTrackbar("算子", g_szOutputWnd, &iValue, g_iCount, (TrackbarCallback)CallbackDemo, (void*)&srcImg);
CallbackDemo(iValue, (void*)&srcImg);
waitKey(0);
return 0;
}
图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有完全跟kernel重叠,所以当3x3滤波时候有1个像素的边缘没有被处理,5x5滤波的时候有2个像素的边缘没有被处理;
在卷积开始之前增加边缘像素,填充的像素值为0或者RGB黑色,比如3x3在
四周各填充1个像素的边缘,这样就确保图像的边缘被处理,在卷积处理之
后再去掉这些边缘。openCV中默认的处理方法是: BORDER_DEFAULT,此外
常用的还有如下几种:
给图像添加边缘
copyMakeBorder(
- Mat src, // 输入图像
- Mat dst, // 添加边缘图像
- int top, // 边缘长度,一般上下左右都取相同值,
- int bottom,
- int left,
- int right,
- int borderType // 边缘类型
- Scalar value )
代码如下(示例):
#include
#include
using namespace std;
using namespace cv;
int g_iCount = 4;
char g_szOutputWnd[] = "输出图像";
void CallbackDemo(int pos, void* userdata)
{
Mat srcImg = *((Mat*)userdata);
Mat dstImg;
int iTop = (int)(0.05 * srcImg.rows);
int iBottom = (int)(0.05 * srcImg.rows);
int iLeft = (int)(0.05 * srcImg.cols);
int iRight = (int)(0.05 * srcImg.cols);
int iBorderType = BORDER_DEFAULT;
switch (pos % g_iCount)
{
case 0:
{
// 填充边缘用指定像素值
iBorderType = BORDER_CONSTANT;
break;
}
case 1:
{
// 填充边缘像素用已知的边缘像素值
iBorderType = BORDER_REPLICATE;
break;
}
case 2:
{
// 用另外一边的像素来补偿填充
iBorderType = BORDER_WRAP;
break;
}
default:
// 默认-推荐
iBorderType = BORDER_DEFAULT;
break;
}
// 随机颜色
Scalar color = Scalar(0, 255, 0);
// color只有在iBorderType为BORDER_CONSTANT时才有效
copyMakeBorder(srcImg, dstImg, iTop, iBottom, iLeft, iRight, iBorderType, color);
/******************************高斯模糊边缘处理******************************/
//GaussianBlur(srcImg, dstImg, Size(3, 3), 0, 0, iBorderType);
imshow(g_szOutputWnd, dstImg);
}
int main(void)
{
Mat srcImg;
srcImg = imread("E:\\11.png");
if (srcImg.empty())
{
cout << "load image fail..." << endl;
return -1;
}
imshow("输入图像", srcImg);
int iValue = 0;
namedWindow(g_szOutputWnd);
createTrackbar("处理边缘", g_szOutputWnd, &iValue, g_iCount, (TrackbarCallback)CallbackDemo, (void*)&srcImg);
CallbackDemo(iValue, (void*)&srcImg);
waitKey(0);
return 0;
}