Fork me on GitHub

OpenCV之图像变换

边缘检测:canny算子、sobel算子、Laplace算子、Scharr滤波器

霍夫变换

重映射

仿射变换

边缘检测

canny算子、sobel算子、Laplace算子、Scharr滤波器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;

int main()
{
Mat srcImage = imread("4.jpg");
if (!srcImage.data)
{
printf("Oh, no, srcImage is error");
return -1;

}

namedWindow("srcImage");
namedWindow("Canny1");
namedWindow("Canny2");
namedWindow("Sobel");
namedWindow("Laplace");
namedWindow("Scharr");
imshow("srcImage", srcImage);


//一.最简单的Canny用法
Mat dstImage;
dstImage = srcImage.clone();

Canny(srcImage, dstImage, 3, 9, 3);
imshow("Canny1", dstImage);

//二.高阶的Canny用法, 转成灰度图,降噪,用Canny,最后将得到的边缘作为掩码,
//拷贝原图到效果图上,得到彩色的边缘图
Mat dst, edge, gray;
//1.创建与src同类型和大小的矩阵dst
dst.create(srcImage.size(), srcImage.type());
//2.将原图转换为灰色图像
cvtColor(srcImage, gray, CV_BGR2GRAY);
//3.先用使用3*3内核降噪
blur(gray, edge, Size(3, 3));
//4.运行Canny算子
Canny(edge, edge, 3, 9, 3);
//5.将dst内的所有元素设为0
dst = Scalar::all(0);
//6.使用Canny算子输出的边缘图g_cannyDetectedEdges作为掩码,来将原图srcImage拷到目标图dst中
srcImage.copyTo(dst, edge);

imshow("Canny2", dst);


//sobel算子
Mat dst_x, dst_y;
Mat s_dst_x, s_dst_y;
Mat ddst;
//1.求x方向的梯度
Sobel(srcImage, dst_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(dst_x, s_dst_x);
imshow("X_Sobel", s_dst_x);
//2.求y方向的梯度
Sobel(srcImage, dst_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
convertScaleAbs(dst_y, s_dst_y);
imshow("Y_Sobel", s_dst_y);
//合并梯度
addWeighted(s_dst_x, 0.5, s_dst_y, 0.5, 0, ddst);
imshow("Sobel", ddst);

//Laplace算子
Mat l_gray, l_dst, l_abs_dst;
//1.使用高斯滤波消除噪声
GaussianBlur(srcImage, l_dst, Size(3, 3), 0, 0, BORDER_DEFAULT);
//2.转为灰度图
cvtColor(srcImage, l_gray, CV_RGB2GRAY);
//3.使用Laplace函数
Laplacian(l_gray, l_ds, CV_16S, 3, t1, 0, BORDER_DEFAULT);
//4.计算绝对值,并将结果转换成8位
convertScaleAbs(l_dst, l_abs_dst);
imshow("Laplace", l_abs_dst);

//Scharr滤波器
Mat s_x, s_y;
Mat s_abs_x, s_abs_y, s_dst;
//1.求X方向梯度
Scharr(srcImage, s_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
convertScaleAbs(s_x, s_abs_x);
imshow("X_Scharr", s_abs_x);
//2.求Y方向梯度
Scharr(srcImage, s_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
convertScaleAbs(s_y, s_abs_y);
imshow("Y_Scharr", s_abs_y);
//3.合并梯度
addWeighted(s_abs_x, 0.5, s_abs_y, 0.5, 0, s_dst);
imshow("Scharr", s_dst);

waitKey(0);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;

Mat srcImage, cImage, sImage, scImage, grayImage;
//Canny相关变量
Mat cannyImg;
int cannyvalue = 1;
//Sobel相关参数
Mat sobel_x, sobel_y;
Mat sobel_abs_x, sobel_abs_y;
int sobelvalue = 1;
//Scharr相关参数
Mat scharr_x, scharr_y;
Mat scharr_abs_x, scharr_abs_y;

//回调函数
void Show();
void on_Canny(int , void *);
void on_Sobel(int, void *);
void Scharr();

int main()
{
Show();

srcImage = imread("4.jpg");
if (!srcImage.data)
{
printf("Oh, no, srcImage is error");
return -1;
}

namedWindow("srcImage");
namedWindow("Canny");
namedWindow("Sobel");
namedWindow("Scharr");
imshow("srcImage", srcImage);

cImage.create(srcImage.size(), srcImage.type());

cvtColor(srcImage, grayImage, CV_BGR2GRAY);

createTrackbar("canny", "Canny", &cannyvalue, 120, on_Canny);
createTrackbar("sobel", "Sobel", &sobelvalue, 3, on_Sobel);
on_Canny(cannyvalue, 0);
on_Sobel(sobelvalue, 0);

Scharr();

while (char(waitKey(1)) != 'q') {}

return 0;
}
void Show()
{
printf("By 晴宝");
}

void on_Canny(int, void *)
{
//降噪
blur(grayImage, cannyImg, Size(3, 3));
//canny算子
Canny(cannyImg, cannyImg, cannyvalue, cannyvalue * 3, 3);
//将dstImage所有元素设为0
cImage = Scalar::all(0);
srcImage.copyTo(cImage, cannyImg);

imshow("Canny", cImage);

}
void on_Sobel(int, void *)
{
//x方向
Sobel(srcImage, sobel_x, CV_16S, 1, 0, (2 * sobelvalue + 1), 1, 0, BORDER_DEFAULT);
convertScaleAbs(sobel_x, sobel_abs_x);
//y方向
Sobel(srcImage, sobel_y, CV_16S, 0, 1, (2 * sobelvalue + 1), 1, 0, BORDER_DEFAULT);
convertScaleAbs(sobel_y, sobel_abs_y);
//合并
addWeighted(sobel_abs_x, 0.5, sobel_abs_y, 0.5, 0, sImage);

imshow("Sobel", sImage);
}
void Scharr()
{
Scharr(srcImage, scharr_x, CV_16S, 1, 0, 1, 0, BORDER_DEFAULT);
convertScaleAbs(scharr_x, scharr_abs_x);

Scharr(srcImage, scharr_y, CV_16S, 0, 1, 1, 0, BORDER_DEFAULT);
convertScaleAbs(scharr_y, scharr_abs_y);

addWeighted(scharr_abs_x, 0.5, scharr_abs_y, 0.5, 0, scImage);

imshow("Scharr", scImage);

}

霍夫变换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;

int main()
{
Mat srcImg = imread("4.jpg");
if (!srcImg.data)
{
printf("Oh, no, srcImg is error");
return -1;
}

namedWindow("srcImg");
namedWindow("Canny");
namedWindow("Lines");
namedWindow("Linesp");
namedWindow("Circle");
imshow("srcImg", srcImg);

Mat tmpImg, dstImg1, dstImg2, dstImg3;

//1.进行边缘检测和转化为灰度图
Canny(srcImg, tmpImg, 50, 200, 3);
cvtColor(tmpImg, dstImg1, CV_GRAY2BGR);
cvtColor(tmpImg, dstImg2, CV_GRAY2BGR);
//1.转为灰度图,进行图像平滑
cvtColor(srcImg, dstImg3, CV_BGR2GRAY);
GaussianBlur(dstImg3, dstImg3, Size(9, 9), 2, 2);


//2.进行霍夫线变换
vector<Vec2f> lines; //定义一个矢量结构lines用于存放得到的线段矢量集合
HoughLines(tmpImg, lines, 1, CV_PI / 180, 150, 0, 0);

vector<Vec4i> linesp;
HoughLinesP(tmpImg, linesp, 1, CV_PI / 180, 80, 50, 10);

//2.进行霍夫圆变换
vector<Vec3f> circles;
HoughCircles(dstImg3, circles, CV_HOUGH_GRADIENT, 1.5, 10, 200, 100, 0, 0);

//3.依次在图中绘制每条线段
for (size_t i = 0; i < lines.size(); i++)
{
float rho = lines[i][0], theta = lines[i][1];
Point pt1, pt2;
double a = cos(theta), b = sin(theta);
double x0 = a * rho, y0 = b * rho;
pt1.x = cvRound(x0 + 1000 * (-b));
pt1.y = cvRound(y0 + 1000 * (a));
pt2.x = cvRound(x0 - 1000 * (-b));
pt2.y = cvRound(y0 - 1000 * (a));
line(dstImg1, pt1, pt2, Scalar(55, 100, 195), 1, CV_AA);

}

for (size_t j = 0; j < linesp.size(); j++)
{
Vec4i l = linesp[j];
line(dstImg2, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(186, 88, 255), 1, CV_AA);

}

//3.依次在图中绘制图圆
for (size_t z = 0; z < circles.size(); z++)
{
Point center(cvRound(circles[z][0]), cvRound(circles[z][1]));
int radius = cvRound(circles[z][2]);
//绘制圆心
circle(srcImg, center, 3, Scalar(0, 255, 0), -1, 8, 0);
//绘制圆轮廓
circle(srcImg, center, radius, Scalar(155, 50, 255), 3, 8, 0);
}


imshow("Canny", tmpImg);
imshow("Lines", dstImg1);
imshow("Linesp", dstImg2);
imshow("Circle", srcImg);

waitKey(0);

return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
using namespace std;
using namespace cv;

Mat srcImage, dstImage, tmpImage;
vector<Vec4i> lines;
//变量接收的TrackBar位置参数
int threadhold = 100;

//回调函数
static void on_HoughLinesp(int , void *);

void Show();

int main()
{
Show();

srcImage = imread("4.jpg");
if (!srcImage.data)
{
printf("Oh, no, srcImage is error");
return -1;
}
namedWindow("srcImage");
namedWindow("HLP");
imshow("srcImage", srcImage);

createTrackbar("HoughLinesp", "HLP", &threadhold, 200, on_HoughLinesp);

//进行边缘检测和转化为灰度图
Canny(srcImage, tmpImage, 50, 200, 3);
cvtColor(tmpImage, dstImage, CV_GRAY2BGR);
//调用一次回调函数,调用一次HoughLinesp函数
on_HoughLinesp(threadhold, 0);
HoughLinesP(tmpImage, lines, 1, CV_PI/180, 80, 50, 10);

imshow("HLP", dstImage);

while (char(waitKey(1) != 'q') ) {}
return 0;
}

static void on_HoughLinesp(int, void *)
{
Mat mydstImage = dstImage.clone();
Mat mytmpImage = tmpImage.clone();

vector<Vec4i> mylines;
HoughLinesP(mytmpImage, mylines, 1, CV_PI/180, threadhold + 1, 50, 10);

for (size_t i = 0; i < mylines.size(); i++)
{
Vec4i l = mylines[i];
line(mydstImage, Point(l[0],l[1]), Point(l[2],l[3]), Scalar(23, 180, 55), 1, CV_AA);
}

imshow("HLP", mydstImage);
}

void Show()
{
printf("\n\n\n\t请调整滚动条观察图像效果\n\n");
printf("\n\n\t\t\t\t\t\t\t\t\t\t\t by 晴宝");
}

重映射

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;

int main()
{
Mat srcImage, dstImage;
Mat map_x, map_y;

srcImage = imread("4.jpg");
if(!srcImage.data)
{
printf("Oh,no, srcImage is error");
return false;
}

imshow("srcImage", srcImage);

//创建和原图一样的效果图,x重映射图, y重映射图
dstImage.create(srcImage.size(), srcImage.type());
map_x.create(srcImage.size(), CV_32FC1);
map_y.create(srcImage.size(), CV_32FC1);

//双层循环,遍历每一个像素点,改变map_x & map_y的值
for(int j = 0; j < srcImage.rows; j++)
{
for(int i = 0; i < srcImage.cols; i++)
{
//改变map_x & map_y的值
map_x.at<float>(j, i) = static_cast<float>(i);
map_y.at<float>(j, i) = static_cast<float>(srcImage.rows - j);
}
}

//进行重映射操作
remap(srcImage, dstImage, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0));

imshow("dstImage", dstImage);

waitKey(0);


return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
//重映射操作
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;

Mat srcImage, dstImage;
Mat map_x, map_y;

int update_map(int key);//更新按键按键
void Show();

int main()
{
Show();

srcImage = imread("4.jpg");
if (!srcImage.data)
{
printf("Oh, no, srcImage is error");
return false;
}
imshow("srcImage", srcImage);

dstImage.create(srcImage.size(), srcImage.type());
map_x.create(srcImage.size(), CV_32FC1);
map_y.create(srcImage.size(), CV_32FC1);

//轮询按键,更新map_x和map_y的值,进行重映射操作并显示效果图
while (1)
{
//获取键盘按键
int key = waitKey(0);

//判断ESC是否按下,若按下便退出
if ((key & 255) == 27)
{
cout << "程序退出。。。。。。\n";
break;
}

//根据按下的键盘按键更新map_x & map_y的值,然后调用remap()进行重映射
update_map(key);
remap(srcImage, dstImage, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0, 0, 0));

imshow("dstImage", dstImage);
}

return 0;
}

void Show()
{
printf("\n\n\t\t\t\t欢迎来到重映射示例程序~\n");
printf("\t当前使用的OpenCV的版本为",CV_VERSION);
printf("\n\t\t按键操作说明:\n\n"
"\t\t键盘按键【ESC】- 退出程序\n"
"\t\t键盘按键【1】 - 第一种映射方式\n"
"\t\t键盘按键【2】 - 第二种映射方式\n"
"\t\t键盘按键【3】 - 第三种映射方式\n"
"\t\t键盘按键【4】 - 第四种映射方式\n"
"\t\t\t\t\t\t\t\t\t\t\t\t\t by 晴宝\n");
}
int update_map(int key)
{
//双层循环,遍历每一个像素点
for (int j = 0; j < srcImage.rows; j++)
{
for (int i = 0; i < srcImage.cols; i++)
{
switch (key)
{
case '1':
if (i > srcImage.cols * 0.25 && i < srcImage.cols * 0.75 &&
j > srcImage.rows * 0.25 && j < srcImage.rows * 0.75)
{
map_x.at<float>(j, i) = static_cast<float>(2 * (i - srcImage.cols * 0.25) + 0.5);
map_y.at<float>(j, i) = static_cast<float>(2 * (j - srcImage.rows * 0.25) + 0.5);
}
else
{
map_x.at<float>(j, i) = 0;
map_y.at<float>(j, i) = 0;
}
break;
case '2':
map_x.at<float>(j, i) = static_cast<float>(i);
map_y.at<float>(j, i) = static_cast<float>(srcImage.rows - j);
break;
case '3':
map_x.at<float>(j, i) = static_cast<float>(srcImage.cols - i);
map_y.at<float>(j, i) = static_cast<float>(j);
break;
case '4':
map_x.at<float>(j, i) = static_cast<float>(srcImage.cols - i);
map_y.at<float>(j, i) = static_cast<float>(srcImage.rows - j);
break;
}
}
}

return 1;
}

仿射变换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;

void Show();

int main()
{
Show();

//1.参数准备
//定义两组点,代表两个三角形
Point2f srcTriangle[3];
Point2f dstTriangle[3];

//定义一些Mat变量
Mat rotMat(2, 3, CV_32FC1);
Mat warpMat(2, 3, CV_32FC1);
Mat srcImage, dstImage_warp, dstImage_rotate;

srcImage = imread("4.jpg");
if (!srcImage.data)
{
printf("Oh, no, srcImage is error");
return false;
}
imshow("srcImage", srcImage);

//设置目标图像的大小和类型与源图像一致
dstImage_warp = Mat::zeros(srcImage.rows, srcImage.cols, srcImage.type());

//设置源图像和目标图像上的三组点以计算仿射变换
srcTriangle[0] = Point2f(0, 0);
srcTriangle[1] = Point2f(static_cast<float>(srcImage.cols - 1), 0);
srcTriangle[2] = Point2f(static_cast<float>(srcImage.rows - 1));

dstTriangle[0] = Point2f(static_cast<float>(srcImage.cols*0.0), static_cast<float>(srcImage.rows*0.33));
dstTriangle[1] = Point2f(static_cast<float>(srcImage.cols*0.65), static_cast<float>(srcImage.rows*0.35));
dstTriangle[2] = Point2f(static_cast<float>(srcImage.cols*0.15), static_cast<float>(srcImage.rows*0.6));

//求得仿射变换
warpMat = getAffineTransform(srcTriangle, dstTriangle);

//对源图像应用刚刚求得的仿射变换
warpAffine(srcImage, dstImage_warp, warpMat, dstImage_warp.size());

//对图像进行缩放后再旋转
//计算绕图像中点顺时针旋转50度缩放因子为0.6的旋转矩阵
Point center = Point(dstImage_warp.cols / 2, dstImage_warp.rows / 2);
double angle = -30.0;
double scale = 0.8;

//通过上面的旋转细节信息求得旋转矩阵
rotMat = getRotationMatrix2D(center, angle, scale);

//旋转已缩放后的图像
warpAffine(dstImage_warp, dstImage_rotate, rotMat, dstImage_warp.size());

imshow("dstImage_warp", dstImage_warp);
imshow("dstImage_rotate", dstImage_rotate);


waitKey(0);

return 0;
}

void Show()
{
printf("\n\n\n\t欢迎来到【仿射变换】示例程序~\n\n");
printf("\n\n\t\t\t\t\t\t\t\t\tby 晴宝\n\n\n");
}