如何对矩形表面进行角点检测

利用OPENCV对矩形表面进行角点检测

简单介绍一下思路,标记一个很像矩形的表面,首先得对图像或视频(以下只说图像,其实视频一样道理)进行预处理,尽可能消除噪声、不感兴趣部分的干扰,比如说我这个示例的图像中有几处灯光,但是我只想提取黄色两条小灯以及其连成的矩形。

示例目标大概样子

思路+代码分析

以下是一些头文件,有些可能用不上,这里用了ros在下一遍文章中将会进一步讲到如何用rviz显示提取的部分仿真内容,这里可以不包括,然后iostream是用来输出变量值来检查修正代码的,也用不上了。其他是一些Opencv的头文件。

#include <ros/ros.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/highgui.hpp>
#include <opencv2/core/core.hpp>

然后声明一下cv和std命名空间,

using namespace std;
using namespace cv;

图像预处理请具体情况具体分析!示例首先通过转换成色调、饱和度、明度空间,然后通过灯光黄色提出黄色,然后二值化处理,最后是闭操作联通区域,消除了旁边的小光点,然后膨胀(可调整腐蚀核)。

        cvtColor(frame, hsv, COLOR_BGR2HSV);
        inRange(hsv, Scalar(0, 43, 46), Scalar(26, 255, 255), mask);
        threshold(hsv, hsv, 70, 230, CV_THRESH_BINARY);
        //闭操作联通+膨胀
        dilate(mask, mask, Mat(), Point(-1, -1), 14);
        erode(mask, mask, Mat(), Point(-1, -1), 19);
        dilate(mask, mask, Mat(), Point(-1, -1), 3);

提取边缘,用到了findContours函数,提取方法是最大外围提取,然后保留图像终点部分。

vector<vector<Point> > cont;
findContours(mask, cont, CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);

用最小旋转矩形包围提取出来的每个轮廓(可能有好几个轮廓区,比如天花灯管),这个矩形可以不和屏幕垂直或平行,它与矩形物体长或宽垂直。

rect[i] = minAreaRect(cont[i]);

这个是画在原图上看的,可以用来检测算法是否合适(imshow('frame',frame)可见)。

line(frame, vertices[j], vertices[(j + 1) % 4], Scalar(255, 255, 0), 0.03);

舍去干扰点(上面有灯管),并且画出四个角点:

if (vertices[j].x > 0 && abs(vertices[j].y - vertices[(j + 1) % 4].y) < 15)
{
dot = Point2f((vertices[j].x + vertices[(j + 1) % 4].x) / 2, (vertices[j].y + vertices[(j + 1) % 4].y) / 2);
 savedots.push_back(dot);
// cout << dot << endl;
circle(draw, dot, 1, Scalar(255, 0, 255), 2, 8, 0);
}

最后imshow一下,按q退出。

imshow("frame", frame);
imshow("draw", draw);

效果图:

完整代码


#include <ros/ros.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <opencv2/highgui.hpp>
#include <opencv2/core/core.hpp>

using namespace std;
using namespace cv;

int main(int argc, char **argv)
{

    
    VideoCapture capture("/home/dale/cvrviz/water.avi");
    if (!capture.isOpened())
    {
        //error in opening the video input
        printf("could not load image..\n");
        return false;
    }
    Mat frame, hsv, mask, cornerpoint;
    while (ros::ok())
    {
        capture >> frame;
        if (frame.empty())
            break;

        cvtColor(frame, hsv, COLOR_BGR2HSV);
        inRange(hsv, Scalar(0, 43, 46), Scalar(26, 255, 255), mask);
        threshold(hsv, hsv, 70, 230, CV_THRESH_BINARY);
        //闭操作联通+膨胀
        dilate(mask, mask, Mat(), Point(-1, -1), 14);
        erode(mask, mask, Mat(), Point(-1, -1), 19);
        dilate(mask, mask, Mat(), Point(-1, -1), 3);

        vector<vector<Point> > cont;
        findContours(mask, cont, CV_RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
        Mat draw = Mat::zeros(mask.size(), CV_8UC3);
        // imshow("mask", mask);
        vector<RotatedRect> rect(cont.size());
        Mat savepoint[4];

        Point2f dot;
        vector<Point2f> savedots;

        if (cont.size() > 1)
        {
            for (int i = 0; i < cont.size(); i++)
            {
                //找出最小旋转矩形
                rect[i] = minAreaRect(cont[i]);
                //定义矩形的4个顶点
                Point2f vertices[4];
                // 排除上面灯管干扰,计算矩形的4个顶点
                if (rect[i].center.y > 80)
                {

                    rect[i].points(vertices);
                }
                for (int j = 0; j < 4; j++)
                {
                    line(frame, vertices[j], vertices[(j + 1) % 4], Scalar(255, 255, 0), 0.03);
                    //提取corner points
                    if (vertices[j].x > 0 && abs(vertices[j].y - vertices[(j + 1) % 4].y) < 15)
                    {

                        dot = Point2f((vertices[j].x + vertices[(j + 1) % 4].x) / 2, (vertices[j].y + vertices[(j + 1) % 4].y) / 2);
                        savedots.push_back(dot);
                        // cout << dot << endl;
                        circle(draw, dot, 1, Scalar(255, 0, 255), 2, 8, 0);
                    }
                }
            }
            imshow("frame", frame);
            imshow("draw", draw);
            //多甲板的话,可以对一个矩形临近点进行距离限定
        }
        int keyboard = waitKey(10);
        if (keyboard == 'q' || keyboard == 27)
            break;
    }

    capture.release();
    return (0);
}


 

posted @ 2019-10-26 20:47  狂小虎  阅读(73)  评论(0编辑  收藏  举报