Code
假设有6x6的图像像素点矩阵。
卷积过程:6x6上面是个3x3的窗口,从左向右,从上向下移动,黄色的每个像个像素点值之和取平均值赋给中心红色像素作为它卷积处理之后新的像素值。每次移动一个像素格。
c++
- blur(Mat src, Mat dst, Size(xradius, yradius), Point(-1,-1));
c++
- GaussianBlur(Mat src, Mat dst, Size(11, 11), sigmax, sigmay);
其中Size(x, y), x, y 必须是正数而且是奇数
c++
中值模糊 medianBlur(Mat src, Mat dest, ksize)
中值模糊的ksize大小必须是大于1而且必须是奇数。
c++
双边模糊 bilateralFilter(src, dest, d=15, 150, 3);
- 15 –计算的半径,半径之内的像数都会被纳入计算,如果提供-1 则根据sigma space参数取值
- 150 – sigma color 决定多少差值之内的像素会被计算
- 3 – sigma space 如果d的值大于0则声明无效,否则根据它来计算d值
跟卷积操作类似,假设有图像A和结构元素B,结构元素B在A上面移动,其中B定义其中心为锚点, 计算B覆盖下A的最大像素值用来替换锚点的像素,其中B作为结构体可以是任意形状
腐蚀跟膨胀操作的过程类似,唯一不同的是以最小值替换锚点重叠下图像的像素值
c++
int element_size = 3;
int max_size = 21;
Mat src, dst, gauss_dst;
void CallBack_Demo(int, void*);
string input_title = "input img";
string output_title = "output img";
string output2gauss_title = "output2gauss img";
int main(int argc, char ** argv) {
src = imread("C:\\Users\\Administrator\\Pictures\\车牌.jpg");
if (!src.data)
{
cout << "read img error" << endl;
return -1;
}
imshow(input_title, src);
namedWindow(output_title, CV_WINDOW_AUTOSIZE);
createTrackbar("Element Size", output_title, &element_size, max_size, CallBack_Demo);
waitKey(0);
return 0;
}
void CallBack_Demo(int, void*) {
int s = element_size * 2 + 1;
/*getStructuringElement(int shape, Size ksize, Point anchor)
- 形状 (MORPH_RECT \MORPH_CROSS \MORPH_ELLIPSE)
- 大小
- 锚点 默认是Point(-1, -1)意思就是中心像素*/
Mat structureElement = getStructuringElement(MORPH_RECT, Size(s, s), Point(-1, -1));
dilate(src, dst, structureElement,Point(-1,-1),1); //膨胀
//erode(src, dst, structureElement, Point(-1, -1), 1); //腐蚀
imshow(output_title, dst);
return;
}
Code
TrackBar – createTrackbar(const String & trackbarname, const String winName, int* value, int count, Trackbarcallback func, void* userdata=0)
其中最中要的是 callback 函数功能。如果设置为NULL就是说只有值update,但是不会调用callback的函数。
顶帽 是原图像与开操作之间的差值图像
黑帽是闭操作图像与源图像的差值图像
c++
morphologyEx(src, dest, CV_MOP_BLACKHAT, kernel);
- Mat src – 输入图像
- Mat dest – 输出结果
- int OPT – CV_MOP_OPEN/ CV_MOP_CLOSE/ CV_MOP_GRADIENT / CV_MOP_TOPHAT/ CV_MOP_BLACKHAT 形态学操作类型
Mat kernel 结构元素
int Iteration 迭代次数,默认是1
图像形态学操作时候,可以通过自定义的结构元素实现结构元素 对输入图像一些对象敏感、另外一些对象不敏感,这样就会让敏 感的对象改变而不敏感的对象保留输出。通过使用两个最基本的 形态学操作 – 膨胀与腐蚀,使用不同的结构元素实现对输入图像 的操作、得到想要的结果。
c++
Mat src, dst;
src = imread("C:\\Users\\15646\\Pictures\\验证码.jpg");
if (!src.data)
{
cout << "not found img" << endl;
waitKey(0);
return -1;
}
//输出原图
imshow("input img", src);
//转化为灰度图像
Mat gray_src;
cvtColor(src, gray_src, CV_BGR2GRAY);
imshow("gray img", gray_src);
//转化为二值图像
/*adaptiveThreshold(
Mat src, // 输入的灰度图像
Mat dest, // 二值图像
double maxValue, // 二值图像最大值
int adaptiveMethod // 自适应方法,只能其中之一 –
// ADAPTIVE_THRESH_MEAN_C , ADAPTIVE_THRESH_GAUSSIAN_C
int thresholdType,// 阈值类型
int blockSize, // 块大小
double C // 常量C 可以是正数,0,负数
)*/
Mat binImg;
adaptiveThreshold(~gray_src, binImg, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 15, -2);
imshow("binary img", binImg);
//定义结构元素
//水平结构元素
Mat hline = getStructuringElement(MORPH_RECT, Size(src.cols / 16, 1), Point(-1, -1));
//垂直结构元素
Mat vline = getStructuringElement(MORPH_RECT, Size(1, src.cols / 16), Point(-1, -1));
//开操作 (腐蚀+膨胀)提取 水平线
//Mat temp;
//erode(binImg, temp, hline);//腐蚀
//dilate(temp, dst, hline);//膨胀
//直接使用开操作的api即可
morphologyEx(binImg, dst, CV_MOP_OPEN, vline);
bitwise_not(dst, dst);
blur(dst, dst, Size(3, 3), Point(-1, -1));
imshow("res", dst);
waitKey(0);
return 0;
Code
pyrUp(Mat src, Mat dst, Size(src.cols*2, src.rows*2))
生成的图像是原图在宽与高各放大两倍
pyrDown(Mat src, Mat dst, Size(src.cols/2, src.rows/2))
生成的图像是原图在宽与高各缩小1/2
c++
//上采样
pyrUp(src, dst, Size(src.cols * 2, src.rows * 2));
imshow("pyrUp img",dst);
//降采样
pyrDown(src, dst, Size(src.cols / 2, src.rows / 2));
imshow("pyrDown img", dst);
//DOG
Mat gray_src, g1, g2, dogImg;
cvtColor(src, gray_src, CV_BGR2GRAY);
GaussianBlur(gray_src, g1, Size(3, 3), 0, 0);
GaussianBlur(g1, g2, Size(3, 3), 0, 0);
subtract(g1, g2, dogImg, Mat());
//归一化显示
normalize(dogImg, dogImg, 255, 0, NORM_MINMAX);
imshow("dog img", dogImg);
阈值 是什么?简单点说是把图像分割的标尺,这个标尺是根据什么产生的,阈值产生算法?阈值类型。(Binary segmentation)
6、相关的API
示例代码 c++
Mat src, dst, gray_src;
int threshold_value = 127;
int threshold_max = 255;
int type_value = 2;
int type_max = 4;
string output_title = "output img";
void Threshold_Demo(int, void *);
int main(int argc, char ** argv) {
src = imread("C:\\Users\\15646\\Pictures\\雷军.jpg");
if (!src.data)
{
cout << "not found img" << endl;
waitKey(0);
return -1;
}
namedWindow(output_title, CV_WINDOW_AUTOSIZE);
createTrackbar("Threshold Value", output_title, &threshold_value, threshold_max, Threshold_Demo);
createTrackbar("Type Value", output_title, &type_value, type_max, Threshold_Demo);
Threshold_Demo(0, 0);
waitKey(0);
return 0;
}
void Threshold_Demo(int, void *) {
cvtColor(src, gray_src, CV_BGR2GRAY);
//设置阈值
threshold(gray_src, dst, threshold_value, threshold_max,type_value);
imshow(output_title, dst);
}
六、自定义线性滤波 1、卷积概念
把kernel放到像素数组之上,求锚点周围覆盖的像素乘积之和(包括锚点),用来替换锚点覆盖下像素点值称为卷积处理。数学表达如下:
+1 | 0 |
---|---|
0 | -1 |
0 | +1 |
---|---|
-1 | 0 |
-1 | 0 | 1 |
---|---|---|
-2 | 0 | 2 |
-1 | 0 | 1 |
-1 | -2 | -1 |
---|---|---|
0 | 0 | 0 |
1 | 2 | 1 |
0 | -1 | 0 |
---|---|---|
-1 | 4 | -1 |
0 | -1 | 0 |
c++
//Robert算子 X方向
Mat kernel_x = (Mat_<int>(2, 2) << 1, 0, 0, -1);
filter2D(src, dst, -1, kernel_x, Point(-1, -1), 0, 0);
imshow("Robert X", dst);
//Robert算子 Y方向
Mat kernel_y = (Mat_<int>(2, 2) << 0,1,-1,0);
filter2D(src, dst, -1, kernel_y, Point(-1, -1), 0, 0);
c++
ilter2D方法filter2D(
Mat src, //输入图像
Mat dst, // 模糊图像
int depth, // 图像深度32/8
Mat kernel, // 卷积核/模板
Point anchor, // 锚点位置
double delta // 计算出来的像素+delta
)
其中 kernel是可以自定义的卷积核
c++
int c = 0;
int index = 0;
int ksize = 0;
while (true)
{
c = waitKey(500);
if ((char)c == 27)
{
break;
}
ksize = 4 + (index % 5) * 2;
Mat kernel = Mat::ones(Size(ksize, ksize), CV_32F) / (float)(ksize*ksize);
filter2D(src, dst, -1, kernel, Point(-1, -1));
index++;
imshow("show", dst);
}
图像卷积的时候边界像素,不能被卷积操作,原因在于边界像素没有完全跟kernel重叠,所以当3x3滤波时候有1个像素的边缘没有被处理,5x5滤波的时候有2个像素的边缘没有被处理。
在卷积开始之前增加边缘像素,填充的像素值为0或者RGB黑色,比如3x3在 四周各填充1个像素的边缘,这样就确保图像的边缘被处理,在卷积处理之 后再去掉这些边缘。openCV中默认的处理方法是: BORDER_DEFAULT,此外 常用的还有如下几种:
c++
Mat src, dst;
src = imread("C:\\Users\\15646\\Pictures\\雷军.jpg");
int top = (int)(0.05*src.rows);
int bottom = (int)(0.05*src.rows);
int left = (int)(0.05*src.cols);
int right = (int)(0.05*src.cols);
RNG rng;
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
/*copyMakeBorder(
- Mat src, // 输入图像
-Mat dst, // 添加边缘图像
-int top, // 边缘长度,一般上下左右都取相同值,
-int bottom,
-int left,
-int right,
-int borderType // 边缘类型
- Scalar value
)
*/
copyMakeBorder(src, dst, top, bottom, left, right, BORDER_CONSTANT,color);
imshow("output img", dst);
示例代码
c++
Mat src, dst;
src = imread("C:\\Users\\15646\\Pictures\\雷军.jpg");
Mat gray_dst;
GaussianBlur(src, dst, Size(3, 3), 0, 0);
cvtColor(dst, gray_dst, CV_BGR2GRAY);
//图像边缘提取
Mat xgrad, ygrad;
//获取x,y方向上的梯度图像
/*cv::Sobel(
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度.
Int dx. // X方向,几阶导数
int dy // Y方向,几阶导数.
int ksize, SOBEL算子kernel大小,必须是1、3、5、7、
double scale = 1
double delta = 0
int borderType = BORDER_DEFAULT
)*/
Sobel(gray_dst, xgrad, CV_16S, 1, 0, 3);
Sobel(gray_dst, ygrad, CV_16S, 0, 1, 3);
convertScaleAbs(xgrad, xgrad); //计算图像像素的绝对值再输出到此图像
convertScaleAbs(ygrad, ygrad);
//合成x,y方向上的梯度图像
Mat res;
addWeighted(xgrad, 0.5, ygrad, 0, 0, res);
imshow("res img", res);
//手动合成
Mat xygrad = Mat(xgrad.size(), xgrad.type());
int height = ygrad.rows;
int width = xgrad.cols;
for (int row = 0; row < height; row++)
{
for (int col = 0; col < width; col++)
{
//将水平梯度和垂直梯度的值相加获取我们需要的值,合成图像
xygrad.at<uchar>(row, col) = saturate_cast<uchar>(xgrad.at<uchar>(row, col) + ygrad.at<uchar>(row, col));
}
}
namedWindow("xygrad", CV_WINDOW_AUTOSIZE);
imshow("xygrad", xygrad);
c++
cv::Scharr (
InputArray Src // 输入图像
OutputArray dst// 输出图像,大小与输入图像一致
int depth // 输出图像深度.
Int dx. // X方向,几阶导数
int dy // Y方向,几阶导数.
double scale = 1
double delta = 0
int borderType = BORDER_DEFAULT
)
c++
Laplacian(
InputArray src,
OutputArray dst,
int depth, //深度CV_16S
int kisze, // 3
double scale = 1,
double delta =0.0,
int borderType = 4
)
c++
Mat src, dst, edge_img;
src = imread("C:\\Users\\15646\\Pictures\\雷军.jpg");
Mat gray_dst;
GaussianBlur(src, dst, Size(3, 3), 0, 0);
cvtColor(dst, gray_dst, CV_BGR2GRAY);
threshold(edge_img, edge_img, 0, 255, THRESH_OTSU | THRESH_BINARY);
Laplacian(gray_dst, edge_img, CV_16S, 3);
convertScaleAbs(edge_img, edge_img);
imshow("laplance img", edge_img);
c++
Mat gray_dst, src;
int t1_value = 50;
int max_value = 255;
string output_title = "output img";
void CannyDemo(int, void*);
int main(int argc, char* argv) {
src = imread("C:\\Users\\15646\\Pictures\\雷军.jpg");
namedWindow(output_title, CV_WINDOW_AUTOSIZE);
//灰度转换
cvtColor(src, gray_dst, CV_BGR2GRAY);
createTrackbar("Threshold Value", output_title, &t1_value, max_value, CannyDemo);
CannyDemo(0, 0);
waitKey(0);
return 0;
}
void CannyDemo(int, void*) {
Mat edge_dst;
//高斯模糊
GaussianBlur(gray_dst, edge_dst, Size(3, 3), 0,0);
/*Canny(
InputArray src, // 8-bit的输入图像
OutputArray edges,// 输出边缘图像, 一般都是二值图像,背景是黑色
double threshold1,// 低阈值,常取高阈值的1/2或者1/3
double threshold2,// 高阈值
int aptertureSize,// Soble算子的size,通常3x3,取值3
bool L2gradient // 选择 true表示是L2来归一化,否则用L1归一化,一般用L1
)*/
Canny(gray_dst, edge_dst, t1_value, t1_value * 2, 3, false);
imshow(output_title, edge_dst);
}