目的:将以下的图片转正显示
opencv自带的函数“透视变换”,但是有一点,四个交点的值我们是不知道的,有几种办法:
1.直接用鼠标去Image watch去查看四个交点的值。
2.用角点检测算法。。。我现在还没学到。
3.使用数学知识,求四条直线然后进行求取。
第一种很简单,分分钟实现了。第二种等以后学到再用。本文主要介绍第三种。
第一种方法实现:
1 Point p1; // 左上角 2 p1.x = 13; 3 p1.y = 65; 4 Point p2; // 右上角 5 p2.x = 474; 6 p2.y = 58; 7 Point p3; // 左下角 8 p3.x = 48; 9 p3.y = 310; 10 Point p4; // 右下角 11 p4.x = 461; 12 p4.y = 317; 13 14 // 透视变换 15 vectorsrc_corners(4); 16 src_corners[0] = p1; 17 src_corners[1] = p2; 18 src_corners[2] = p3; 19 src_corners[3] = p4; 20 int width, height; 21 width = SourseImage.cols; 22 height = SourseImage.rows; 23 24 vector dst_corners(4); 25 dst_corners[0] = Point(0, 0); 26 dst_corners[1] = Point(width, 0); 27 dst_corners[2] = Point(0, height); 28 dst_corners[3] = Point(width, height); 29 30 // 获取透视变换矩阵 31 Mat warpmatrix = getPerspectiveTransform(src_corners, dst_corners); 32 warpPerspective(SourseImage, OutputImage, warpmatrix, SourseImage.size(), INTER_LINEAR); 33 imshow("123", SourseImage);
效果图如下:就是手工输入有点牵强~~
第三种方法实现:
形态学计算-->>反转
边缘检测:
直线检测:(这个参数得适当的调整,不然效果很差)
透视变换:
代码:
1 int main(int argc, char** argv) 2 { 3 SourseImage = imread("1.png"); 4 if (SourseImage.empty()) return -1; 5 imshow("sourse", SourseImage); 6 //-------------------预处理----------------------// 7 GaussianBlur(SourseImage, MiddleImage, Size(3, 3), 3, 3); 8 cvtColor(MiddleImage, MiddleImage, CV_BGR2GRAY); 9 threshold(MiddleImage, MiddleImage, 0, 255, THRESH_BINARY | THRESH_OTSU); 10 imshow("threImage", MiddleImage); 11 //-------------------形态学操作-----------------// 12 Mat kernel = getStructuringElement(MORPH_RECT, Size(27, 27)); 13 morphologyEx(MiddleImage, MiddleImage, MORPH_OPEN, kernel); 14 morphologyEx(MiddleImage, MiddleImage, MORPH_OPEN, kernel); 15 bitwise_not(MiddleImage, MiddleImage, Mat()); 16 imshow("morphology", MiddleImage); 17 //----------------------边缘检测-----------------------------// 18 vector> contours; 19 vector hierarchy; 20 Mat testImage = Mat::zeros(MiddleImage.size(), MiddleImage.type()); 21 findContours(MiddleImage, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE); 22 for (size_t i = 0; i < contours.size(); i++) 23 { 24 drawContours(testImage, contours, i, Scalar(255, 255, 255), 1); 25 } 26 imshow("contours", testImage); 27 28 int width = SourseImage.cols; 29 int height = SourseImage.rows; 30 //--------------------------霍夫直线检测------------------------// 31 vector lines; 32 int accu = min(width*0.3, height*0.3); 33 HoughLinesP(testImage, lines, 1, CV_PI / 360, accu, accu, 40);//参数最好用图片的长宽变换来的,容易移植 34 Mat lineImage = Mat::zeros(MiddleImage.size(), MiddleImage.type()); 35 for (size_t i = 0; i < lines.size(); i++) 36 { 37 line(lineImage, Point(lines[i][0], lines[i][1]), Point(lines[i][2], lines[i][3]), Scalar(255, 255, 255), 1, 8, 0); 38 } 39 imshow("lines", lineImage); 40 //-----------------查找线段对应的矩形边界------------------// 41 Vec4i topline, bottomline, leftline, rightline; 42 for (size_t i = 0; i < lines.size(); i++) 43 { 44 double roti = abs(lines[i][3] - lines[i][1]); 45 //判断是平行X轴还是平行Y轴-->>|y2-y1| 46 if (roti < SourseImage.cols / 3)//平行X轴(上下两条边) 47 { 48 if (lines[i][1] < SourseImage.rows / 2 && lines[i][3] < SourseImage.rows / 2) topline = lines[i]; 49 else bottomline = lines[i]; 50 } 51 else//左右两条边 52 { 53 if (lines[i][0] < SourseImage.cols / 2 && lines[i][2] < SourseImage.cols / 2) leftline = lines[i]; 54 else rightline = lines[i]; 55 } 56 } 57 //--------------------计算四条线段的交点-----------------------// 58 double b_top, b_bot, b_rit, b_lef; 59 double k_top, k_bot, k_rit, k_lef; 60 Point lef_top, rit_top, lef_bot, rit_bot; 61 //直线斜率 62 k_top = (topline[3] - topline[1]) / (topline[2] - topline[0]); 63 k_bot = (bottomline[3] - bottomline[1]) / (bottomline[2] - bottomline[0]); 64 k_rit = (rightline[3] - rightline[1]) / (rightline[2] - rightline[0]); 65 k_lef = (leftline[3] - leftline[1]) / (leftline[2] - leftline[0]); 66 //直线表达式因子,推到一遍就知道了 67 b_top = topline[3] - k_top*topline[2]; 68 b_bot = bottomline[3] - k_bot*bottomline[2]; 69 b_rit = rightline[3] - k_rit*rightline[2]; 70 b_lef = leftline[3] - k_lef*leftline[2]; 71 //计算交点值 72 lef_top.x = abs((b_top - b_lef) / (k_top - k_lef)); 73 rit_top.x = abs((b_top - b_rit) / (k_top - k_rit)); 74 lef_bot.x = abs((b_bot - b_lef) / (k_bot - k_lef)); 75 rit_bot.x = abs((b_bot - b_rit) / (k_bot - k_rit)); 76 77 lef_top.y = abs(k_top*lef_top.x + b_top); 78 rit_top.y = abs(k_top*rit_top.x + b_top); 79 lef_bot.y = abs(k_bot*lef_bot.x + b_bot); 80 rit_bot.y = abs(k_bot*rit_bot.x + b_bot); 81 //画出交点 82 Mat lastImage = Mat::zeros(MiddleImage.size(), CV_8UC3); 83 circle(lastImage, lef_top, 5, Scalar(0, 0, 255)); 84 circle(lastImage, rit_top, 5, Scalar(0, 0, 255)); 85 circle(lastImage, lef_bot, 5, Scalar(0, 0, 255)); 86 circle(lastImage, rit_bot, 5, Scalar(0, 0, 255)); 87 imshow("circle", lastImage); 88 //-------------------------透视变换------------------------// 89 //存储需要变换的四点 90 vector srcPoint(4), dstPoint(4); 91 srcPoint[0] = lef_top; 92 srcPoint[1] = rit_top; 93 srcPoint[2] = lef_bot; 94 srcPoint[3] = rit_bot; 95 96 dstPoint[0] = Point2f(0, 0); 97 dstPoint[1] = Point2f(SourseImage.cols, 0); 98 dstPoint[2] = Point2f(0, SourseImage.rows); 99 dstPoint[3] = Point2f(SourseImage.cols, SourseImage.rows); 100 101 Mat Change = getPerspectiveTransform(srcPoint,dstPoint); 102 warpPerspective(SourseImage, OutputImage, Change, OutputImage.size()); 103 imshow("test",OutputImage); 104 waitKey(0); 105 return 0; 106 }
其实那个阈值化也可以自己手动去调节:
g_Value =95;
void on_AdaptThreshold(int, void*)
{
int k = 2 * g_Value + 1;
adaptiveThreshold(MiddleImage, OutputImage, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, k, 0);
imshow("AdaptThreshold", OutputImage);
}
参考:贾志刚opencv系列