机甲大师:矩形框选(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;
}


相关文章
|
7月前
|
索引 Python
星际争霸之小霸王之小蜜蜂(七)--消失的子弹
星际争霸之小霸王之小蜜蜂(七)--消失的子弹
|
5月前
|
缓存 数据库 数据安全/隐私保护
我绘制文章插图的三个神级工具
我绘制文章插图的三个神级工具
|
5月前
复现sci顶刊中的画中画(局部细节放大)
复现sci顶刊中的画中画(局部细节放大)
127 0
|
8月前
|
算法 计算机视觉 C++
机甲大师:矩形框选(23/4/23已更新)
机甲大师:矩形框选(23/4/23已更新)
26 0
|
12月前
|
机器学习/深度学习 算法
ICLR 2023 Spotlight | 2D图像脑补3D人体,衣服随便搭,还能改动作
ICLR 2023 Spotlight | 2D图像脑补3D人体,衣服随便搭,还能改动作
|
12月前
|
小程序
如何做一个俄罗斯方块6:形状停靠
在处理形状停靠之前,有一点儿东西需要了解,就是已经停靠的方块和正在下落的方块不是一种方块,如图,红色的表示的是已经停靠的方块,绿色的表示下落的绿色方块的作用是展示当前下落的形状,红色方块的作用是标识出哪些位置已经摆放了方块。
87 0
|
12月前
|
Java
手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏05图像仿射变换(平移和缩放操作)
手把手一步一步教你使用Java开发一个大型街机动作闯关类游戏05图像仿射变换(平移和缩放操作)
116 0
|
12月前
如何做一个俄罗斯方块5:形状碰撞检测(下)
其实,两侧的碰撞判断跟我们上一节讲过的向下移动的碰撞判断原理是一样的,向下碰撞检测的是每一个方块下方的位置是否有其它方块,那么向左/右碰撞检测的就是每个方块左/右侧的位置是否有其他的方块。
281 0
|
12月前
|
开发工具
如何做一个俄罗斯方块4:形状碰撞检测(上)
在游戏开发中,我们所说的“碰撞”经常指的是物理碰撞,什么是物理碰撞呢?一般的在游戏开发工具中都会包含一个叫做“物理引擎”的东西,它的作用就是在游戏中模拟出现实中的物理效果。例如,我们扔一个东西,这个东西会因为重力而下落,最终落到地上,与地面发生碰撞。在游戏中,我们可以借助物理引擎,来模拟出东西下落掉到地面上的效果。当东西掉到地面上时,我们就说这个东西与地面发生了碰撞。
274 0
|
12月前
|
小程序 搜索推荐 开发者
谈谈宝石方块游戏中的设计
宝石方块是在上一个俄罗斯方块工程的基础上改编的,所以制作起来很快,我只用了不到两天的时间就完成了游戏的功能,后续又花了几天的时间制作游戏的界面,优化游戏的体验。
108 0