/*
工程在69那个例程文件目录
检测四边形轮廓以及角点
*/
/*晚上调好模板匹配以及透视变换,再试试OCR库
*/
#pragma warning(disable:4996)
//
#include <opencv.hpp>
#include <opencv2/video.hpp>
#include <opencv2/imgPRoc//imgproc.hpp>
#include <opencv2/ml/ml.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include<time.h>
#include<math.h>
#include <set>
using namespace cv;
using namespace std;
//-----------------------------------【命名空间声明部分】--------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace cv;
using namespace std;
RNG rng(12345);
float getDistance(CvPoint pointO, CvPoint pointA);
float getAngle(CvPoint pointM, CvPoint pointL, CvPoint pointR);
float getDist_P2L(CvPoint pointP, CvPoint pointA, CvPoint pointB);
int list_connor(int i1, int i2, int i3);
//-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数的声明
//-----------------------------------------------------------------------------------------------
static void ShowHelpText();
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main()
{
int iiii = 1000;
int n = 1;//保存图片名称末尾
int geshu = 0;
Mat frame;
Mat srcImage = Mat::zeros(600, 800, CV_8UC3);
//【0】显示欢迎和帮助文字
ShowHelpText();
char strangerName[2000];//截取的四边形矩形图片名字
//定义一些参数3
Mat threshold_output;
vector<vector<Point>> contours;
vector<Vec4i> hierarchy;
//【1】从摄像头读入视频
VideoCapture capture(1); //该参数为0,则打开计算机自带摄像头,如果为1则打开外置USB摄像头
//capture.set(CAP_PROP_FRAME_WIDTH, 1920.0);//设置摄像头采集图像分辨率
// capture.set(CAP_PROP_FRAME_HEIGHT, 1080.0);
while (1)
{
iiii++;
//隔一段时间保存一张图片
if (iiii >= 500)//通过改变i后面的值来刷新图片界面
{
clock_t start = clock();
iiii = 0;
capture >> frame;// //读取当前帧,capture >> frame与capture.read(frame)功能一样,
if (frame.empty())
{
return 0;
}
char* cstr = new char[120];
//Mat srcImage0 = imread("10.jpg", 0);
resize(frame, srcImage, srcImage.size());
// srcImage = srcImage > 200;//二值化
//灰度化
cvtColor(srcImage, srcImage, CV_BGR2GRAY);//灰度化
//二值化
threshold(srcImage, srcImage, 200, 255, CV_THRESH_BINARY | CV_THRESH_OTSU);
namedWindow("原图", 1);
imshow("原图", srcImage);
waitKey(30);
//getStructuringElement函数会返回指定形状和尺寸的结构元素
Mat element = getStructuringElement(MORPH_RECT, Size(3, 3));
//morphologyEx(srcImage, srcImage, MORPH_CLOSE, element);//闭运算滤波
vector<vector<Point>> contours, RectContours;//轮廓,为点向量,
findContours(srcImage, contours, CV_RETR_LIST, CV_CHAIN_APPROX_NONE);//找轮廓
vector<vector<Point>> hull(contours.size());//用于存放凸包
Mat drawing(srcImage.size(), CV_8UC3, cv::Scalar(0));
int i = 0;
vector<float> length(contours.size());//用于保存每个轮廓的长度
vector<float> Area_contours(contours.size()), Area_hull(contours.size()), Rectangularity(contours.size()), circularity(contours.size());
for (i = 0; i < contours.size(); i++)
{//把所有的轮廓画出来
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
length[i] = arcLength(contours[i], true);//轮廓的长度
if (length[i] >200 && length[i] <2000)
{//通过长度匹配滤除小轮廓
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]; //矩形度
circularity[i] = (4 * 3.1415*Area_contours[i]) / (length[i] * length[i]);//圆形度
//drawContours(drawing, contours, i, color, 1);//得到方框
if (Rectangularity[i]>0.8&&circularity[i]<0.95)
{//通过矩形度和圆形度滤除数字
//drawContours(drawing, contours, i, Scalar(255, 255, 255), 1);
RectContours.push_back(hull[i]);//把提取出来的方框导入到新的轮廓组
drawContours(drawing, hull, i, color, 1);//得到四边形
}
}
}
/********************** 自己添加的用于切割四边形 **************************************************************************************************************/
/********************** 自己添加的 **************************************************************************************************************/
// 多边形逼近轮廓 + 获取矩形和圆形边界框
vector<vector<Point> > contours_poly(RectContours.size());
vector<Rect> boundRect(RectContours.size()); //最小矩形
vector<Point2f>center(RectContours.size()); //圆心
vector<float>radius(RectContours.size()); //半径
//一个循环,遍历所有部分,进行本程序最核心的操作
for (unsigned int i = 0; i < RectContours.size(); i++)
{
approxPolyDP(RectContours[i], contours_poly[i], 3, true);//用指定精度逼近多边形曲线
boundRect[i] = boundingRect(Mat(contours_poly[i]));//计算点集的最外面(up-right)矩形边界
minEnclosingCircle(contours_poly[i], center[i], radius[i]);//对给定的 2D点集,寻找最小面积的包围圆形
}
// 绘制多边形轮廓 + 包围的矩形框 + 圆形框
Mat drawing1 = Mat::zeros(srcImage.size(), CV_8UC3);
Mat image_cut[100]; //从img中按照rect进行切割,此时修改image_cut时image中对应部分也会修改,因此需要copy
Mat image_copy[100]; //clone函数创建新的图片
int geshu = 0;
for (int unsigned i = 0; i < RectContours.size(); i++)
{
Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));//随机设置颜色
//drawContours(drawing1, contours_poly, i, color, 1, 8, vector<Vec4i>(), 0, Point());//绘制轮廓
Point text_lb;//中点
//circle(drawing, center[i], (int)radius[i], color, 2, 8, 0);//绘制圆
//if (radius[i]<39 && radius[i]>20 && center[i].y>300 && center[i].x>200 && center[i].x<400)
//if (radius[i]<39 && radius[i]>20 && center[i].y>300 && center[i].x>350 && center[i].x<400)
//中间下面的点
// if (radius[i]<39 && radius[i]>20 && center[i].y > 200 && center[i].x > 100 && center[i].x < 650)
//{
geshu++;
rectangle(drawing1, boundRect[i].tl(), boundRect[i].br(), color, 2, 8, 0);//绘制矩形
text_lb = Point((boundRect[i].tl().x + boundRect[i].br().x)*0.5, (boundRect[i].tl().y + boundRect[i].br().y)*0.5);
//putTextZH(drawing, "中点", center[i], Scalar(0, 0, 255), 10, "Arial");
int width = abs(boundRect[i].tl().x - boundRect[i].br().x);
int height = abs(boundRect[i].tl().y - boundRect[i].br().y);
Rect rect(boundRect[i].tl().x, boundRect[i].tl().y, width, height); //创建一个Rect框,属于cv中的类,四个参数代表x,y,width,height
image_cut[i] = Mat(srcImage, rect); //从img中按照rect进行切割,此时修改image_cut时image中对应部分也会修改,因此需要copy
image_copy[i] = image_cut[i].clone(); //clone函数创建新的图片
sprintf_s(strangerName, "数字%d", geshu);
namedWindow(strangerName, 0);//参数为零,则可以自由拖动strangerName
imshow(strangerName, image_copy[i]);
//}
}
namedWindow("找到四边形外接矩形", 1);
imshow("找到四边形外接矩形", drawing1);
waitKey(30);
///****************************************************************************************************************************************/
///****************************************************************************************************************************************/
float distance = 0, distanceMax = 0;
Point connorPoint1, connorPoint2, connorPoint3, connorPoint4, point_add;
vector<Point> connor4_add(3); //先找到的三个角点
int conP_i1, conP_i2, conP_i3, conP_i_add;
int j = 0, flag = 0;
Point finally_contours[80][4];//轮廓,为点向量,新的轮廓
for (j = 0; j < RectContours.size(); j++) //四边形轮廓个数
{
distance = 0;
distanceMax = 0;
for (i = 0; i < RectContours[j].size(); i++) //每个轮廓点的个数11到19点不等
{//找第一个角点
distance = getDistance(RectContours[j][i], RectContours[j][0]);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint1 = RectContours[j][i]; //第一个角点
conP_i1 = i;
}
}
distance = 0;
distanceMax = 0;
for (i = 0; i < RectContours[j].size(); i++)
{//找第二个角点
distance = getDistance(RectContours[j][i], connorPoint1);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint2 = RectContours[j][i]; //第二个角点
conP_i2 = i;
}
}
distance = 0;
distanceMax = 0;
for (i = 0; i < RectContours[j].size(); i++)
{//找第三个角点
distance = getDistance(RectContours[j][i], connorPoint1) + getDistance(RectContours[j][i], connorPoint2);
if (distance>distanceMax)
{
distanceMax = distance;
connorPoint3 = RectContours[j][i]; //第三个角点
conP_i3 = i;
}
}
flag = list_connor(conP_i1, conP_i2, conP_i3);//对三个角点由大到小排序
switch (flag)
{//对三个角点排序
case 0:break;
case 123:break;
case 132:point_add = connorPoint2; connorPoint2 = connorPoint3; connorPoint3 = point_add; break;//2,3交换
case 213:point_add = connorPoint1; connorPoint1 = connorPoint2; connorPoint2 = point_add; break;//1,2交换
case 231:point_add = connorPoint1; connorPoint1 = connorPoint2; connorPoint2 = point_add;
point_add = connorPoint2; connorPoint2 = connorPoint3; connorPoint3 = point_add; break;//1,2交换+2,3交换
case 321:point_add = connorPoint3; connorPoint3 = connorPoint1; connorPoint1 = point_add; break;//1,3交换
case 312:point_add = connorPoint3; connorPoint3 = connorPoint1; connorPoint1 = point_add;
point_add = connorPoint2; connorPoint2 = connorPoint3; connorPoint3 = point_add; break;//1,3交换+2,3交换
}
switch (flag)
{//对三个角点排序
case 0:break;
case 123:break;
case 132:conP_i_add = conP_i2; conP_i2 = conP_i3; conP_i3 = conP_i_add; break;//2,3交换
case 213:conP_i_add = conP_i1; conP_i1 = conP_i2; conP_i2 = conP_i_add; break;//1,2交换
case 231:conP_i_add = conP_i1; conP_i1 = conP_i2; conP_i2 = conP_i_add;
conP_i_add = conP_i2; conP_i2 = conP_i3; conP_i3 = conP_i_add; break;//1,2交换+2,3交换
case 321:conP_i_add = conP_i3; conP_i3 = conP_i1; conP_i1 = conP_i_add; break;//1,3交换
case 312:conP_i_add = conP_i3; conP_i3 = conP_i1; conP_i1 = conP_i_add;
conP_i_add = conP_i2; conP_i2 = conP_i3; conP_i3 = conP_i_add; break;//1,3交换+2,3交换
}
distance = 0;
distanceMax = 0;
for (i = conP_i3; i < conP_i2; i++)
{//相隔两角点之间找到怀疑是4角点的点
distance = getDistance(RectContours[j][i], connorPoint3) + getDistance(RectContours[j][i], connorPoint2);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_add[0] = RectContours[j][i];
}
}
distance = 0;
distanceMax = 0;
for (i = conP_i2; i < conP_i1; i++)
{//相隔两角点之间找到怀疑是4角点的点
distance = getDistance(RectContours[j][i], connorPoint1) + getDistance(RectContours[j][i], connorPoint2);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_add[1] = RectContours[j][i];
}
}
distance = 0;
distanceMax = 0;
for (i = conP_i1; i < RectContours[j].size() + conP_i3; i++)
{//相隔两角点之间找到怀疑是4角点的点
if (i< RectContours[j].size())
{
distance = getDistance(RectContours[j][i], connorPoint1) + getDistance(RectContours[j][i], connorPoint3);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_add[2] = RectContours[j][i];
}
}
else
{
distance = getDistance(RectContours[j][i - RectContours[j].size()], connorPoint1) + getDistance(RectContours[j][i - RectContours[j].size()], connorPoint3);
if (distance>distanceMax)
{
distanceMax = distance;
connor4_add[2] = RectContours[j][i - RectContours[j].size()];
}
}
}
if (getDist_P2L(connor4_add[0], connorPoint3, connorPoint2)>10)
{
connorPoint4 = connor4_add[0];
}
else if (getDist_P2L(connor4_add[1], connorPoint2, connorPoint1)>10)
{
connorPoint4 = connor4_add[1];
}
else if (getDist_P2L(connor4_add[2], connorPoint1, connorPoint3)>10)
{
connorPoint4 = connor4_add[2];
}
circle(drawing, connorPoint1, 3, Scalar(255, 255, 255), FILLED, LINE_AA);
circle(drawing, connorPoint2, 3, Scalar(255, 255, 255), FILLED, LINE_AA);
circle(drawing, connorPoint3, 3, Scalar(255, 255, 255), FILLED, LINE_AA);
circle(drawing, connorPoint4, 3, Scalar(255, 255, 255), FILLED, LINE_AA);
finally_contours[j][0] = connorPoint1;
finally_contours[j][1] = connorPoint2;
finally_contours[j][2] = connorPoint3;
finally_contours[j][3] = connorPoint4;
cout << "\n轮廓 " << j + 1 << " 的四个角点坐标分别为:\n" << finally_contours[j][0] << finally_contours[j][1] << finally_contours[j][2] << finally_contours[j][3] << endl;
}
namedWindow("轮廓", 1);
imshow("轮廓", drawing);
clock_t ends = clock();
cout << "Running Time : " << (double)(ends - start) / CLOCKS_PER_SEC << endl;
}
}
return 0;
}
float getDist_P2L(CvPoint pointP, CvPoint pointA, CvPoint pointB)
{//点到直线的距离:P到AB的距离
//求直线方程
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;
}
//对角点进行排序,因为之前检测出的轮廓是带序号的
int list_connor(int i1, int i2, int i3)
{//排序
int flag = 0;
Point point_add;
if (i1 >= i2&&i2 >= i3)
flag = 123;
else if (i1 >= i3&& i3 >= i2)
flag = 132;
else if (i2 >= i1&&i1 >= i3)
flag = 213;
else if (i2 >= i3&&i3 >= i1)
flag = 231;
else if (i3 >= i2&&i2 >= i1)
flag = 321;
else if (i3 >= i1&&i1 >= i2)
flag = 312;
return flag;
}
float getDistance(CvPoint pointO, CvPoint pointA)
{//求两点之间距离
float distance;
distance = powf((pointO.x - pointA.x), 2) + powf((pointO.y - pointA.y), 2);
distance = sqrtf(distance);
return distance;
}
float getAngle(CvPoint pointM, CvPoint pointL, CvPoint pointR)
{//求三点之间的夹角
CvPoint L, R;
float dist_L, dist_R, Theta;
L.x = pointL.x - pointM.x;
L.y = pointL.y - pointM.y;
R.x = pointR.x - pointM.x;
R.y = pointR.y - pointM.y;
dist_L = getDistance(pointL, pointM);
dist_R = getDistance(pointR, pointM);
Theta = acos((L.x*R.x + L.y*R.y) / (dist_L*dist_R));
return Theta;
}
//-----------------------------------【ShowHelpText( )函数】----------------------------------
// 描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
static void ShowHelpText()
{
//输出欢迎信息和OpenCV版本
printf("\n\n\t\t\t检测四边形角点\n");
printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION);
printf("\n\n ----------------------------------------------------------------------------\n");
//输出一些帮助信息
printf("\n\n\n\t毛哥好\n\n");
printf("\n\n\t按键操作说明: \n\n"
"\t\t键盘按键【ESC】- 退出程序\n\n"
"\t\t毛哥2018/5/24\n\n");
}
程序备份——摄像头实时采集图像并寻找四边形角点以及切割出四边形最小外接矩形
最新推荐文章于 2023-07-07 12:31:22 发布