机甲大师:矩形框选(23/4/23已更新)

简介: 机甲大师:矩形框选(23/4/23已更新)

机甲大师:矩形框选算法(23/4/23已更新)

2023/4/17:由于大创的关系,我把这个算法整合进新项目中,所以同时修复了代码中的一些问题,比如全局对象问题,当然挺多没改的(代码是大一写的,现在看看真的是屎。)


效果如图:



——————————————————分割线—————————————————


本项目是一个基于Opencv与C++的项目,主要实现了装甲板灯条的识别,并用矩形动态框选


本项目是我RM视觉初学者时,结合队友的项目基础,一点点磨出来的,算是一个优化版本。


PS:这个版本矩形框不会跳动,很稳定,我优化了很久,但视频帧率有点低


整个项目我在初学时都标注了详尽的注释,可谓花了很多功夫。希望能帮到各位有需要的人吧。


个人额外提醒:注意项目中的 功能4:斜矩形框选 ,那个是优化的精髓。我研究了很久,可能是我当时Opencv基础较差的原因,可以让矩形框选保持稳定。


项目配置:


采用Opecv4以上版本:

#include <iostream>
#include<vector>
#include<opencv2/opencv.hpp>
#include<opencv2/highgui/highgui_c.h>
#include<imgproc/imgproc.hpp>
#include<core/core.hpp>
using namespace std;
using namespace cv;
class rec_recognition
{
public:
    int match_num;
    vector<RotatedRect>rect_match;
    vector<Point3i>level_end;
    Mat src;
    Mat src_end, dst;
    Mat GrayImg;
    Mat BinBrightImg;
    Mat BinGrayImg;
    Mat final;
    double max(double first, double secend);
    double min(double first, double secend);
    void pre_treatment();
    void application();
    int match();
private:
    int index = 0, maxlevel = 0;
    RotatedRect rect, leftrect, rightrect;
    Rect rect_first;
    Point2f currentCenter;
    double area[2];
    double height = (leftrect.size.height + rightrect.size.height) / 2;
};
int main(){
    VideoCapture capture("/Users/nathanchen/Downloads/rec_recognition/rectangle.mp4"); //这里输入文件
    rec_recognition apply;
    while (1){
        capture >> apply.src;
        apply.pre_treatment();
        apply.match();
        if (apply.match_num == 1){ //如果match处匹配成功,那就返回1
            apply.application();
            //imshow("rec_recognition", apply.src_end);
            imshow("rec_recognition1", apply.src);
        }
        apply.rect_match.clear();
        apply.level_end.clear();
        waitKey(1000 / capture.get(CAP_PROP_FPS)); //每隔多少秒读下一帧
    }
    return 0;
}
void rec_recognition::pre_treatment(){
    //色彩通道处理
    std::vector<Mat> channels;// 把一个3通道图像转换成3个单通道图像
    split(src, channels);//分离色彩通道
    //只留蓝色(可以修改成其他颜色)——————————————————————————————————————修改颜色
    GrayImg = channels.at(0) - channels.at(2);
    //阈值处理
    threshold(GrayImg, BinBrightImg, 55, 255, THRESH_BINARY);
    threshold(GrayImg, BinGrayImg, 25, 255, THRESH_BINARY);
    //形态学操作(膨胀)
    Mat element_d = getStructuringElement(MORPH_ELLIPSE, Size(2, 2));//形态学操作
    dilate(BinBrightImg, BinBrightImg, element_d);//膨胀
    //与操作(?????)
    BinBrightImg &= BinGrayImg;
    //形态学操作(膨胀与腐蚀)
    Mat element_d0 = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
    Mat element_e0 = getStructuringElement(MORPH_ELLIPSE, Size(3, 3));
    //再次腐蚀膨胀,为了让线条更连贯
    dilate(BinBrightImg, BinBrightImg, element_d0);
    erode(BinBrightImg, final, element_e0);
    //--------------------------寻找矩形框架---------------------------------//
    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    Point2f vertex[4];
    findContours(final, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE, Point(0, 0));
    //筛选矩形
    for (int i = 0; i < hierarchy.size(); ++i)
    {
        rect = minAreaRect(contours.at(i));
        if (rect.size.width > rect.size.height)
        {
            swap(rect.size.width, rect.size.height);
            rect.angle += 90.0;
        }
        while (rect.angle >= 90.0)rect.angle -= 180.0;
        while (rect.angle <= -90.0)rect.angle += 180.0;
        if (rect.size.width * rect.size.height < 1000)
        {
            if (rect.size.width < rect.size.height / 2.5)
            {
                if (rect.angle < 45 || rect.angle>135)
                {
                    rect_match.push_back(rect);  //将匹配好的矩形放入数组(rect_match)中
                }
            }
        }
        //测试:矩形框选灯条
        /*rect_first = boundingRect(contours.at(i));
         if (rect_first.width < rect_first.height)
         {
             rectangle(src, rect_first, Scalar(255, 0, 255), 2, LINE_8);
         }*/
    }
    cout << rect_match.size() << endl;
    //清空数组,防止内存泄漏
    contours.clear();
    hierarchy.clear();
    //imshow("Shlimazl1", final);
}
int rec_recognition::match(){
    int level = 0;
    if (rect_match.size() < 2){
        return match_num = 0;
    }else{
        for (int j = 0; j < rect_match.size(); ++j){
            for (int k = j + 1; k < rect_match.size(); ++k){
                int level = 0;
                leftrect = rect_match[j];
                rightrect = rect_match[k];
                //左右矩形高匹配
                if ((min(leftrect.size.height, rightrect.size.height) / max(leftrect.size.height, rightrect.size.height)) == 1)
                {
                    level += 10;
                }
                else if ((min(leftrect.size.height, rightrect.size.height) / max(leftrect.size.height, rightrect.size.height)) > 0.95)
                {
                    level += 8;
                }
                else if ((min(leftrect.size.height, rightrect.size.height) / max(leftrect.size.height, rightrect.size.height)) > 0.9)
                {
                    level += 6;
                }
                else if ((min(leftrect.size.height, rightrect.size.height) / max(leftrect.size.height, rightrect.size.height)) > 0.85)
                {
                    level += 4;
                }
                else if ((min(leftrect.size.height, rightrect.size.height) / max(leftrect.size.height, rightrect.size.height)) > 0.8)
                {
                    level += 2;
                }
                //左右矩形角度匹配
                if (leftrect.angle == rightrect.angle)
                {
                    level += 10;
                }
                else if (abs(leftrect.angle - rightrect.angle) < 4)
                {
                    level += 8;
                }
                else if (abs(leftrect.angle - rightrect.angle) < 8)
                {
                    level += 6;
                }
                else if (abs(leftrect.angle - rightrect.angle) < 12)
                {
                    level += 4;
                }
                else if (abs(leftrect.angle - rightrect.angle) < 16)
                {
                    level += 2;
                }
                //左右矩形面积匹配
                area[0] = leftrect.size.width * leftrect.size.height;
                area[1] = rightrect.size.width * rightrect.size.height;
                if (area[0] == area[1])
                {
                    level += 10;
                }
                else if (min(area[0], area[1]) * 1.2 > max(area[0], area[1]))
                {
                    level += 8;
                }
                else if (min(area[0], area[1]) * 1.5 > max(area[0], area[1]))
                {
                    level += 6;
                }
                else if (min(area[0], area[1]) * 1.8 > max(area[0], area[1]))
                {
                    level += 4;
                }
                else if (min(area[0], area[1]) * 2.1 > max(area[0], area[1]))
                {
                    level += 2;
                }
                //左右矩形中心的y值匹配
                if (leftrect.center.y == rightrect.center.y)
                {
                    level += 10;
                }
                else if (abs(leftrect.center.y - rightrect.center.y) < 0.05 * height)
                {
                    level += 8;
                }
                else if (abs(leftrect.center.y - rightrect.center.y) < 0.1 * height)
                {
                    level += 6;
                }
                else if (abs(leftrect.center.y - rightrect.center.y) < 0.15 * height)
                {
                    level += 4;
                }
                else if (abs(leftrect.center.y - rightrect.center.y) < 0.2 * height)
                {
                    level += 2;
                }
                //左右矩形中心的x值匹配
                if ((abs(leftrect.center.x - rightrect.center.x) / 2.5 * height) == 1)
                {
                    level += 10;
                }
                else if (1 > (abs(leftrect.center.x - rightrect.center.x) / 2.5 * height) > 0.9)
                {
                    level += 8;
                }
                else if (0.9 > (abs(leftrect.center.x - rightrect.center.x) / 2.5 * height) > 0.8)
                {
                    level += 6;
                }
                else if (0.8 > (abs(leftrect.center.x - rightrect.center.x) / 2.5 * height) > 0.7)
                {
                    level += 4;
                }
                else if (0.7 > (abs(leftrect.center.x - rightrect.center.x) / 2.5 * height) > 0.6)
                {
                    level += 2;
                }
                level_end.push_back(Point3i(j, k, level));   //level_end 为一个数组 , 为什么要用3D坐标系???秀技???
            }
        }
        //level 的比较
        int maxlevel = 0;
        for (int C = 0; C < level_end.size(); ++C)
        {
            if (level_end[C].z > maxlevel)
            {
                maxlevel = level_end[C].z;
                index = C;
            }
        }
        // rect_match[level_end[index]
        //首先我们筛选出优质的矩形对,这里面包含(左矩形的标号,右矩形的标号,权值),这里是一个三元数组,x为左矩形,y为右
        //输出中心点坐标
        currentCenter.x = (rect_match[level_end[index].x].center.x + rect_match[level_end[index].y].center.x) / 2;
        currentCenter.y = (rect_match[level_end[index].x].center.y + rect_match[level_end[index].y].center.y) / 2;
        //return currentCenter, rect_match[level_end[index].x].center, rect_match[level_end[index].y].center;
        return match_num = 1;
    }
}
void rec_recognition::application()
{
    Point2f points1[4];
    Point2f points2[4];
    Point2f Leftc;
    Point2f Rightc;
    //注意:在视频中,左右矩形会交换
    rect_match[level_end[index].y].points(points2);
    rect_match[level_end[index].x].points(points1);
    Leftc = rect_match[level_end[index].x].center;
    Rightc = rect_match[level_end[index].y].center;
    //功能1:输出中心点坐标
    //cout << "//*********************************************" << endl;
    //cout << "(" << Leftc.x << "," << Leftc.y << ")" << endl;
    //cout << "(" << Rightc.x << "," << Rightc.y << ")" << endl;
    //cout << "*********************************************//" << endl;
     //功能2:画出中心点
    //点型
    //circle(apply.src, currentCenter, 2, Scalar(0, 0, 255), 2, 8, 0);
    //十字型
    line(src, Point(currentCenter.x - 5, currentCenter.y), Point(currentCenter.x + 5, currentCenter.y), Scalar(0, 0, 255), 2, 8, 0);
    line(src, Point(currentCenter.x, currentCenter.y - 5), Point(currentCenter.x, currentCenter.y + 5), Scalar(0, 0, 255), 2, 8, 0);
    //功能3:正矩形框选
   /* line(apply.src, Point(currentCenter.x + (Leftc.x - Rightc.x) / 2, currentCenter.y - (Leftc.x - Rightc.x) / 4),
        Point(currentCenter.x - (Leftc.x - Rightc.x) / 2, currentCenter.y - (Leftc.x - Rightc.x) / 4), Scalar(0, 0, 255), 2, 8);
    line(apply.src, Point(currentCenter.x + (Leftc.x - Rightc.x) / 2, currentCenter.y + (Leftc.x - Rightc.x) / 4),
        Point(currentCenter.x + (Leftc.x - Rightc.x) / 2, currentCenter.y - (Leftc.x - Rightc.x) / 4), Scalar(0, 0, 255), 2, LINE_8);
    line(apply.src, Point(currentCenter.x - (Leftc.x - Rightc.x) / 2, currentCenter.y + (Leftc.x - Rightc.x) / 4),
        Point(currentCenter.x - (Leftc.x - Rightc.x) / 2, currentCenter.y - (Leftc.x - Rightc.x) / 4), Scalar(0, 0, 255), 2, 8);
    line(apply.src, Point(currentCenter.x + (Leftc.x - Rightc.x) / 2, currentCenter.y + (Leftc.x - Rightc.x) / 4),
        Point(currentCenter.x - (Leftc.x - Rightc.x) / 2, currentCenter.y + (Leftc.x - Rightc.x) / 4),
        Scalar(0, 0, 255), 2, 8);*/
        //功能4:斜矩形框选
        //一开始points1[0]在右矩形,然后过一段时间它会跳到左边
    if (points2[0].x <= points1[0].x) {
        //矩形的长
        line(src, points1[2], points2[1], Scalar(0, 0, 255), 2, 8, 0);
        line(src, points1[3], points2[0], Scalar(0, 0, 255), 2, 8, 0);
        //矩形的宽
        line(src, points1[2], points1[3], Scalar(0, 0, 255), 2, 8, 0);
        line(src, points2[1], points2[0], Scalar(0, 0, 255), 2, 8, 0);
    }
    else if (points2[0].x > points1[0].x) {
        //矩形的长
        line(src, points1[0], points2[3], Scalar(0, 0, 255), 2, 8, 0);
        line(src, points1[1], points2[2], Scalar(0, 0, 255), 2, 8, 0);
        //矩形的宽
        line(src, points1[0], points1[1], Scalar(0, 0, 255), 2, 8, 0);
        line(src, points2[2], points2[3], Scalar(0, 0, 255), 2, 8, 0);
    }
    //test(点测试)
   /* circle(apply.src, points1[0], 2, Scalar(0, 0, 255), 2, 8, 0);
    circle(apply.src, points1[1], 2, Scalar(0, 0, 255), 2, 8, 0);
    circle(apply.src, points2[0], 2, Scalar(255, 0, 0), 2, 8, 0);
    circle(apply.src, points2[1], 2, Scalar(255, 0, 0), 2, 8, 0);*/
    //下一步的操作:
    //预测:重力加速度,重力为g,
    //做好预测
}
double rec_recognition::max(double first, double second){
    return first > second ? first : second;
}
double rec_recognition::min(double first, double second){
    return first < second ? first : second;
}


