# 任务1_Armor **Repository Path**: MrDreamQ/task-1--armor ## Basic Information - **Project Name**: 任务1_Armor - **Description**: No description available - **Primary Language**: C++ - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-09-24 - **Last Updated**: 2021-10-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 任务1_Armor ## 项目介绍——装甲板识别算法逻辑 以下是本人的装甲板识别算法逻辑解释。 ### 图像预处理 #### 红(蓝)色通道的灰度值增强 读入视频帧后,可以用某通道的灰度值矩阵减去另一通道的灰度值矩阵,以增强某通道的灰度值(两个通道灰度值相近的地方将会接近或等于0,这就使得灯光的颜色在该通道的灰度图上能很好地独立出来)。如增强视频中的红色灯光,则可将视频帧的三通道分离出来,然后用r通道的灰度值矩阵减去b通道的灰度值矩阵,对应代码如下: ```c++ split(src, channels); if (_color) //判断是否灯带颜色为红光 { gray_img = channels.at(2) - channels.at(0); //增强r通道的灰度值,即增强红色光,并写入到单通道图像中 } else { gray_img = channels.at(0) - channels.at(2); //增强b通道的灰度值,即增强蓝色光,并写入到单通道图像中 } ``` #### 图像二值化 用threshold函数将灰度矩阵二值化: ```c++ threshold(gray_img, binBright_img, 100, 255, THRESH_BINARY); //二值化视频帧 ``` ### 寻找符合条件的轮廓 使用: ```c++ findContours(binBright_img.clone(), lightContours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE) ``` 将轮廓找出,并将外层轮廓的像素点坐标存入二维容器`vector> lightContours` 使用: ```c++ rrect = minAreaRect(lightContours[n]); //返回某个轮廓外接矩形 ``` 得到某个轮廓的外接矩形后,使用外接矩形的长短边之比、长边长度等参数选出装甲板灯条的轮廓及其外接矩形。 ### 输出轮廓的上下角点和中心点 本人根据像素坐标系的y坐标,选出该轮廓的上下角点,对应代码如下: ```c++ auto max_y = (double)lightContours[n][0].y; auto min_y = (double)lightContours[n][0].y; double temp; Point up_point = lightContours[n][0], low_point = lightContours[n][0]; for (size_t i = 0; i < lightContours[n].size(); i++) { temp = (double)lightContours[n][i].y; if (temp > max_y) { max_y = temp; up_point = lightContours[n][i]; } if (temp < min_y) { min_y = temp; low_point = lightContours[n][i]; } } light_point.push_back(up_point); light_point.push_back(low_point); light_center.push_back(cpt); ``` 注意:这里的up_point是y坐标较大的点,由于原点在左上角,因此up_point在视频里是下角点,low_point是上角点(懒得改了(笑),主要改了后也要对任务6作出修改,当时没懂像素坐标系这个原理) 将该轮廓的上下角点及其外接矩形的中心点分别存入容器`light_point`,`light_center`。 ### 略过两个灯条之间非装甲板的过长区域的标定 在装甲板_2.avi的视频中,两个灯条之间可能是装甲板,也可能是非装甲板的过长区域,如下图所示: ![](https://s3.bmp.ovh/imgs/2021/10/45d8bd4886759544.png) 本人利用该区域的x轴方向长度与y轴方向长度之比来略过该区域的标定,代码如下: ```c++ bool passLongArea = false; //是否跳过两个灯条中间区域的标定 if (light_point.size() > 2) { auto x_length =abs(light_point[2].x - light_point[0].x); auto y_length = abs(light_point[1].y - light_point[0].y); if (x_length / y_length > 4.0) { passLongArea = true; //若中间区域的长宽比大于4,则跳过两个灯条中间区域的标定 } } ```  注意:这里只是简略的使用x轴坐标之差与y轴坐标之差的比值做判断,最正规的方式应为**两个灯条下角点的欧式距离与一个灯条上下角点的欧式距离之比** ### 标定装甲板区域 最后利用`line,circle,putText`等函数在原三通道视频帧相应的坐标位置标定装甲板区域即可。 ### 运行轨迹和目标打击点的简单预测 本人采用的方案——取3帧画面之间装甲板中心点坐标变化速度的平均值作为下一帧装甲板中心点的变化速度,将此速度的数倍加上该帧数下的装甲板中心点坐标即可得到目标预测点的坐标,代码如下: ## 使用说明 ### 运行任务1可执行文件 需要重新编译得到`makefile和test`可执行文件 在该项目文件夹下,运行命令行指令: ```bash $ mkdir build $ cd build/ $ cmake .. $ make $ ./test <装甲板_1和装甲板_2视频文件的路径> ``` 即可查看识别装甲板_2的装甲板识别效果视频。 效果如图(绿圈即为目标点的预测范围圆): ![](https://s3.bmp.ovh/imgs/2021/10/77d264d6654c3f73.png) ![](https://s3.bmp.ovh/imgs/2021/10/276a3c158488f5c6.png) ### 实例化对象的接口设置 与任务2和任务6的程序一样,该任务程序里有多种构造函数重载: ```c++ armorDetect::armorDetect() { _is_recorded = false; _color = RED; _is_continuous = true; } /** * @brief 写入视频判断参数设置 * * @param is_recorded 写入视频判断参数,true为写入视频并保存至build文件夹 */ armorDetect::armorDetect(bool is_recorded) { _is_recorded = is_recorded; _color = RED; _is_continuous = true; } /** * @brief 写入视频判断参数、灯条颜色参数、播放模式参数设置 * * @param is_recorded 写入视频判断参数,true为写入视频并保存至build文件夹 * @param color 灯条颜色参数,将会影响b通道或r通道灰度值的增强 * @param is_continuous 播放模式参数,true为连续播放模式,false为逐帧播放模式 */ armorDetect::armorDetect(bool is_recorded = false, int color = RED, bool is_continuous = true) { _is_recorded = is_recorded; _color = color; _is_continuous = is_continuous; } ``` 其中,参数1为视频写入判断参数,参数2为视频中灯条的颜色**(会影响图像预处理阶段中红蓝色通道增强过程)**,参数3为视频播放模式判断参数。 在`main.cpp`创建对象时,可选择多种创建方式。 如程序默认的创建对象方式为: ```c++ armorDetect test(false, RED, true); ``` 则运行程序后不会写入并保存效果视频,处理默认灯条颜色——红光,连续播放模式。 若创建对象的方式为: ```c++ armorDetect test(true, BLUE, false); ``` 则运行程序后会写入视频,并在build文件夹下保存名为"代码效果.avi",编码格式为MPG2的视频文件,处理的灯条颜色为蓝色,逐帧播放视频(按下任意按键使视频前进,按下esc键退出) 注意:在连续播放模式中,按下空格键使视频暂停,暂停后按下除esc键的任意键使视频继续,暂停后按下esc键退出,该逻辑代码如下: ```c++ if (waitKey(30) == ' ') //按下空格暂停 { if (waitKey(0) == 27) //暂停后按下esc键退出 { break; } } ``` ## 注意事项 调用任务1可执行文件时,要附上装甲板\_1和装甲板\_2的视频文件路径。