opencv检测矩形

2023-05-16

参考:使用OpenCV检测图像中的矩形_知来者逆的博客-CSDN博客_opencv检测图像中的矩形

1.得到原始图像之后,代码处理的步骤是:
(1)滤波增强边缘。
(2)分离图像通道,并检测边缘。
(3) 提取轮廓。
(4)使用图像轮廓点进行多边形拟合。
(5)计算轮廓面积并得到矩形4个顶点。
(6)求轮廓边缘之间角度的最大余弦。
(7)画出矩形。

一、首先介绍几个本文用到的函数:
1、mixChannels()函数
用途:用于将输入数组的指定通道复制到输出数组的指定通道。

mixChannels()参数说明:

void mixChannels(
const Mat* src, //输入数组或向量矩阵,所有矩阵的大小和深度必须相同。
size_t nsrcs, //矩阵的数量
Mat* dst, //输出数组或矩阵向量,大小和深度必须与src[0]相同
size_t ndsts,//矩阵的数量
const int* fromTo,//指定被复制通道与要复制到的位置组成的索引对
size_t npairs //fromTo中索引对的数目
);
示例:HSV通道获取

在HSV颜色空间中: 
色相 (Hue):代表色彩。取 0 到 360 度的数值来衡量(红-黄-绿-青-蓝-洋红)。 
饱和度 (Saturation):又称色度,指色彩的深浅,饱和度代表灰色与色调的比例,并以 0% (灰色) 到 100% (完全饱和) 来衡量;S = 0时只有灰度。 
色调 (Value):色彩的明亮程度。V=1。它包含RGB模型中的R=1,G=1,B=1 三个面,所代表的颜色较亮。

利用mixChannels()函数通过复制指定通道可以看到HSV颜色空间下的三个通道的具体情况。

#include<opencv2/opencv.hpp>
using namespace cv;
 
int main()
{
    Mat src, hsv, dst;
    src = imread("1.jpg");
    if (src.empty())
    {
        printf("can not load image \n");
        return -1;
    }
    namedWindow("input", WINDOW_AUTOSIZE);  
    imshow("input", src);
    cvtColor(src, hsv, COLOR_BGR2HSV);  
    dst.create(hsv.size(), hsv.depth());
    //分离Hue/色相通道
    int ch[] = {0, 0};
    mixChannels(&hsv, 1, &dst, 1, ch, 1);
    imshow("H channel", dst);
    //分离Saturation/饱和度通道
    int ch1[] = {1, 0};
    mixChannels(&hsv, 1, &dst, 1, ch1, 1);
    imshow("S channel", dst);
    //分离Value/色调通道
    int ch2[] = {2, 0};
    mixChannels(&hsv, 1, &dst, 1, ch2, 1);
    imshow("V channel", dst);
 
    waitKey(0);
    return 0;
}
2、approxPolyDP 多边拟合函数
approxPolyDP 主要功能是把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合。

原理图:对比之前黑点连线,之后蓝色连线:

参数详解;

void approxPolyDP(
InputArray curve,        //一般是由图像的轮廓点组成的点集
OutputArray approxCurve, //表示输出的多边形点集
double epsilon,          //主要表示输出的精度,就是另个轮廓点之间最大距离数,5,6,7,,8,,,,,
bool closed              //表示输出的多边形是否封闭
)
示例: 

