#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. 零基础学习java------23---------动态代理,ip,url案例

    1. 动态代理 2. ip,url案例 给定的access.log是电信运营商的用户上网数据,第一个字段是时间, 第二个字段是ip地址,第三个字段是访问的网站,其他字段可以忽略不计. 第一个字段是网段 ...

  2. nodejs-Cluster模块

    JavaScript 标准参考教程(alpha) 草稿二:Node.js Cluster模块 GitHub TOP Cluster模块 来自<JavaScript 标准参考教程(alpha)&g ...

  3. json模块中函数的用法

    json模块中主要使用四个函数:json.load(),json.dump(),json.loads(),json.dumps() json.loads()是将一个json编码的字符串转换成pytho ...

  4. 【leetocde】922. Sort Array By Parity II

    Given an array of integers nums, half of the integers in nums are odd, and the other half are even.  ...

  5. 【Git项目管理】git新手入门——基础教程

    一.Git工作流程 直接上手看图,了解Git工具的工作流程: 以上包括一些简单而常用的命令,但是先不关心这些,先来了解下面这4个专有名词. Workspace:工作区 Index / Stage:暂存 ...

  6. java中super的几种用法,与this的区别

    1. 子类的构造函数如果要引用super的话,必须把super放在函数的首位. class Base { Base() { System.out.println("Base"); ...

  7. Shell脚本实现根据文件的修改时间来分类文件

    #!/bin/bash # exctute # ./mod.sh file_type input_folder output_folder # ./mod.sh *.txt /tmp /data/ # ...

  8. 使用IntelliJ IDEA创建简单的Spring Boot项目

    方法一: File - New -Project 创建结束后进行测试运行,修改代码如下: package com.springboot.testone; import org.springframew ...

  9. 通过js禁用浏览器的回退事件

    js代码: <script> history.pushState(null, null, document.URL); window.addEventListener('popstate' ...

  10. [BUUCTF]PWN10——[第五空间2019 决赛]PWN5

    [第五空间2019 决赛]PWN5 题目网址:https://buuoj.cn/challenges#[第五空间2019%20决赛]PWN5 步骤: 例行检查,32位,开启了nx和canary(栈保护 ...