相关文章
|
1月前
Midjourney-03 收集Prompt 动漫风格 樱花 武士 魔法少女 自然 机甲 拟人动物 歌剧场景 星际飞船 神秘森林 精灵 详细记录 超多图片 多种风格 附带文本 关键词
Midjourney-03 收集Prompt 动漫风格 樱花 武士 魔法少女 自然 机甲 拟人动物 歌剧场景 星际飞船 神秘森林 精灵 详细记录 超多图片 多种风格 附带文本 关键词
24 0
|
程序员 定位技术 容器
ChatGPT工作提效之使用百度地图在首都为六一儿童节献爱心(多边形覆盖物、文本标注、自动获取经纬度、爱心函数)
ChatGPT工作提效之使用百度地图在首都为六一儿童节献爱心(多边形覆盖物、文本标注、自动获取经纬度、爱心函数)
122 0
|
6月前
|
人工智能 监控 API
OpenCV这么简单为啥不学——1.11、蓝背景证件照替换白色或红色
OpenCV这么简单为啥不学——1.11、蓝背景证件照替换白色或红色
81 0
|
6月前
|
缓存 数据库 数据安全/隐私保护
我绘制文章插图的三个神级工具
我绘制文章插图的三个神级工具
109 0
|
6月前
复现sci顶刊中的画中画(局部细节放大)
复现sci顶刊中的画中画(局部细节放大)
411 0
|
机器学习/深度学习 编解码 算法
CV之NoGAN:利用图像增强技术(图片上色)实现对旧图像和电影片段进行着色和修复(爱因斯坦、鲁迅旧照/清末官员生活场景等案例)
CV之NoGAN:利用图像增强技术(图片上色)实现对旧图像和电影片段进行着色和修复(爱因斯坦、鲁迅旧照/清末官员生活场景等案例)
CV之NoGAN:利用图像增强技术(图片上色)实现对旧图像和电影片段进行着色和修复(爱因斯坦、鲁迅旧照/清末官员生活场景等案例)
|
算法 计算机视觉 C++
机甲大师:矩形框选(23/4/23已更新)
机甲大师:矩形框选(23/4/23已更新)
45 0
|
人工智能 API 数据安全/隐私保护
Python3,5行代码,Chatxxx能对PDF文件进行旋转、提取、合并等一系列操作,看了这篇,80岁老奶奶走路都不扶墙了。
Python3,5行代码,Chatxxx能对PDF文件进行旋转、提取、合并等一系列操作,看了这篇,80岁老奶奶走路都不扶墙了。
101 0
|
机器学习/深度学习 算法
ICLR 2023 Spotlight | 2D图像脑补3D人体,衣服随便搭,还能改动作
ICLR 2023 Spotlight | 2D图像脑补3D人体,衣服随便搭,还能改动作
|
Java
手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏05图像仿射变换(平移和缩放操作)
手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏05图像仿射变换(平移和缩放操作)
155 0