#include <opencv2/opencv.hpp>    
#include <stdio.h>    
#include <stdlib.h>    
#include <iostream>  
 
 
using namespace cv;  
using namespace std;  
 

	Mat img = imread("F:/project/jushi/ellipse_detector-sdk/images/04_00010.bmp", -1);

	//Mat img = imread("4.jpg", -1);
	pyrDown(img, img, Size(img.cols / 2, img.rows / 2), 4);
	imshow("img", img); imwrite("img.jpg", img);

	//通过canny算法找轮廓,这样 findcontours 的结果会好些
	Mat canny_out;
	Canny(img, canny_out, 45, 127, 3, false);
	imshow("canny_out", canny_out); imwrite("canny_out.jpg", canny_out);

	//寻找轮廓
	vector<vector<Point>> contours;
	vector<Vec4i> hierachy;
	findContours(canny_out, contours, hierachy, RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(-1, -1));
	drawContours(img, contours, -1, Scalar(0, 0, 255), 1, 8, hierachy);

	//定义圆形、方形、旋转矩形、椭圆的存储容器
	vector<vector<Point>> contours_ploy(contours.size());
	vector<Rect> rects_ploy(contours.size());
	vector<Point2f> circle_centers(contours.size());
	vector<float> circle_radius(contours.size());
	vector<RotatedRect> RotatedRect_ploy;//注意:由于下面赋值的过程中有个点数大于5的条件,所以这里没有直接初始化,才有下面pushback的方法添加值。
	vector<RotatedRect> ellipse_ploy;//注意,这里是画椭圆,但是容器类型是 RotatedRect

	//将结果放到各自的容器中
	for (size_t i = 0; i < contours.size(); i++)
	{
		
		approxPolyDP(contours[i], contours_ploy[i], 5, true);
		rects_ploy[i] = boundingRect(contours_ploy[i]);
		minEnclosingCircle(contours_ploy[i], circle_centers[i], circle_radius[i]);

		if (contours_ploy[i].size() > 5)
		{
			
				RotatedRect temp1 = minAreaRect(contours_ploy[i]);
			RotatedRect_ploy.push_back(temp1);

			RotatedRect temp2 = fitEllipse(contours_ploy[i]);
			ellipse_ploy.push_back(temp2);
		}
	}

	//定义最终绘图的图片
	Mat draw_rect(img.size(), img.type(), Scalar::all(0)),
		draw_rotateRect(img.size(), img.type(), Scalar::all(0)),
		draw_circle(img.size(), img.type(), Scalar::all(0)),
		draw_ellipse(img.size(), img.type(), Scalar::all(0));

	//绘图圆形、矩形
	RNG rng(12345);
	for (size_t i = 0; i < contours.size(); i++)
	{
		
			Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		rectangle(draw_rect, rects_ploy[i], color, 1, 8);
		circle(draw_circle, circle_centers[i], circle_radius[i], color, 1, 8);
	}
	imshow("draw_rect", draw_rect); imwrite("draw_rect.jpg", draw_rect);
	imshow("draw_circle", draw_circle); imwrite("draw_circle.jpg", draw_circle);

	//绘图椭圆形、旋转矩形
	Point2f pot[4];
	for (size_t i = 0; i < ellipse_ploy.size(); i++)
	{
		
			Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
		ellipse(draw_ellipse, ellipse_ploy[i], color, 1, 8);

		RotatedRect_ploy[i].points(pot);
		for (int j = 0; j < 4; j++)
		{
			 line(draw_rotateRect, pot[j], pot[(j + 1) % 4], color);
		}
	}
	imshow("draw_ellipse", draw_ellipse); imwrite("draw_ellipse.jpg", draw_ellipse);
	imshow("draw_rotateRect", draw_rotateRect); imwrite("draw_rotateRect.jpg", draw_rotateRect);


	waitKey();


 

3、 多边形绘制polylines()
参数详解:

void cv::polylines  (   Mat &   img,  //作为画布的矩阵
        const Point *const *    pts,  //折线顶点数组
        const int *     npts,         //折线顶点个数
        int     ncontours,            //待绘制折线数
        bool    isClosed,             //是否是闭合折线(多边形)
        const Scalar &      color,    //折线的颜色
        int     thickness = 1,        //折线粗细
        int     lineType = LINE_8,    //线段类型
        int     shift = 0             //缩放比例(0是不缩放,4是1/4)
    )
二、矩形检测实现源码
本文矩形检测代码来自于(我爱计算机公众号):https://mp.weixin.qq.com/s/WV78mvRn-cYm11cL4l2ScA

分享的开源代码地址:https://github.com/alyssaq/opencv

其算法流程:

1.中值滤波去噪;

2.依次提取不同的颜色通道(BGR)检测矩形;

3.对每一通道使用canny检测边缘或者使用多个阈值二值化;

4.使用findContours函数查找轮廓;

5.使用approxPolyDP函数去除多边形轮廓一些小的波折;

6.找到同时满足面积较大和形状为凸的四边形;

7.判断轮廓中两两邻接直线夹角余弦是否小于0.3(意味着角度在90度附近),是则此四边形为找到的矩形。

该代码效果还是不错的!

 依赖于OpenCV,代码和本文的原版代码基本一致,原版可能更通用:

// The "Square Detector" program.
// It loads several images sequentially and tries to find squares in
// each image
//“矩形检测”程序。
//它按顺序加载了几张图像,并试图找到正方形
 
#include "opencv2/core/core.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
 
#include <iostream>
#include <math.h>
#include <string.h>
 
using namespace cv;
using namespace std;
 

 
int thresh = 50, N = 5;
const char* wndname = "Square Detection Demo";
 
// helper function:
// finds a cosine of angle between vectors
// from pt0->pt1 and from pt0->pt2
//辅助功能:
//求出向量夹角的余弦
//从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);
}
 
