#include "opencv2/opencv.hpp"
#include <iostream>
#include <math.h>
#include <string.h> using namespace cv;
using namespace std; int thresh = 50, N = 11;
const char* wndname = "Square Detection Demo"; int calcdistance(Point_<int> &point1, Point_<int> &point2) { int x1 = point1.x;
int y1 = point1.y;
int x2 = point2.x;
int y2 = point2.y;
int dist = sqrt(pow(x2-x1,2)+pow(y2-x1,2)); return dist;
} // 查找向量之间的角度余弦
// 从pt0->pt1和从pt0->pt2
static double angle( Point pt1, Point pt2, Point pt0 )
{
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
return (dx1*dx2 + dy1*dy2)/sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
//cos<a,b>=(ab的内积)/(|a||b|)
} // 返回图像上检测到的正方形序列
// 序列存储在指定的内存存储器中
static void findSquares( const Mat& image, vector<vector<Point> >& squares )
{
int h = image.rows;
int w = image.cols;
squares.clear(); Mat pyr, timg, gray0(image.size(), CV_8U), gray; // 缩小和放大图像以滤除噪音
pyrDown(image, pyr, Size(image.cols/2, image.rows/2));//高斯降噪,并只取奇数行列缩小图片
pyrUp(pyr, timg, image.size());//插入偶数行列,再次高斯降噪
vector<vector<Point> > contours; // 在图像的每个颜色平面中查找正方形
for( int c = 0; c < 3; c++ )
{
int ch[] = {c, 0};
mixChannels(&timg, 1, &gray0, 1, ch, 1); // 尝试几个阈值级别
for( int l = 0; l < N; l++ )
{
if( l == 0 )
{
Canny(gray0, gray, 0, thresh, 5); dilate(gray, gray, Mat(), Point(-1,-1));
}
else
{
// apply threshold if l!=0:
// tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0
gray = gray0 >= (l+1)*255/N;
} //注意_RETR_LIST参数指明最后只存了角点位置
findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE); vector<Point> approx; // test each contour
//一次取一个轮廓判断下是不是矩形
for( size_t i = 0; i < contours.size(); i++ )
{ //approx存储了近似后的轮廓
approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true); if( approx.size() == 4 && //矩形必须是四个点
fabs(contourArea(Mat(approx))) > 5000 &&
isContourConvex(Mat(approx)) )//必须是凸的,咋理解????
{
double maxCosine = 0; for( int j = 2; j < 5; j++ )
{
// find the maximum cosine of the angle between joint edges
double cosine = fabs(angle(approx[j%4], approx[j-2], approx[j-1]));
maxCosine = MAX(maxCosine, cosine);
}
//依次计算1,2,3顶点的角余弦,最大的余弦值,对应三个角中的最小角,也就是三个角中,最不像直角的
//为什么是三个呢?三个中最不像直角的都接近直角了,剩下的自然也是直角
if( maxCosine < 0.3 )
if (calcdistance(approx[0],approx[3]) < h && calcdistance(approx[0],approx[1]) < w){
squares.push_back(approx);
}
}
}
}
}
} // 函数绘制图像中的所有四边形
static void drawSquares(Mat& image, const vector<vector<Point> >& squares)
{
for( size_t i = 0; i < squares.size(); i++ )
{
const Point* p = &squares[i][0];
int n = (int)squares[i].size();
circle(image,squares[i][0],3,Scalar(0,0,255),1);
circle(image,squares[i][1],3,Scalar(0,0,255),1);
circle(image,squares[i][2],3,Scalar(0,0,255),1);
circle(image,squares[i][3],3,Scalar(0,0,255),1);
// polylines(image, &p, &n, 1, true, Scalar(0,255,0), 1, LINE_AA);
// break;
Rect rr = boundingRect(squares[i]); //通过最小包围盒来获取主要函数区域
vector<vector<Point>> main;
main[0][0] = squares[0][0];
main[0][1] = squares[0][1];
main[0][2] = squares[1][2];
main[0][3] = squares[1][3]; circle(image,main[0][0],1,Scalar(0,255,0),-1);
circle(image,main[0][1],1,Scalar(0,255,0),-1);
imshow("main",image);
RotatedRect minRect = minAreaRect(Mat(main[0]));
Point2f vertex[4];//用于存放最小矩形的四个顶点
minRect.points(vertex);//返回矩形的四个顶点给vertex
//绘制最小面积包围矩形
vector<Point>min_rectangle;
for (int i = 0; i < 4; i++)
{
line(image, vertex[i], vertex[(i + 1) % 4], Scalar(0, 255, 255), 1, 8);//非常巧妙的表达式
min_rectangle.push_back(vertex[i]);//将最小矩形的四个顶点以Point的形式放置在vector容器中
}
// rectangle(image,Point(rr.x, rr.y), Point(rr.x + rr.width, rr.y + rr.height), 1);
// float area = contourArea(squares[i], false);
// cout << "area==" << area << endl;
// break;
} imshow(wndname, image);
} int main(int /*argc*/, char** /*argv*/)
{
Mat image = imread("/home/leoxae/KeekoRobot/TestPic/大班/1.png");
vector<vector<Point>> squares; findSquares(image, squares); for (auto itx = squares.begin(); itx != squares.end(); itx++){
vector<Point> points = *itx;
cout << "Pts=" << points << endl;
}
/* Point b_tl = squares[0][0];
Point b_tr = squares[0][1];
Point b_bl = squares[0][2];
Point b_br = squares[0][3]; Point s_tl = squares[1][0];
Point s_tr = squares[1][1];
Point s_bl = squares[1][2];
Point s_br = squares[1][3];
cout << "b_tl==" << b_tl << endl;
cout << "b_tr==" << b_tr << endl;
cout << "b_bl==" << b_bl << endl;
cout << "b_br==" << b_br << endl; cout << "s_tl==" << s_tl << endl;
cout << "s_tr==" << s_tr << endl;
cout << "s_bl==" << s_bl << endl;
cout << "s_br==" << s_br << endl;
circle(image,b_bl,3,Scalar(0,0,255),-1);
circle(image,b_tr,3,Scalar(0,0,255),-1);
circle(image,b_bl,3,Scalar(0,0,255),-1);
circle(image,b_br,3,Scalar(0,0,255),-1); circle(image,s_tl,3,Scalar(0,255,0),-1);
circle(image,s_tr,3,Scalar(0,255,0),-1);
circle(image,s_bl,3,Scalar(0,255,0),-1);
circle(image,s_br,3,Scalar(0,255,0),-1);*/ // imshow("drawcircle",image);
drawSquares(image, squares); waitKey();
return 0;
}

