网站首页 > 技术文章 正文
基于opencv图像识别的AI五子棋系列3—查找棋盘四个顶点
该篇文章是基于opencv图像识别的AI五子棋系列的第三篇文章。通过该系列文章,你可以进阶opencv图像识别,熟悉图像识别与处理的大致流程,熟悉AI智能......
上一篇文章对五子棋棋盘进行了棋盘轮廓的查找,该篇文章,则在上一篇文章的基础上进行棋盘四个顶点的查找,查找到四个顶点后方便我们进行后续处理。
首先看一下处理的效果图
程序具体实现过程
在上篇文章基础上,我们查找到了棋盘的轮廓,如下图:
这里的轮廓是保存在
vector<vector<Point>> RectContours_fainal ;
里的。我们要在一个不规则的四边形中找到四个顶点还是需要一些技巧的。
这里我写了一个FindConnor() 函数来实现寻找到四个角点。函数实现如下:
/***** 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[i], RectContours[0]);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint[0].point = RectContours[i];
connorPoint[0].index = i;
}
}
distance = 0;
distanceMax = 0;
for (i = 0; i < RectContours.size(); i++)
{//找第二个角点
distance = getDistance(RectContours[i], connorPoint[0].point);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint[1].point = RectContours[i];
connorPoint[1].index = i;
}
}
distance = 0;
distanceMax = 0;
for (i = 0; i < RectContours.size(); i++)
{//找第三个角点
distance = getDistance(RectContours[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[1].point);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint[2].point = RectContours[i];
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[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[1].point);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_Doubt[0].point = RectContours[i];
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[i], connorPoint[2].point) + getDistance(RectContours[i], connorPoint[1].point);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_Doubt[1].point = RectContours[i];
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[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[2].point);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_Doubt[2].point = RectContours[i];
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;
}
具体思路如下:
首先我们观察四边形,哪些点会是角点呢?显然是对角线这一特征,根据这一个特性,我们遍历所有的点,并一 一求出两点之间距离,在两个点距离最远的时候,肯定是四个角点中的两个。
找第一个顶点——第一个顶点怎么求,首先随意找轮廓中一个点,然后求任意两点距离,距离最大的那个肯定是个顶点,然后同理,以第一个点为基准,找第二个顶点。第三个顶点就是,距离第一个顶点和第二个顶点距离最远的那个点,然后对前三个点进行排序,最后再找第四个顶点,第四个顶点肯定在三个已知任意两个顶点的中间,求出来,然后再根据距离,就可以找到了。
然后我们利用ListConnor函数对找到的四个顶点进行排序,使它顺时针排序。方便后续我们透视变换用——透视变换需要指定顺序才能进行透视变换。
下面语句是将得到的四个角点画出来。
if (ConnorPoint_Output[0].size()>0)
{
vector<vector<Point>> RectContours;
vector<Point> pts;
for (int i = 0; i < ConnorPoint_Output[0].size(); i++)
{
Point one_ConnorGroup[4];
for (int j = 0; j < 4; j++)
{//为单独一组角点赋值
one_ConnorGroup[j] = ConnorPoint_Output[j][i];
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;
}
完整源码如下:
/*****************************************************************************************
五子棋棋盘棋子识别检测
1、灰度化,二值化
2、查找棋盘最外边轮廓
3、找到棋盘四个顶点
*****************************************************************************************/
#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>
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[i] = moments(dangban_RectContours[i], false);
}
/// 计算中心矩:
vector<Point2f> mc(dangban_RectContours.size());
for (int i = 0; i < dangban_RectContours.size(); i++)
{
mc[i] = Point2f(mu[i].m10 / mu[i].m00, mu[i].m01 / mu[i].m00);
zhongxin_zuobiao.push_back(mc[i]);
}
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[i].index>Point_Ordered[j].index)
{
Point_Ordered[j].index = Point_list[i].index;
Point_Ordered[j].point = Point_list[i].point;
}
}
}
else
{
for (i = 0; i < Point_list.size(); i++)
{
if (Point_list[i].index<Point_Ordered[j - 1].index&&Point_list[i].index>Point_Ordered[j].index)
{
Point_Ordered[j].index = Point_list[i].index;
Point_Ordered[j].point = Point_list[i].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[i].index>Point_Ordered[j].index)
{
Point_Ordered[j].index = Point_list[i].index;
Point_Ordered[j].point = Point_list[i].point;
}
}
}
else
{
for (i = 0; i < Point_list.size(); i++)
{
if (Point_list[i].index<Point_Ordered[j - 1].index&&Point_list[i].index>Point_Ordered[j].index)
{
Point_Ordered[j].index = Point_list[i].index;
Point_Ordered[j].point = Point_list[i].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[i], RectContours[0]);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint[0].point = RectContours[i];
connorPoint[0].index = i;
}
}
distance = 0;
distanceMax = 0;
for (i = 0; i < RectContours.size(); i++)
{//找第二个角点
distance = getDistance(RectContours[i], connorPoint[0].point);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint[1].point = RectContours[i];
connorPoint[1].index = i;
}
}
distance = 0;
distanceMax = 0;
for (i = 0; i < RectContours.size(); i++)
{//找第三个角点
distance = getDistance(RectContours[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[1].point);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint[2].point = RectContours[i];
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[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[1].point);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_Doubt[0].point = RectContours[i];
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[i], connorPoint[2].point) + getDistance(RectContours[i], connorPoint[1].point);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_Doubt[1].point = RectContours[i];
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[i], connorPoint[0].point) + getDistance(RectContours[i], connorPoint[2].point);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_Doubt[2].point = RectContours[i];
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[i].index = i;
}
for (i = 0; i < Point_list.size(); i++)
{//找到所有上一级斜比下一级小的角点
if (i>0 && i<Point_list.size() - 1)
{
Kl = abs(((float)(Point_list[i].point.y - Point_list[i - 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[i - 1].point.x)));
Kr = abs(((float)(Point_list[i].point.y - Point_list[i + 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[i + 1].point.x)));
if (Kl<Kr)
{
Connor_Point_Ready.push_back(Point_list[i]);
}
}
else if (i == 0)
{
Kl = abs(((float)(Point_list[i].point.y - Point_list[Point_list.size() - 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[Point_list.size() - 1].point.x)));
Kr = abs(((float)(Point_list[i].point.y - Point_list[i + 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[i + 1].point.x)));
if (Kl<Kr)
{
Connor_Point_Ready.push_back(Point_list[i]);
}
}
else if (i == Point_list.size() - 1)
{
Kl = abs(((float)(Point_list[i].point.y - Point_list[i - 1].point.y)) / ((float)(Point_list[i].point.x - Point_list[i - 1].point.x)));
Kr = abs(((float)(Point_list[i].point.y - Point_list[0].point.y)) / ((float)(Point_list[i].point.x - Point_list[0].point.x)));
if (Kl<Kr)
{
Connor_Point_Ready.push_back(Point_list[i]);
}
}
}
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[i].point.y >= Ymax)
{
Point_First.point = Connor_Point_Ready[i].point;
Point_First.index = Connor_Point_Ready[i].index;
Ymax = Connor_Point_Ready[i].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[i] = Point_list[Point_First.index + i].point;
}
else
{
PointOrder[i] = Point_list[Point_First.index + i - Point_list.size()].point;
}
}
}
return PointOrder;
}
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[i] = arcLength(contours[i], true);
if (length[i] >2000 && length[i] <12000)
{//通过长度匹配滤除小轮廓
convexHull(Mat(contours[i]), hull[i], false);//把凸包找出来
Area_contours[i] = contourArea(contours[i]);
Area_hull[i] = contourArea(hull[i]);
Rectangularity[i] = Area_contours[i] / Area_hull[i];
printf("过滤后轮廓长度为 length=%5f\n", length[i]);
circularity[i] = (4 * 3.1415*Area_contours[i]) / (length[i] * length[i]);
//drawContours(drawing, contours, i, (255, 0, 0), 2);//得到方框
//if (Rectangularity[i]>0.9&&circularity[i]<0.8&&Area_hull[i]>8000 && Area_hull[i]<50000)
{//通过凸包面积滤除不对的凸包,找到最终的四边形
RectContours.push_back(hull[i]);//把提取出来的方框导入到新的轮廓组
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[i], zhongxin_zuobiao[j]);
//if (dis_juxin <65)
{
line(drawing, zhongxin_zuobiao[i], 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[i]), hull1[i], false);//把凸包找出来
RectContours_fainal.push_back(hull1[i]);//把提取出来的方框导入到新的轮廓组
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;
for (i = 0; i < RectContours_fainal.size(); i++)
{
ConnorPoint = FindConnor(RectContours_fainal[i]);
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]代表第i个轮廓的0号角点,顺时针输出
//return ConnorPoint_Output;
if (ConnorPoint_Output[0].size()>0)
{
vector<vector<Point>> RectContours;
vector<Point> pts;
for (int i = 0; i < ConnorPoint_Output[0].size(); i++)
{
Point one_ConnorGroup[4];
for (int j = 0; j < 4; j++)
{//为单独一组角点赋值
one_ConnorGroup[j] = ConnorPoint_Output[j][i];
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;
}
waitKey(0);
return 0;
}
下一篇文章,我们根据寻找到的四个角点,进行透视变换,将侧视图投影成俯视图。
猜你喜欢
- 2024-09-30 OPENCV-python 第一天 python opencv教程
- 2024-09-30 Python帮您十步搞定人脸检测 人脸检测 python
- 2024-09-30 OpenCV学习笔记(一)之图像金字塔-上采样与降采样与DOG
- 2024-09-30 Ubuntu18.04LTS下OpenCV的配置 ubuntu opencv4
- 2024-09-30 计算机视觉之Opencv(1)——基本操作
- 2024-09-30 OpenCV系列教程_03 opencv官方教程
- 2024-09-30 CV之 HOG特征描述算子-行人检测 卜算子 黄州定慧院寓居作
- 2024-09-30 OpenCV SURF特征点检测和匹配 opencv特征提取方法
- 2024-09-30 Opencv从零开始 - [启蒙篇] - 读取、几何变换
- 2024-09-30 密码忘记了?没事,我早就用Python给你监听了
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)