基于opencv图像辨认的AI五子棋系列4—棋盘透视变换
该篇文章是基于opencv图像辨认的AI五子棋系列的第四篇文章。经过该系列文章,你可以进阶opencv图像辨认,熟习图像辨认与处理的大致流程,熟习AI智能......
上一篇文章对五子棋棋盘轮廓的四个角点停止了寻觅,该篇文章,则在上一篇文章的基础上停止经过棋盘四个顶点停止透视变换。
首先看一下处理的效果图。
四个顶点
透视变换后
完成思绪
这里完成次要应用了透视变换的原理。
透视变换(perspective transformation)是将图像投影到一个新的视平面,也称为投影映射
求解变换公式的函数:
Mat getPerspectiveTransform(const Point2f src[], const Point2f dst[])输入原始图像和变换之后的图像对应4个点,便可以得到变换矩阵。之后用求解得到的矩阵输入perspectiveTransform便可以对一组点停止变换:
void perspectiveTransform(InputArray src, OutputArray dst, InputArray m)留意这里src和dst的输入并不是图像,而是图像对应的坐标。
详细参数参见官方文档:
warpPerspective
https://docs.opencv.org/2.4.9/modules/imgproc/doc/geometric_transformations.html#warpperspective
getPerspectiveTransform
https://docs.opencv.org/2.4.9/modules/imgproc/doc/geometric_transformations.html#getperspectivetransform
透视变换示例代码
下面我们经过几幅图片来直观地感受一下透视变换的作用和效果。
原图:一张倾斜拍摄的照片
透视变换处理过程
俯瞰图:透视变换结果图
经过下面三幅图,我们应该知道了透视变换的作用,下面我贴出该示例代码,供大家学习和了解。
//透视变换,检测四边形,有时分容易检测不出//但目前效果最好的就是该程序#include "stdafx.h"#include "core/core.hpp" #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/highgui/highgui.hpp> #include <iostream> #include <set>cv::Point2f center(0, 0);//求四个顶点的坐标 // 类函数,Point2f为一个类对象cv::Point2f computeIntersect(cv::Vec4i a, cv::Vec4i b){ int x1 = a[0], y1 = a[1], x2 = a[2], y2 = a[3], x3 = b[0], y3 = b[1], x4 = b[2], y4 = b[3]; if (float d = ((float)(x1 - x2)*(y3 - y4) - (y1 - y2)*(x3 - x4))) { cv::Point2f pt; pt.x = ((x1*y2 - y1 * x2)*(x3 - x4) - (x1 - x2)*(x3*y4 - y3 * x4)) / d; pt.y = ((x1*y2 - y1 * x2)*(y3 - y4) - (y1 - y2)*(x3*y4 - y3 * x4)) / d; return pt; } else return cv::Point2f(-1, -1);}//确定四个点的中心线void sortCorners(std::vector<cv::Point2f>& corners, cv::Point2f center){ std::vector<cv::Point2f> top, bot; for (unsigned int i = 0; i< corners.size(); i++) { if (corners.y<center.y) { top.push_back(corners); } else { bot.push_back(corners); } } cv::Point2f tl = top[0].x > top[1].x ? top[1] : top[0]; cv::Point2f tr = top[0].x > top[1].x ? top[0] : top[1]; cv::Point2f bl = bot[0].x > bot[1].x ? bot[1] : bot[0]; cv::Point2f br = bot[0].x > bot[1].x ? bot[0] : bot[1]; corners.clear(); //留意以下存放顺序是顺时针,当时这里出错了,假如想恣意顺序下文开拓的四边形矩阵留意对应 corners.push_back(tl); corners.push_back(tr); corners.push_back(br); corners.push_back(bl);}// 算法流程:先彩色转灰度,然后模糊求canny边缘,再用hough检测直线,// 求出四线交点,应用opencv自带的求透视矩阵的函数求出透视矩阵,// 然后应用透视矩阵转换源图像所需的四边形,效果图后续见图片:int main(){ cv::Mat src = cv::imread("4.jpg"); if (src.empty()) { return -1; } cv::Mat bw; //彩色转灰度 cv::cvtColor(src, bw, CV_BGR2GRAY); cv::namedWindow("gray_src",0); imshow("gray_src", bw); //模糊 cv::blur(bw, bw, cv::Size(3, 3)); cv::namedWindow("blur", 0); imshow("blur", bw); //边缘检测 cv::Canny(bw, bw, 100, 100, 3); cv::namedWindow("cannyblur", 0); imshow("cannyblur", bw); //hough检测直线 std::vector<cv::Vec4i> lines; cv::HoughLinesP(bw, lines, 1, CV_PI / 180, 70, 30, 10); //1像素分辨才能 1度的角度分辨才能 >70可以检测成连线 30是最小线长 //在直线L上的点(且点与点之间间隔小于maxLineGap=10的)连成线段,然后这些点全部删除,并且记录该线段的参数,就是起始点和终止点 //needed for visualization only//这里是将检测的线调整到延伸至全屏,即射线的效果,其实可以不必这么做 for (unsigned int i = 0; i<lines.size(); i++) { cv::Vec4i v = lines; lines[0] = 0; lines[1] = ((float)v[1] - v[3]) / (v[0] - v[2])* -v[0] + v[1]; lines[2] = src.cols; lines[3] = ((float)v[1] - v[3]) / (v[0] - v[2])*(src.cols - v[2]) + v[3]; } std::vector<cv::Point2f> corners;//线的交点存储 for (unsigned int i = 0; i<lines.size(); i++) { for (unsigned int j = i + 1; j<lines.size(); j++) { cv::Point2f pt = computeIntersect(lines, lines[j]); if (pt.x >= 0 && pt.y >= 0) { corners.push_back(pt); } } } std::vector<cv::Point2f> approx; cv::approxPolyDP(cv::Mat(corners), approx, cv::arcLength(cv::Mat(corners), true)*0.02, true); //检测能否是四边形,很多图片检测不到 if (approx.size() != 4) { std::cout << "The object is not quadrilateral(四边形)!" << std::endl; return -1; } //get mass center for (unsigned int i = 0; i < corners.size(); i++) { center += corners; } center *= (1. / corners.size()); sortCorners(corners, center); cv::Mat dst = src.clone(); //Draw Lines for (unsigned int i = 0; i<lines.size(); i++) { cv::Vec4i v = lines; cv::line(dst, cv::Point(v[0], v[1]), cv::Point(v[2], v[3]), CV_RGB(0, 255, 0)); //目的版块画绿线 } //draw corner points cv::circle(dst, corners[0], 3, CV_RGB(255, 0, 0), 2); cv::circle(dst, corners[1], 3, CV_RGB(0, 255, 0), 2); cv::circle(dst, corners[2], 3, CV_RGB(0, 0, 255), 2); cv::circle(dst, corners[3], 3, CV_RGB(255, 255, 255), 2); //draw mass center cv::circle(dst, center, 3, CV_RGB(255, 255, 0), 2); cv::Mat quad = cv::Mat::zeros(300, 220, CV_8UC3);//设定校正过的图片从320*240变为300*220 //corners of the destination image std::vector<cv::Point2f> quad_pts; quad_pts.push_back(cv::Point2f(0, 0)); quad_pts.push_back(cv::Point2f(quad.cols, 0));//(220,0) quad_pts.push_back(cv::Point2f(quad.cols, quad.rows));//(220,300) quad_pts.push_back(cv::Point2f(0, quad.rows)); // Get transformation matrix cv::Mat transmtx = cv::getPerspectiveTransform(corners, quad_pts); //求源坐标系(已畸变的)与目的坐标系的转换矩阵 // Apply perspective transformation透视转换 cv::warpPerspective(src, quad, transmtx, quad.size()); cv::namedWindow("src", 0); cv::imshow("src", src); cv::namedWindow("image", 0); cv::imshow("image", dst); cv::namedWindow("quadrilateral", 0); cv::imshow("quadrilateral", quad); cv::waitKey(); return 0;}最终代码
了解了下面透视变换的方法后我们就可以将五子棋盘停止透视变换了。
/*****************************************************************************************五子棋棋盘棋子辨认检测1、灰度化,二值化2、查找棋盘最外边轮廓3、找到棋盘四个顶点4、将棋盘透视变换成正投影*****************************************************************************************/#include<opencv2/opencv.hpp>#include <iostream> #include <fstream> #include <stdlib.h> //srand()和rand()函数 #include <time.h> //time()函数 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/imgproc/imgproc.hpp> #include <opencv2/objdetect/objdetect.hpp> #include <opencv2/ml/ml.hpp> #include<opencv2\opencv.hpp>#include <opencv2\imgproc\types_c.h>#include<windows.h>#define PI 3.1415926#define ROTATED_SIZE 600 //透视变换后的表盘图像大小#define CUT_SIZE 0 //透视变换时周围裁剪长度using namespace std;using namespace cv;using namespace ml;struct Index_Point{//带引索的点 Point point; int index;};/********************************************************************************************函数功能 : 找每个轮廓的中心坐标*输入参数 :轮廓或者凸包*返 回 值 : 点向量*编写工夫 : 2018.8.9*作 者 : diyun********************************************************************************************/vector< Point2f> find_lunkuo_zhongxin(vector<vector<Point>> dangban_RectContours){ vector<Point2f> zhongxin_zuobiao; /// 计算矩 vector<Moments> mu(dangban_RectContours.size()); for (int i = 0; i < dangban_RectContours.size(); i++) { mu = moments(dangban_RectContours, false); } /// 计算中心矩: vector<Point2f> mc(dangban_RectContours.size()); for (int i = 0; i < dangban_RectContours.size(); i++) { mc = Point2f(mu.m10 / mu.m00, mu.m01 / mu.m00); zhongxin_zuobiao.push_back(mc); } return zhongxin_zuobiao;}/***** 求两点间间隔*****/float getDistance(Point pointO, Point pointA){ float distance; distance = powf((pointO.x - pointA.x), 2) + powf((pointO.y - pointA.y), 2); distance = sqrtf(distance); return distance;}//两点之间间隔float getDistance_1(Point pointO, Point pointA){ float distance; distance = powf((pointO.x - pointA.x), 2) + powf((pointO.y - pointA.y), 2); distance = sqrtf(distance); return distance;}/***** 点到直线的间隔:P到AB的间隔*****/float getDist_P2L(Point pointP, Point pointA, Point pointB){ //求直线方程 int A = 0, B = 0, C = 0; A = pointA.y - pointB.y; B = pointB.x - pointA.x; C = pointA.x*pointB.y - pointA.y*pointB.x; //代入点到直线间隔公式 float distance = 0; distance = ((float)abs(A*pointP.x + B*pointP.y + C)) / ((float)sqrtf(A*A + B*B)); return distance;}//辅助函数:/***** 输入角点组的前3个值,前往按Index由大到小排好的角点组*****/vector<Index_Point> ListConnor_0(vector<Index_Point> Point_list){ int i, j; vector<Index_Point> Point_Ordered(Point_list.size()); for (j = 0; j < Point_Ordered.size() - 1; j++) { if (j == 0) { for (i = 0; i < Point_list.size(); i++) { if (Point_list.index>Point_Ordered[j].index) { Point_Ordered[j].index = Point_list.index; Point_Ordered[j].point = Point_list.point; } } } else { for (i = 0; i < Point_list.size(); i++) { if (Point_list.index<Point_Ordered[j - 1].index&&Point_list.index>Point_Ordered[j].index) { Point_Ordered[j].index = Point_list.index; Point_Ordered[j].point = Point_list.point; } } } if (Point_list[j].index == 0) { Point_Ordered[2].index = Point_list[j].index; Point_Ordered[2].point = Point_list[j].point; } } return Point_Ordered;}//辅助函数:/***** 输入角点组,前往按Index由大到小排好的角点组*****/vector<Index_Point> ListConnor(vector<Index_Point> Point_list){ int i, j; vector<Index_Point> Point_Ordered(Point_list.size()); for (j = 0; j < Point_Ordered.size(); j++) { if (j == 0) { for (i = 0; i < Point_list.size(); i++) { if (Point_list.index>Point_Ordered[j].index) { Point_Ordered[j].index = Point_list.index; Point_Ordered[j].point = Point_list.point; } } } else { for (i = 0; i < Point_list.size(); i++) { if (Point_list.index<Point_Ordered[j - 1].index&&Point_list.index>Point_Ordered[j].index) { Point_Ordered[j].index = Point_list.index; Point_Ordered[j].point = Point_list.point; } } } if (Point_list[j].index == 0) { Point_Ordered[3].index = Point_list[j].index; Point_Ordered[3].point = Point_list[j].point; } } return Point_Ordered;}/***** 2、输入单个轮廓,前往逆时针排序的4个角点*****/vector<Index_Point> FindConnor(vector<Point> RectContours){ ///**(1).根据间隔条件找出三个角点 float distance = 0, distanceMax = 0; vector<Index_Point> connorPoint(4), connor_order(4); int i = 0; distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第一个角点 distance = getDistance(RectContours, RectContours[0]); if (distance>distanceMax) { distanceMax = distance; connorPoint[0].point = RectContours; connorPoint[0].index = i; } } distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第二个角点 distance = getDistance(RectContours, connorPoint[0].point); if (distance>distanceMax) { distanceMax = distance; connorPoint[1].point = RectContours; connorPoint[1].index = i; } } distance = 0; distanceMax = 0; for (i = 0; i < RectContours.size(); i++) {//找第三个角点 distance = getDistance(RectContours, connorPoint[0].point) + getDistance(RectContours, connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connorPoint[2].point = RectContours; connorPoint[2].index = i; } } ///**(2).对曾经找到的角点陈列 connor_order = ListConnor_0(connorPoint);// connorPoint = connor_order; ///**(3).找出3个怀疑是第四角点的点 vector<Index_Point> connor4_Doubt(3); distance = 0; distanceMax = 0; for (i = connorPoint[1].index; i < connorPoint[0].index; i++) {//1,0号角点之间找到怀疑是4角点的点 distance = getDistance(RectContours, connorPoint[0].point) + getDistance(RectContours, connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[0].point = RectContours; connor4_Doubt[0].index = i; } } distance = 0; distanceMax = 0; for (i = connorPoint[2].index; i < connorPoint[1].index; i++) {//2,1号角点之间找到怀疑是4角点的点 distance = getDistance(RectContours, connorPoint[2].point) + getDistance(RectContours, connorPoint[1].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[1].point = RectContours; connor4_Doubt[1].index = i; } } distance = 0; distanceMax = 0; for (i = connorPoint[0].index; i < RectContours.size() + connorPoint[2].index; i++) {//0,2号角点之间找到怀疑是4角点的点 if (i< RectContours.size()) { distance = getDistance(RectContours, connorPoint[0].point) + getDistance(RectContours, connorPoint[2].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[2].point = RectContours; connor4_Doubt[2].index = i; } } else { distance = getDistance(RectContours[i - RectContours.size()], connorPoint[0].point) + getDistance(RectContours[i - RectContours.size()], connorPoint[2].point); if (distance>distanceMax) { distanceMax = distance; connor4_Doubt[2].point = RectContours[i - RectContours.size()]; connor4_Doubt[2].index = i; } } } ///**(4).经过点到直线的间隔找到第四个角点 if (getDist_P2L(connor4_Doubt[0].point, connorPoint[0].point, connorPoint[1].point)>10) { connorPoint[3] = connor4_Doubt[0]; } else if (getDist_P2L(connor4_Doubt[1].point, connorPoint[1].point, connorPoint[2].point)>10) { connorPoint[3] = connor4_Doubt[1]; } else if (getDist_P2L(connor4_Doubt[2].point, connorPoint[0].point, connorPoint[2].point)>10) { connorPoint[3] = connor4_Doubt[2]; } ///**(5).对四个角点按顺时针排序 connor_order = ListConnor(connorPoint); return connor_order;}/***** 3、输入逆时针排序的4个角点,把0号点赋给右下角那个,最后逆时针输入*****/vector<Point> FindFirstPoint(vector<Index_Point> Point_list){ int i, Ymax, Y; float Kl, Kr; vector<Index_Point> Connor_Point_Ready; for (i = 0; i < Point_list.size(); i++) { Point_list.index = i; } for (i = 0; i < Point_list.size(); i++) {//找到一切上一级斜比下一级小的角点 if (i>0 && i<Point_list.size() - 1) { Kl = abs(((float)(Point_list.point.y - Point_list[i - 1].point.y)) / ((float)(Point_list.point.x - Point_list[i - 1].point.x))); Kr = abs(((float)(Point_list.point.y - Point_list[i + 1].point.y)) / ((float)(Point_list.point.x - Point_list[i + 1].point.x))); if (Kl<Kr) { Connor_Point_Ready.push_back(Point_list); } } else if (i == 0) { Kl = abs(((float)(Point_list.point.y - Point_list[Point_list.size() - 1].point.y)) / ((float)(Point_list.point.x - Point_list[Point_list.size() - 1].point.x))); Kr = abs(((float)(Point_list.point.y - Point_list[i + 1].point.y)) / ((float)(Point_list.point.x - Point_list[i + 1].point.x))); if (Kl<Kr) { Connor_Point_Ready.push_back(Point_list); } } else if (i == Point_list.size() - 1) { Kl = abs(((float)(Point_list.point.y - Point_list[i - 1].point.y)) / ((float)(Point_list.point.x - Point_list[i - 1].point.x))); Kr = abs(((float)(Point_list.point.y - Point_list[0].point.y)) / ((float)(Point_list.point.x - Point_list[0].point.x))); if (Kl<Kr) { Connor_Point_Ready.push_back(Point_list); } } } Y = 0; Ymax = 0; Index_Point Point_First; Point_First.point = Point(0, 0); Point_First.index = 0; for (i = 0; i < Connor_Point_Ready.size(); i++) {//以y最大的那个点作为first点 if (Connor_Point_Ready.point.y >= Ymax) { Point_First.point = Connor_Point_Ready.point; Point_First.index = Connor_Point_Ready.index; Ymax = Connor_Point_Ready.point.y; } } vector<Point> PointOrder(Point_list.size()); if (Point_list.size()>0) { for (i = 0; i < Point_list.size(); i++) { if (Point_First.index + i< Point_list.size()) { PointOrder = Point_list[Point_First.index + i].point; } else { PointOrder = Point_list[Point_First.index + i - Point_list.size()].point; } } } return PointOrder;}//数字转换字符串string num2str(int i){ stringstream ss; ss << i; return ss.str();}/*输入原图和(一组)四个角点,前往透视变换后的图方向dir: 0.从下往上看1.从左往右看2.从上往下看3.从右往左看*/Mat RotateContours(Mat srcImg, Point* Connor, int dir){ //找一个轮廓放入dstImg Mat dstImg; Mat drawing(srcImg.size(), CV_8UC1, cv::Scalar(0)); vector<vector<Point>> RectContours; vector<Point> pts; pts.push_back(Connor[0]); pts.push_back(Connor[1]); pts.push_back(Connor[2]); pts.push_back(Connor[3]); RectContours.push_back(pts); drawContours(drawing, RectContours, 0, Scalar(255, 255, 255), -1); srcImg.copyTo(dstImg, drawing);//截取出需求的图片 //透视变换 Point2f src_vertices[4]; for (int i = 0; i < 4; i++) { src_vertices.x = pts.x; src_vertices.y = pts.y; } Point2f dst_vertices[4], Targer[4]; Targer[0] = Point(0, 0); Targer[1] = Point(ROTATED_SIZE, 0); Targer[2] = Point(ROTATED_SIZE, ROTATED_SIZE); Targer[3] = Point(0, ROTATED_SIZE); for (int i = 0; i < 4; i++) { if (i + dir<4) { dst_vertices = Targer[i + dir]; } else { dst_vertices = Targer[i + dir - 4]; } } Mat warpMatrix = getPerspectiveTransform(src_vertices, dst_vertices); Mat rotated; warpPerspective(dstImg, rotated, warpMatrix, Size(ROTATED_SIZE, ROTATED_SIZE), INTER_LINEAR, BORDER_CONSTANT); rotated = rotated(Range(CUT_SIZE, ROTATED_SIZE - CUT_SIZE), Range(CUT_SIZE, ROTATED_SIZE - CUT_SIZE)); //测试 //imshow("透视变换", rotated); return rotated;}//5、输入原图及一切角点,前往一切透视变换后的图像,用于停止仪表辨认vector<Mat> Find_rotated_all(Mat srcImg, vector<vector<Point>>AllConnor){ vector<Mat> rotated_all; Mat rotated; for (int i = 0; i < AllConnor[0].size(); i++) { Point one_ConnorGroup[4]; for (int j = 0; j < 4; j++) {//为单独一组角点赋值 one_ConnorGroup[j] = AllConnor[j]; } rotated = RotateContours(srcImg, one_ConnorGroup, 0); //测试 //namedWindow("透视变换", 0); //imshow("透视变换", rotated); //waitKey(2); } rotated_all.push_back(rotated); return rotated_all;}int main(){ float ret = 0; Mat srcImage0 = imread("1.jpg");//读取图片 //Mat srcImage0 = imread("app/1.jpg");//读取图片 if (srcImage0.empty()) { cout << " 待预测图像不存在: " << endl; printf("[ALG ERROR][函数:%s][行号:%d],图片未正常读取,请检查输入途径非常正确 \n", __FUNCTION__, __LINE__, 1); cout << " 待预测图像不存在: " << endl; } Mat srcImage, srcImage1; resize(srcImage0, srcImage0, Size(1920, 1080)); cvtColor(srcImage0, srcImage1, CV_BGR2GRAY); namedWindow("灰度化", 0); imshow("灰度化", srcImage1); waitKey(2); srcImage = srcImage1 > 150; // 二值化 namedWindow("二值化", 0); imshow("二值化", srcImage); waitKey(2); /*****1、输入二值化图像前往有用的轮廓*****/ /*普统统过长度滤除剩下两个,选择外部那个*/ vector<vector<Point>> contours, RectContours, RectContours_fainal; int height = srcImage.rows; findContours(srcImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);//找轮廓 CV_RETR_EXTERNAL,表示最外层轮廓 vector<vector<Point>> hull(contours.size());//用于存放凸包 vector<float> length(contours.size()); vector<float> Area_contours(contours.size()), Area_hull(contours.size()), Rectangularity(contours.size()), circularity(contours.size()); Mat drawing(srcImage.size(), CV_8UC3, cv::Scalar(255, 255, 255)); for (int i = 0; i < contours.size(); i++) {//历遍一切的轮廓 length = arcLength(contours, true); if (length >2000 && length <12000) {//经过长度婚配滤除小轮廓 convexHull(Mat(contours), hull, false);//把凸包找出来 Area_contours = contourArea(contours); Area_hull = contourArea(hull); Rectangularity = Area_contours / Area_hull; printf("过滤后轮廓长度为 length=%5f\n", length); circularity = (4 * 3.1415*Area_contours) / (length * length); //drawContours(drawing, contours, i, (255, 0, 0), 2);//得到方框 //if (Rectangularity>0.9&&circularity<0.8&&Area_hull>8000 && Area_hull<50000) {//经过凸包面积滤除不对的凸包,找到最终的四边形 RectContours.push_back(hull);//把提取出来的方框导入到新的轮廓组 drawContours(drawing, hull, i, Scalar(0, 0, 0), 1);//得到方框 } } } if (0 == RectContours.size()) { printf("[ALG ERROR][函数:%s][行号:%d],RectContours.size=0,当前帧未找到轮廓,检查仪表二值化能否正常\n", __FUNCTION__, __LINE__); } /*辨认结果显示,调试用*/ //namedWindow("找轮廓结果", 0); //imshow("找轮廓结果", drawing); //waitKey(2); /*将多个轮廓,弄成一个*/ cvtColor(drawing, drawing, CV_BGR2GRAY); drawing = drawing<150; // 二值化 //imshow("二值化", drawing); //将线衔接起来 vector<Point2f> zhongxin_zuobiao; zhongxin_zuobiao = find_lunkuo_zhongxin(RectContours); if (zhongxin_zuobiao.size() >= 2) { for (int i = 0; i < zhongxin_zuobiao.size() - 1; i++) { for (int j = i + 1; j < zhongxin_zuobiao.size(); j++) { float dis_juxin = getDistance_1(zhongxin_zuobiao, zhongxin_zuobiao[j]); //if (dis_juxin <65) { line(drawing, zhongxin_zuobiao, zhongxin_zuobiao[j], Scalar(0), 40, LINE_AA); } } } } vector<vector<Point>> contours1; findContours(drawing, contours1, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);//找轮廓 CV_RETR_EXTERNAL,表示最外层轮廓 Mat drawing1(drawing.size(), CV_8UC3, cv::Scalar(255, 255, 255)); vector<vector<Point>> hull1(contours1.size());//用于存放凸包 for (int i = 0; i < contours1.size(); i++) {//历遍一切的轮廓 //drawContours(drawing1, contours1, i, Scalar(0, 255, 0), 1);//得到方框 convexHull(Mat(contours1), hull1, false);//把凸包找出来 RectContours_fainal.push_back(hull1);//把提取出来的方框导入到新的轮廓组 drawContours(drawing1, hull1, i, Scalar(0, 0, 0), 4);//得到方框 } namedWindow("最终轮廓", 0); imshow("最终轮廓", drawing1); vector<Index_Point> ConnorPoint(4); vector<Point> ConnorPoint_ordered(4); vector<vector<Point>> ConnorPoint_Output(4); int i; string filename;//裁剪出来的负样本图片文件名 for (i = 0; i < RectContours_fainal.size(); i++) { ConnorPoint = FindConnor(RectContours_fainal); ConnorPoint_ordered = FindFirstPoint(ConnorPoint); ConnorPoint_Output[0].push_back(ConnorPoint_ordered[2]); ConnorPoint_Output[1].push_back(ConnorPoint_ordered[1]); ConnorPoint_Output[2].push_back(ConnorPoint_ordered[0]); ConnorPoint_Output[3].push_back(ConnorPoint_ordered[3]); } //ConnorPoint_Output[0]代表第i个轮廓的0号角点,顺时针输入 //return ConnorPoint_Output; vector<Mat> rotated_all; if (ConnorPoint_Output[0].size()>0) { vector<vector<Point>> RectContours; vector<Point> pts; for (int i = 0; i < ConnorPoint_Output[0].size(); i++) { //2、输入原图及一切角点,前往一切透视变换后的图像,用于停止仪表辨认 rotated_all = Find_rotated_all(srcImage0, ConnorPoint_Output); Point one_ConnorGroup[4]; for (int j = 0; j < 4; j++) {//为单独一组角点赋值 one_ConnorGroup[j] = ConnorPoint_Output[j]; circle(srcImage0, one_ConnorGroup[j], 10, Scalar(0, 255, 0), -1); //第五个参数我设为-1,表明这是个实点。 } namedWindow("四个角点", 0); imshow("四个角点", srcImage0); waitKey(2); } } else { //没找到,前往下帧继续找 printf("[ALG ERROR][函数:%s][行号:%d],当前帧未找到方形仪表四边形四个角点,前往,下帧继续找 \n", __FUNCTION__, __LINE__); return ERROR; } printf("透视变换后的图像个数=%d \n", rotated_all.size()); if (rotated_all.size() == 0) { //没找到,前往下帧继续找 printf("[ALG ERROR][函数:%s][行号:%d],未找到透视变换后的图像 \n", __FUNCTION__, __LINE__); return ERROR; return ERROR; } //namedWindow("test_img[]", 0); //imshow("test_img[]", rotated_all[0]); filename = "finall_rotated_all_" + num2str(0) + ".jpg"; imwrite(filename, rotated_all[0]); namedWindow("透视变换后棋盘", 0); imshow("透视变换后棋盘", rotated_all[0]); waitKey(2); waitKey(2000); waitKey(2000); waitKey(0); return 0;} |