[opencv]approxDP多边形逼近获取四边形轮廓信息的更多相关文章

  1. [opencv]计算多边形逼近曲线的长度

    //利用曲线逼近,计算逼近曲线的长度 //首先创建一个逼近曲线 vector<Point2f> approx; approxPolyDP(contours[i], approx, 2, t ...

  2. OpenCV探索之路(十一):轮廓查找和多边形包围轮廓

    Canny一类的边缘检测算法可以根据像素之间的差异,检测出轮廓边界的像素,但它没有将轮廓作为一个整体.所以要将轮廓提起出来,就必须将这些边缘像素组装成轮廓. OpenCV中有一个很强大的函数,它可以从 ...

  3. Python 图像处理 OpenCV (15):图像轮廓

    前文传送门: 「Python 图像处理 OpenCV (1):入门」 「Python 图像处理 OpenCV (2):像素处理与 Numpy 操作以及 Matplotlib 显示图像」 「Python ...

  4. OpenCV学习笔记(十一) 轮廓操作

    在图像中寻找轮廓 首先利用Canny算子检测图像的边缘,再利用Canny算子的输出作为 寻找轮廓函数 findContours 的输入.最后用函数 drawContours 画出轮廓.边界Counto ...

  5. opencv —— approxPolyDP 生成逼近曲线

    生成逼近曲线:approxPolyDP 函数 该函数采用 Douglas-Peucker 算法(也称迭代终点拟合算法).可以有效减少多边形曲线上点的数量,生成逼近曲线,简化后继操作. 经典的 Doug ...

  6. 第十七节,OpenCV(学习六)图像轮廓检测

    1.检测轮廓 轮廓检测是图像处理中经常用到的,OpenCV-Python接口中使用cv2.findContours()函数查找检测物体的轮廓. cv2.findContours(image, mode ...

  7. Python+OpenCV图像处理(十六)—— 轮廓发现

    简介:轮廓发现是基于图像边缘提取的基础寻找对象轮廓的方法,所以边缘提取的阈值选定会影响最终轮廓发现结果. 代码如下: import cv2 as cv import numpy as np def c ...

  8. sql 2012中获取表的信息,包含字段的描述

    1.获取数据库中的表 select name from sysobjects where type='U' 2.获取表字段(此处是Route表) Select name from syscolumns ...

  9. ThinPHP命名空间,连接数据库是要修改的配置文件,Model数据模型层,跨控制器调用,如何获取系统常量信息,

    一.命名空间(主要是为了实现自动加载类) *命名空间(相当于虚拟的目录),为了让类有一个统一的文件夹来管理(可以自动加载'类'),每个文件都要有命名空间*tp如何做命名空间:*TP框架下有一个初始命名 ...

随机推荐

  1. Shell【常用知识总结】

    一.常用知识总结 1.特殊变量($0,@,#,*,?) $0:当前脚本的文件名. $n:n是一个数字,表示第几个参数. $#:传递给脚本或函数的参数个数. $*:传递给脚本或函数的所有参数.当被双引号 ...

  2. Hive(七)【内置函数】

    目录 一.系统内置函数 1.查看系统自带内置函数 2.查看函数的具体用法 二.常用内置函数 1.数学函数 round 2.字符函数 split concat concat_ws lower,upper ...

  3. 数据存储SharePreferences详解

    1.SharedPreferences存储 SharedPreferences时使用键值对的方式来存储数据的,也就是在保存一条数据时,需要给这条数据提供一个对应的键,这样在读取的时候就可以通过这个键把 ...

  4. SpringBoot让测试类飞起来的方法

    单元测试是项目开发中必不可少的一环,在 SpringBoot 的项目中,我们用 @SpringBootTest 注解来标注一个测试类,在测试类中注入这个接口的实现类之后对每个方法进行单独测试. 比如下 ...

  5. Java 总纲

    Java基础篇 Java资源下载 IntelliJ IDEA为类和方法自动添加注释 为什么JAVA对象需要实现序列化? maven ubantu安装maven Java Maven项目搭建 maven ...

  6. Linux:while read line与for循环的区别

    while read line:是一次性将文件信息读入并赋值给变量line , while中使用重定向机制,文件中的所有信息都被读入并重定向给了整个while 语句中的line 变量. for:是每次 ...

  7. ABP.VNext-模块

    一.什么是ABP.Vnext? ABP.Vnext是一个基于Asp.Net Core Web应用程序框架.主要目的是用来快速开发Web应用, ABP.Vnext不仅提供完整Web应用程序开发模板,而且 ...

  8. Java如何生成随机数 - Random、ThreadLocalRandom、SecureRandom

    Java7 的Random伪随机数和线程安全的ThreadLocalRandom 一.Random伪随机数: Random 类专门用于生成一个伪随机数,它有两个构造器: 一个构造器使用默认的种子(以当 ...

  9. BUUCFT pwn asis2016_b00ks

    看师傅们wp的时候,我才知道这个道题是wiki上面的例题.我看了一些师傅的wp,发现大家都是一种做法,都是通过mmap堆地址,来找libc基地址的.而我试了一下fastbisn attack,发现也可 ...

  10. 建立资源的方法(Project)

    <Project2016 企业项目管理实践>张会斌 董方好 编著 终于,进入第5章资源计划编制了,所以就不能还在任务工作表里厮混了是吧,那就先进入资源工作表吧:[任务]>[甘特图]& ...