視覺組考覈——裝甲板識別
識別Robomaster的裝甲板的簡易程序
算法分析
裝甲板識別主要分這幾步
圖像處理—>提取燈柱同時對燈柱進行篩選->燈條匹配—>裝甲板的篩選
1.圖像處理
亮度處理
利用其中(, 是亮度增減變量)降低圖像的亮度
Mat Adjust_Bright(Mat src)
{
Mat dst_BR = Mat::zeros(src.size(), src.type());
Mat BrightnessLut(1, 256, CV_8UC1);
for (int i = 0; i < 256; i++) {
BrightnessLut.at<uchar>(i) = saturate_cast<uchar>(i + _Armor.Image_bright);
}
LUT(src, BrightnessLut, dst_BR);
return dst_BR;
}
灰度化處理
考慮到燈柱爲紅色和藍色,識別時
- 對與紅色裝甲板,r通道-b通道
- 對於藍色裝甲板,b通道-r通道
if (_Armor.Armor_Color) dst = channels[2] - channels[0];
else dst = channels[0] - channels[2];
均值濾波
考慮到燈柱中心是接近於白色的,在通道相減後進行一下均值濾波
blur(dst, dst, Size(1,3));
圖像二值化
閾值化處理得到二值圖
threshold(dst, dst, _Armor.threshold_value, 255, THRESH_BINARY);
膨脹
進行膨脹處理讓圖像中的輪廓更明顯
Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3), Point(-1, -1));
dilate(dst, dst, kernel);
2. 輪廓擬合
尋找輪廓
findContours可以二值圖中找到燈柱輪廓,但耗時較大
vector<vector<Point>> Light_Contour; // 發現的輪廓
findContours(dst, Light_Contour, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); // 尋找輪廓
篩選輪廓
利用fitEllipse()得到輪廓的旋轉矩陣
篩選的條件有
- 面積
- 長寬比
- 角度的傾斜程度
- 輪廓面積比
for (int i = 0; i < Light_Contour.size(); i++) {
// 求輪廓面積
float Light_Contour_Area = contourArea(Light_Contour[i]);
// 去除較小輪廓&fitEllipse的限制條件
if (Light_Contour_Area < _Armor.Light_Area_min || Light_Contour[i].size() <= 5)
continue;
// 用橢圓擬合區域得到外接矩形
RotatedRect Light_Rec = fitEllipse(Light_Contour[i]);
Light_Rec = adjustRec(Light_Rec);
if (Light_Rec.angle > _Armor.Light_angle)
continue;
// 長寬比和輪廓面積比限制
if (Light_Rec.size.width / Light_Rec.size.height > _Armor.Light_Aspect_ratio
|| Light_Contour_Area / Light_Rec.size.area() < _Armor.Light_crown)
continue;
// 擴大燈柱的面積
Light_Rec.size.height *= 1.1;
Light_Rec.size.width *= 1.1;
vContour.push_back(Light_Rec);
}
3. 燈柱匹配
尋找兩個相同的燈條
篩選條件有
- 角度差
- 長度差比率
- 寬度差比率
//判斷是否爲相同燈條
float Contour_angle = abs(vContour[i].angle - vContour[j].angle); //角度差
if (Contour_angle >= _Armor.Light_Contour_angle)
continue;
//長度差比率
float Contour_Len1 = abs(vContour[i].size.height - vContour[j].size.height) / max(vContour[i].size.height, vContour[j].size.height);
//寬度差比率
float Contour_Len2 = abs(vContour[i].size.width - vContour[j].size.width) / max(vContour[i].size.width, vContour[j].size.width);
if (Contour_Len1 > _Armor.Light_Contour_Len || Contour_Len2 > _Armor.Light_Contour_Len)
continue;
4. 裝甲板篩選
篩選條件有
- 長寬比
- x差比率
- y差比率
RotatedRect Rect;
Rect.center.x = (vContour[i].center.x + vContour[j].center.x) / 2.; //x座標
Rect.center.y = (vContour[i].center.y + vContour[j].center.y) / 2.; //y座標
Rect.angle = (vContour[i].angle + vContour[j].angle) / 2.; //角度
float nh, nw, yDiff, xDiff;
nh = (vContour[i].size.height + vContour[j].size.height) / 2; //高度
// 寬度
nw = sqrt((vContour[i].center.x - vContour[j].center.x) * (vContour[i].center.x - vContour[j].center.x) + (vContour[i].center.y - vContour[j].center.y) * (vContour[i].center.y - vContour[j].center.y));
float ratio = nw / nh; //匹配到的裝甲板的長寬比
xDiff = abs(vContour[i].center.x - vContour[j].center.x) / nh; //x差比率
yDiff = abs(vContour[i].center.y - vContour[j].center.y) / nh; //y差比率
if (ratio < _Armor.Armor_ratio_min || ratio > _Armor.Armor_ratio_max || xDiff < _Armor.Armor_xDiff || yDiff > _Armor.Armor_yDiff)
continue;
Rect.size.height = nh;
Rect.size.width = nw;
vRec.push_back(Rect);
參數設置
struct ArmorParam {
float Image_bright; // 圖像降低的亮度
int threshold_value; // threshold閾值
float Light_Area_min; // 燈柱的最小值
float Light_Aspect_ratio; // 燈柱的長寬比限制
float Light_crown; // 燈柱的輪廓面積比限制
float Light_angle; // 燈柱的傾斜角度
float Light_Contour_angle; // 燈柱角度差
float Light_Contour_Len; // 燈柱長度差比率
float Armor_ratio_max; // 裝甲板的長寬比max
float Armor_ratio_min; // 裝甲板的長寬比min
float Armor_xDiff; // 裝甲板x差比率
float Armor_yDiff; // 裝甲板y差比率
bool Armor_Color; // 裝甲板顏色
ArmorParam()
{
Image_bright = -100;
threshold_value = 25;
Light_Area_min = 20;
Light_Aspect_ratio = 1.0;
Light_crown = 0.5;
Light_angle = 10;
Light_Contour_angle = 7;
Light_Contour_Len = 0.25;
Armor_ratio_max = 5.0;
Armor_ratio_min = 1.0;
Armor_xDiff = 0.5;
Armor_yDiff = 2.0;
Armor_Color = RED;
}
};
缺陷
- 由於剛剛接觸圖像處理的內容,對於圖像處理的很多方法並不是很瞭解,所以在對圖像二值化的時候有較大的問題,不能夠做到一個動態閾值去動態處理,只能自己去調試。而且二值化的方法也存在一定的問題。
- 由於素材原因和時間的原因,對裝甲板的篩選並沒有做到位,會出現很多非裝甲板被識別出來,可以在後期做一個對裝甲板上的數字的模板匹配。
- 缺乏運動的目標跟蹤機制無法利用之前已經獲得的裝甲板的位置跟蹤下一幀中的裝甲板位置來優化算法的時間。
- 無法計算裝甲板的距離,應該根據離裝甲板的距離選擇不同的處理方法
- 只能做到簡單的裝甲板識別,無法進行目標選擇,可以根據距離和裝甲板的大小選擇最優的打擊目標
參考
[1]. 東南大學的開源代碼
[2]. CSDN-Raring_Ringtail-RoboMaster視覺教程(4)裝甲板識別算法
[3]. CSDN-Daibingh-RM裝甲識別程序分析(一)