// returns sequence of squares detected on the image.
// the sequence is stored in the specified memory storage
//返回在图像上检测到的正方形序列。
//序列存储在指定的内存中
static void findSquares(const Mat& image, vector<vector<Point> >& squares)
{
    squares.clear();
 
    //Mat pyr, timg, gray0(image.size(), CV_8U), gray;
    //down-scale and upscale the image to filter out the noise
    //按比例放大图像,滤除噪声
    //pyrDown(image, pyr, Size(image.cols/2, image.rows/2));
    //pyrUp(pyr, timg, image.size());
 
 
    // blur will enhance edge detection
    //中值滤波将增强边缘检测
    Mat timg(image);
    medianBlur(image, timg, 9);
    Mat gray0(timg.size(), CV_8U), gray;
 
    vector<vector<Point> > contours;
 
    // find squares in every color plane of the image
    //在图像的每个颜色平面上找到正方形
    for (int c = 0; c < 3; c++)
    {
        int ch[] = { c, 0 };
        mixChannels(&timg, 1, &gray0, 1, ch, 1);//将输入数组的指定通道复制到输出数组的指定通道
 
        // try several threshold levels
        //尝试几个阈值级别
        for (int l = 0; l < N; l++)
        {
            // hack: use Canny instead of zero threshold level.
            // Canny helps to catch squares with gradient shading
            // Canny帮助捕捉带有渐变阴影的正方形
            if (l == 0)
            {
                // apply Canny. Take the upper threshold from slider
                // and set the lower to 0 (which forces edges merging)
                Canny(gray0, gray, 5, thresh, 5);
                // dilate canny output to remove potential
                // holes between edge segments
                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;
            }
 
            // find contours and store them all as a list
            findContours(gray, contours, RETR_LIST, CHAIN_APPROX_SIMPLE);
 
            vector<Point> approx;
 
            // test each contour
            for (size_t i = 0; i < contours.size(); i++)
            {
                // approximate contour with accuracy proportional
                // to the contour perimeter
                //近似轮廓与精度成正比
                //到轮廓周长
                approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);
 
                // square contours should have 4 vertices after approximation
                // relatively large area (to filter out noisy contours)
                // and be convex.
                // Note: absolute value of an area is used because
                // area may be positive or negative - in accordance with the
                // contour orientation
                if (approx.size() == 4 &&
                    fabs(contourArea(Mat(approx))) > 1000 &&
                    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);
                    }
 
                    // if cosines of all angles are small
                    // (all angles are ~90 degree) then write quandrange
                    // vertices to resultant sequence
                    if (maxCosine < 0.3)
                        squares.push_back(approx);
                }
            }
        }
    }
}
 
 
// the function draws all the squares in the image
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();
        //dont detect the border
        if (p->x > 3 && p->y > 3)
            polylines(image, &p, &n, 1, true, Scalar(0, 0, 255), 3, LINE_AA);
    }
    imshow(wndname, image);
}
 
 
int main(int /*argc*/, char** /*argv*/)
{
    static const char* names[] = { "./image/2stickies.jpg", "./image/manyStickies.jpg",0 };
    help();
    namedWindow(wndname, 1);
    vector<vector<Point> > squares;
 
    for (int i = 0; names[i] != 0; i++)
    {
        Mat image = imread(names[i], 1);
        if (image.empty())
        {
            cout << "Couldn't load " << names[i] << endl;
            continue;
        }
 
        findSquares(image, squares);
        drawSquares(image, squares);
        imwrite( "out.jpg", image );
        int c = waitKey();
        if ((char)c == 27)
            break;
    }
    return 0;
}

原版矩形检测


1.OpenCV没有内置的矩形检测的函数,如果想检测矩形,要自己去实现。
2.我这里使用的OpenCV版本是3.30.

矩形检测
1.得到原始图像之后,代码处理的步骤是:
(1)滤波增强边缘。
(2)分离图像通道,并检测边缘。
(3) 提取轮廓。
(4)使用图像轮廓点进行多边形拟合。
(5)计算轮廓面积并得到矩形4个顶点。
(6)求轮廓边缘之间角度的最大余弦。
(7)画出矩形。
2.代码

