matchLights

本函数将灯条两两匹配,条件判断和传入conTain()判断是否为装甲板将符合的装甲板放入candidateArmors容器

这个部分主要是对灯条的两两配对,主要通过物理特征

    // 将旋转矩形从左到右排序
    sort(candidateLights.begin(), candidateLights.end(),
         [](RotatedRect& a1, RotatedRect& a2) {
             return a1.center.x < a2.center.x; });

sort函数是一种快速排序算法,其参数要求传入指针和函数指针,写法较为固定

这里的函数写法名为 “lambda表达式” 也可以叫匿名函数,是一种C++引入的新特性

通过这个方法,我们就可以不用再写一个函数,简化代码


下面则是对每两个灯条进行匹配:

        Light lightI = candidateLights[i];
        Point2f centerI = lightI.center;    // 灯条1中心点

        for (size_t j = i + 1; j < candidateLights.size(); j++)
        {
            Light lightJ = candidateLights[j];
            Point2f centerJ = lightJ.center;     // 灯条2中心点
            double armorWidth = POINT_DIST(centerI,centerJ) - (lightI.width + lightJ.width)/2.0;                    // 计算装甲板宽度
            double armorHeight = (lightI.height + lightJ.height) / 2.0;                                             // 计算装甲板高度
            double armor_ij_ratio = lightI.height / lightJ.height;                                                  // 计算两灯条高度比例
            double armorAngle = atan2((centerI.y - centerJ.y),fabs(centerI.x - centerJ.x))/CV_PI*180.0;    // 计算装甲板倾斜角度

            // 宽高比筛选条件
            bool small_wh_ratio_ok = armor_small_min_wh_ratio < armorWidth/armorHeight && armorWidth/armorHeight < armor_small_max_wh_ratio;
            bool big_wh_ratio_ok = armor_big_min_wh_ratio < armorWidth/armorHeight && armorWidth/armorHeight < armor_big_max_wh_ratio;
            bool wh_ratio_ok = small_wh_ratio_ok || big_wh_ratio_ok;

            // 左右灯条角度差筛选条件
            bool angle_offset_ok = fabs(lightI.angle - lightJ.angle) < armor_max_offset_angle;

            // 左右亮灯条中心点高度差筛选条件
            bool height_offset_ok = fabs(lightI.center.y - lightJ.center.y) / armorHeight < armor_height_offset;

            // 左右灯条的高度比
            bool ij_ratio_ok = armor_ij_min_ratio < armor_ij_ratio && armor_ij_ratio < armor_ij_max_ratio;

            // 候选装甲板角度筛选条件
            bool angle_ok = fabs(armorAngle) < armor_max_angle;

            // 条件集合
            bool is_like_Armor = wh_ratio_ok && angle_offset_ok && height_offset_ok && ij_ratio_ok && angle_ok;

            // 判断为装甲板
            if (is_like_Armor)
            {

                Point2f armorCenter = (centerI + centerJ) / 2.0;
                RotatedRect armor_rrect = RotatedRect(armorCenter,
                                                      Size2f(armorWidth,armorHeight),
                                                      -armorAngle);

                // 装甲板4点坐标 (从左下角开始逆时针)
                Point2f pt4[4] = { lightI.bottom, lightJ.bottom, lightJ.top, lightI.top };

                // conTain()判断两个灯条是否包含了其他灯条
                if (!conTain(armor_rrect,candidateLights,i,j))
                {
                    Armor armor(armor_rrect);

                    for(int index = 0; index < 4; index++)
                        armor.armor_pt4[index] = pt4[index];

                    // 判断大小装甲板
                    if(small_wh_ratio_ok)
                        armor.type = SMALL;
                    else
                        armor.type = BIG;

                    // 判断装甲板的 id
                    preImplement(armor);
                    // 将符合要求的装甲板放入 candidateArmors 容器
                    candidateArmors.emplace_back(armor);
#ifdef DRAW_ARMORS_RRT
                    //cout<<"LightI_angle :   "<<lightI.angle<<"   LightJ_angle :   "<<lightJ.angle<<"     "<<fabs(lightI.angle - lightJ.angle)<<endl;
                    //cout<<"armorAngle   :   "<<armorAngle * 180 / CV_PI <<endl;
                    //cout<<"    w/h      :   "<<armorWidth/armorHeight<<endl;
                    //cout<<"height-offset:   "<<fabs(lightI.height - lightJ.height) / armorHeight<<endl;
                    //cout<<" height-ratio:   "<<armor_ij_ratio<<endl;

                    Mat showSrc = _src.clone();
                    Point2f vertice_armors[4];
                    armor.points(vertice_armors);
                    for (int m = 0; m < 4; m++)
                    {
                        line(showSrc, vertice_armors[m], vertice_armors[(m + 1) % 4], CV_RGB(0, 255, 255),2,LINE_8);
                    }
                    //circle(showSrc,armorCenter,15,Scalar(0,255,255),-1);
//                    namedWindow("showSrc",WINDOW_NORMAL);
                    imshow("showSrc", showSrc);
                    putText(showSrc,to_string(armorAngle),armor.armor_pt4[3],FONT_HERSHEY_COMPLEX,1.0,Scalar(0,255,255),2,8);
#endif //DRAW_ARMORS_RRT
                }
            }

        }

这里物理特征匹配不进行讲解,请自行观看

这里需要注意的就是装甲板的角点顺序,从左下角开始逆时钟进行计算

如图:

请记住装甲板的角点顺序


这里是对装甲板数字的一个存储函数,请看:

// 判断装甲板的 id
preImplement(armor);

// 功能: 获取传入装甲板的数字ROI,放置在numROIs容器内
void ArmorDetector::preImplement(Armor& armor)
{
    Mat numDst;
    Mat num;

    // Light length in image
    const int light_length = 14;     //大致为高的一半
    // Image size after warp
    const int warp_height = 30;
    const int small_armor_width = 32;//为48/3*2
    const int large_armor_width = 44;//约为70/3*2
    // Number ROI size
    const cv::Size roi_size(22, 30);

    const int top_light_y = (warp_height - light_length) / 2;
    const int bottom_light_y = top_light_y + light_length;
    //std::cout<<"type:"<<armor.type<<std::endl;
    // 大小装甲板的宽度不同
    const int warp_width = armor.type == SMALL ? small_armor_width : large_armor_width;

    cv::Point2f target_vertices[4] = {
            cv::Point(0, bottom_light_y),
            cv::Point(warp_width, bottom_light_y),
            cv::Point(warp_width, top_light_y),
            cv::Point(0, top_light_y),
    };
    const Mat& rotation_matrix = cv::getPerspectiveTransform(armor.armor_pt4, target_vertices);
    cv::warpPerspective(_src, numDst, rotation_matrix, cv::Size(warp_width, warp_height));

    // Get ROI
    numDst = numDst(cv::Rect(cv::Point((warp_width - roi_size.width) / 2, 0), roi_size));
    Dnn_Detect.img_processing(numDst, numROIs);
}

这里会将认为是装甲板的中心部分进行剪裁缩放,然后存放到 numROIs 容器中,以便后续进行数字识别

warpPerspective 是OpenCV库中用于执行透视变换的函数之一。透视变换可以将图像从一个透视投影转换为另一个透视投影,实现图像的旋转、缩放、平移等操作,我们通过这个函数来剪裁数字部分

关于Dnn_Detect,这个是一个封装好的推理类,在后续chooseTarget中的会进行简单介绍

results matching ""

    No results matching ""