概述
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;
}
最后
以上就是昏睡花生为你收集整理的机甲大师:矩形框选的全部内容,希望文章能够帮你解决机甲大师:矩形框选所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复