//检测矩形
//第一个参数是传入的原始图像,第二是输出的图像。

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);
}
int main(int argc, char** argv)
{
	//检测矩形
	//第一个参数是传入的原始图像,第二是输出的图像。
	/*void findSquares(const Mat& image, Mat &out)
	{*/
	Mat image = imread("F:/project/jushi/ellipse_detector-sdk/images/04_00010.bmp", IMREAD_COLOR);

		int thresh = 50, N = 5;
		vector<vector<Point> > squares;
		squares.clear();

		Mat src, dst, gray_one, gray,out;

		src = image.clone();
		out = image.clone();
		gray_one = Mat(src.size(), CV_8U);
		//滤波增强边缘检测
		medianBlur(src, dst, 9);
		//bilateralFilter(src, dst, 25, 25 * 2, 35);

		vector<vector<Point> > contours;
		vector<Vec4i> hierarchy;

		//在图像的每个颜色通道中查找矩形
		for (int c = 0; c < image.channels(); c++)
		{
			int ch[] = { c, 0 };

			//通道分离
			mixChannels(&dst, 1, &gray_one, 1, ch, 1);

			// 尝试几个阈值
			for (int l = 0; l < N; l++)
			{
				// 用canny()提取边缘
				if (l == 0)
				{
					//检测边缘
					Canny(gray_one, gray, 5, thresh, 5);
					//膨脹
					dilate(gray, gray, Mat(), Point(-1, -1));
					imshow("dilate", gray);
				}
				else
				{
					gray = gray_one >= (l + 1) * 255 / N;
				}

				// 轮廓查找
				//findContours(gray, contours, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
				findContours(gray, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);

				vector<Point> approx;

				// 检测所找到的轮廓
				for (size_t i = 0; i < contours.size(); i++)
				{
					//使用图像轮廓点进行多边形拟合
					approxPolyDP(Mat(contours[i]), approx, arcLength(Mat(contours[i]), true)*0.02, true);

					//计算轮廓面积后,得到矩形4个顶点
					if (approx.size() == 4 && fabs(contourArea(Mat(approx))) > 1000 && isContourConvex(Mat(approx)))
					{
						double maxCosine = 0;

						for (int j = 2; j < 5; j++)
						{
							// 求轮廓边缘之间角度的最大余弦
							double cosine = fabs(angle(approx[j % 4], approx[j - 2], approx[j - 1]));
							maxCosine = MAX(maxCosine, cosine);
						}

						if (maxCosine < 0.3)
						{
							squares.push_back(approx);
						}
					}
				}
			}
		}


		for (size_t i = 0; i < squares.size(); i++)
		{
			const Point* p = &squares[i][0];

			int n = (int)squares[i].size();
			if (p->x > 3 && p->y > 3)
			{
				polylines(out, &p, &n, 1, true, Scalar(0, 255, 0), 3, LINE_AA);
			}
		}
		imshow("dst", out);
		waitKey(0);



3.运行结果

————————————————
版权声明:本文为CSDN博主「知来者逆」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/matt45m/article/details/95753563

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

opencv检测矩形 的相关文章

  • 如何使用 colorchecker 在 opencv 中进行颜色校准?

    我有数码相机获取的色彩检查器图像 我如何使用它来使用 opencv 校准图像 按照以下颜色检查器图像操作 您是想问如何进行颜色校准或如何使用 OpenCV 进行校准 为了进行颜色校准 您可以使用校准板的最后一行 灰色调 以下是您应该逐步进行
  • cv2.drawContours() - 取消填充字符内的圆圈(Python,OpenCV)

    根据 Silencer的建议 我使用了他发布的代码here https stackoverflow com questions 48244328 copy shape to blank canvas opencv python 482465
  • 如何将 Mat (opencv) 转换为 INDArray (DL4J)?

    我希望任何人都可以帮助我解决这个任务 我正在处理一些图像分类并尝试将 OpenCv 3 2 0 和 DL4J 结合起来 我知道DL4J也包含Opencv 但我认为它没什么用 谁能帮我 如何转换成 INDArray 我尝试阅读一些问题here
  • 如何在 cv2.VideoWriter 中使用 FPS 参数?

    好的 所以我正在制作视频 我想确切地知道如何使用 FPS 参数 它是一个浮点数 所以我假设这是我想要的每帧之间的间隔 你能给个例子吗 我只想知道视频会如何随着 FPS 参数值的变化而变化 因为我制作的视频现在太快了 谢谢 确实只是这样 fr
  • “没有名为‘cv2’的模块”,但已安装

    我已经安装了包含 opencv 贡献的 whl 文件 因为我想使用 SIFT 算法 我在 conda 环境中使用 pip 安装了它 所以当我在 conda list 中提示时 它会向我显示 opencv python 3 4 5 contr
  • BRISK 特征检测器检测零个关键点

    下面显示的 Brisk 探测器没有给我任何关键点 有人可以提出一个问题吗 我将尝试用一些代码解释我在下面所做的事情 include opencv2 features2d features2d hpp using namespace cv u
  • opencv水印周围的轮廓

    我想在图像中的水印周围画一个框 我已经提取了水印并找到了轮廓 但是 不会在水印周围绘制轮廓 轮廓是在我的整个图像上绘制的 请帮我提供正确的代码 轮廓坐标的输出为 array 0 0 0 634 450 634 450 0 dtype int
  • 从扫描文档中提取行表 opencv python

    我想从扫描的表中提取信息并将其存储为 csv 现在我的表提取算法执行以下步骤 应用倾斜校正 应用高斯滤波器进行去噪 使用 Otsu 阈值进行二值化 进行形态学开局 Canny 边缘检测 进行霍夫变换以获得表格行 去除重复行 10像素范围内相
  • 使用卡尔曼滤波器跟踪位置和速度

    我正在使用卡尔曼滤波器 恒定速度模型 来跟踪物体的位置和速度 我测量对象的 x y 并跟踪 x y vx vy 这是有效的 但是如果在传感器读数 x y vx vy 上添加 20 mm 的高斯噪声 即使该点没有移动 只是噪声也会发生波动 对
  • 如何检测图像是否像素化

    之前有人在 SO 上提出过这样的问题 在Python中检测像素化图像 https stackoverflow com questions 12942365 detecting a pixelated image in python还有关于q
  • 使用python从gst管道抓取帧到opencv

    我在用着OpenCV http opencv org 和GStreamer0 10 我使用此管道通过自定义套接字通过 UDP 接收 MPEG ts 数据包sockfd由 python 提供并显示它xvimagesink 而且效果很好 以下命
  • 提取二值图像中的最中心区域

    我正在处理二进制图像 之前使用此代码来查找二进制图像中的最大区域 Use the hue value to convert to binary thresh 20 thresh thresh img cv2 threshold h thre
  • 旋转矩阵openCV

    我想知道如何找到框架中一组特征的旋转矩阵 我会更具体 我有 2 个具有 20 个特征的帧 假设第 1 帧和第 2 帧 我可以估计两个帧中特征的位置 例如 假设位置 x y 处的某个第 1 帧特征 并且我确切地知道它在哪里 所以假设为 x y
  • OpenCV Python 和 SIFT 功能

    我知道有很多关于Python and OpenCV但我没有找到有关这个特殊主题的帮助 我想提取SIFT关键点来自 python OpenCV 中的图像 我最近安装了 OpenCV 2 3 可以访问 SURF 和 MSER 但不能访问 SIF
  • 将图像加载到现有 Mat 中

    有没有办法将图像加载到现有的 Mat 中 如果没有 有没有办法控制 OpenCV 在调用 cv imread 时分配内存的位置 我只是为您的类创建一个构造函数 该构造函数接受 imread 的输入参数并将图像直接加载 并分配 到您的类中 所
  • 如何选择图像插值方法? (Emgu/OpenCV)

    Emgu OpenCV的 net包装器 提供的图像调整大小功能可以使用四种插值方法中的任意一种 http www emgu com wiki files 1 4 0 0 html 596dd03d 301e d3c6 4c53 c42855
  • 静态 OpenCV 库中未定义的引用

    我有一个使用 OpenCV 3 1 的 C 项目 并且使用共享库可以正常工作 但现在我想使用静态库 位于项目目录中的文件夹中 来编译它 因为我希望能够在未安装 OpenCV 的情况下导出它 如果需要还可以编辑和重新编译 这次我重新编译了 O
  • Python 2.7/OpenCV 3.3: cv2.initUn DistorifyMap 中出现错误。不显示不扭曲的校正图像

    我想扭曲并校正我的立体图像 为此 我在 Python 2 7 中使用了 Opencv 3 3 我使用的代码是 import cv2 import numpy as np cameraMatrixL np load mtx left npy
  • opencv不失真图像有一个奇怪的圆圈

    我尝试使用 opencv 针孔模型来计算校准参数 然后使图像不失真 问题是 未失真的图像中有一个奇怪的圆圈 如下所示 代码 原始图像和结果图像是here https github com wennycooper A004 pinhole 任
  • HTC One M8 - 使用第二个后置摄像头

    我有一台 HTC One M8 设备 它有 2 个后置摄像头和一个额外的前置摄像头 我的问题是尝试访问第二个后置摄像头 我已经成功制作了一个应用程序 它同时运行 2 个摄像头 1 个前置摄像头和 1 个后置摄像头 但问题是我无法访问第二个后

随机推荐