RM识别风车

个人介绍

RM视觉组新手,现在写的这个代码暂时不考虑运行效率。

环境配置

ubuntu18.04 + qt5.11 + opencv-4.2.0

思路

  • 由于颜色差异较大,考虑到hsv比rgb在颜色识别上具有更加准确的特点,转到hsv色彩空间进行分离
  • 利用膨胀将流水灯以及轮廓连接起来,利用继承关系轻松识别
  • 做一个定位圆(直接circle可以很方便地画出,用拟合圆精确度会更高,如果将拟合效果通过gsl库中的函数处理,效果更加精确,考虑到gsl暂时没有掌握,下面直接用拟合圆)

启程前的准备

  • 下载gpick,提前取色,保证h,s,v值能在准确范围内,在阈值操作处理中不必估计(对于这个颜色区别较大的情况,直接估计范围也能达到相同的效果)
sudo apt-get install gpick

贴一张取色之后的图
在这里插入图片描述

正式开始

目标:识别出目标装甲并且定位

  1. 载入视频
VideoCapture cap;
    cap.open("/home/husonghui/C_plus_practice/march_partice/detect_energe/red-black.avi");
    if(!cap.isOpened()){
        cout << "fail to load!" << endl;
        return -1;
    }
    Mat frame;
    while(1){
		cap >> frame;
		if(frame.empty())
			break;
		...
		...
	}
  1. 由于hsv取值范围的问题,{[h∈[0,360°],s∈[0,1],v∈[0,1] },进行归一化处理,转入hsv空间
        Mat hsv = frame.clone();
        frame.convertTo(frame,CV_32FC3,1.0/255,0);
        cvtColor(frame,hsv,COLOR_BGR2HSV);
  1. gpick提色:对外面的灯条以及被击打后的流水灯进行提色,可以发现流水灯其实可以被分离出去(这里不分出去了,轮廓继承关系要用到)
h(色相)s(饱和度)v(亮度值)
外灯条68496
68696
58793
49387
59280
49187
被击打后的流水灯4357100
3957100
4358100
3960100
4060100
4254100

根据数据,再转换一下,得到范围内的最大最小,并进行二值化处理

        double low_H = 3;
        double low_S = 0.55;
        double low_V = 0.9;
        double high_H = 40;
        double high_S = 0.93;
        double high_V = 1;
        Mat hsv = frame.clone();
        inRange(hsv,Scalar(low_H,low_S,low_V),Scalar(high_H,high_S,high_V),frame_threshold);

看一下效果,打击前后的边缘都存在断线。
在这里插入图片描述
在这里插入图片描述
4. 目标装甲轮廓识别及定位:

  • 轮廓识别:进行形态学处理,膨胀一次,闭操作迭代4次 ,防止断线
        Mat kernel = getStructuringElement(cv::MORPH_RECT,Size(3,3));
        dilate(frame_threshold,frame_threshold,kernel,Point(-1,-1),2);
        morphologyEx(frame_threshold,frame_threshold,MORPH_CLOSE,kernel,Point(-1,-1),4);

处理之后,轮廓继承关系就很明显了(存在父轮廓,无同级轮廓和子轮廓的就是目标装甲的轮廓)
在这里插入图片描述

        vector<vector<Point>> contours;
        vector<Vec4i> hierarchy;
        Scalar color(0,255,0);
        
        Point2f point_dete_center;      //保存 RotatedRect类 返回的中心点坐标
        findContours(frame_threshold,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE);
        for(int i = 0; i != contours.size(); ++i){
            if(hierarchy[i][3] != -1 && hierarchy[i][2] == -1 && hierarchy[i][1] == -1 && hierarchy[i][0] == -1){
                drawContours(frame,contours,i,color,4,8);
  • 通过外接矩形定位装甲位置
                RotatedRect rect_tmp = minAreaRect(contours[i]);
                point_dete_center = rect_tmp.center;
  1. 拟合圆,直接用了下面这篇文章,通过增加if()画圆,点集用装甲外接矩形的中点
    这里面需要用到一个拟合圆函数
			if(LeastSquaresCircleFitting(vec_collect,centroid,radius)){
                    circle(frame,centroid,radius,Scalar(150,150,150),2,8);
                }

再通过目标装甲的位置和圆心加上一个半径

 line(frame,p_1,point_dete_center,Scalar(255,0,0),4);

效果图
在这里插入图片描述

函数代码

int main()
{
    VideoCapture cap;
    cap.open("/home/husonghui/C_plus_practice/march_partice/detect_energe/red-black.avi");
    if(!cap.isOpened()){
        cout << "fail to load!" << endl;
        return -1;
    }

    Mat frame;
    vector<Point2d> vec_collect;
    double radius;
    Point2d centroid;

    while(1){
        cap >> frame;
        if(frame.empty())
            break;
        Mat hsv = frame.clone();
        Mat frame_threshold = frame.clone();

        frame.convertTo(frame,CV_32FC3,1.0/255,0);
        cvtColor(frame,hsv,COLOR_BGR2HSV);

        double low_H = 3;
        double low_S = 0.55;
        double low_V = 0.9;
        double high_H = 40;
        double high_S = 0.93;
        double high_V = 1;
        inRange(hsv,Scalar(low_H,low_S,low_V),Scalar(high_H,high_S,high_V),frame_threshold);

        Mat kernel = getStructuringElement(cv::MORPH_RECT,Size(3,3));
        dilate(frame_threshold,frame_threshold,kernel,Point(-1,-1),2);
        morphologyEx(frame_threshold,frame_threshold,MORPH_CLOSE,kernel,Point(-1,-1),4);

        vector<vector<Point>> contours;
        vector<Vec4i> hierarchy;
        Scalar color(0,255,0);

        Point2f point_dete_center;      //保存 RotatedRect类 返回的中心点坐标
        findContours(frame_threshold,contours,hierarchy,RETR_TREE,CHAIN_APPROX_SIMPLE);
        for(int i = 0; i != contours.size(); ++i){
            if(hierarchy[i][3] != -1 && hierarchy[i][2] == -1 && hierarchy[i][1] == -1 && hierarchy[i][0] == -1){
                drawContours(frame,contours,i,color,4,8);

                RotatedRect rect_tmp = minAreaRect(contours[i]);
                point_dete_center = rect_tmp.center;
                vec_collect.emplace_back(rect_tmp.center);
                if(LeastSquaresCircleFitting(vec_collect,centroid,radius)){
                    circle(frame,centroid,radius,Scalar(150,150,150),2,8);

                }
            }
        }
        line(frame,centroid,point_dete_center,Scalar(255,0,0),4);

        namedWindow("threshold",WINDOW_AUTOSIZE);
        imshow("threshold",frame);
        waitKey(1);
    }
    destroyAllWindows();
    return 0;
}
  • 4
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值