1.头文件
#pragma once
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
using namespace std;
using namespace cv;
enum DiColor{RED = 0,BLUE = 1};
class ZhuangJia
{
public:
ZhuangJia();
//二值化
void erZhiHua(const Mat &src, Mat &dst, uchar yz);
//亮度调整
void liangDuTiaoZheng(const Mat &src, Mat &dst, double k, double b);
//寻找目标区域
vector<RotatedRect> xunZhaoJuXing(const Mat &img);
//拟合可能区域
vector<RotatedRect> niHeMuBiao(vector<RotatedRect> vr);
//寻找最佳目标区域
RotatedRect xunZhaoZuiJia(vector<RotatedRect> vr);
//画出目标区域
void huaChuMuBiao(Mat img, RotatedRect vr);
//计算距离,返回值越大,距离越大
int jiSuanjuLi(const Mat &imgroi, int &yuzhi);
public:
uchar huiDu;
uchar diColor;//地方颜色标志
int juLiYuZhi;
int imgwidth;
int imgheight;
int liangDu;
int jianCeBianChang;
Mat erZhiTu;
Mat imgG;//绿色通道图像
Mat imgD;//敌方颜色通道图像
Mat imgTemplate;
Mat imgTemplateSmall;
bool isFar;
};
2.源文件
#include "zhuangjia.hpp"
#ifndef DEBUG
//#define DEBUG
#endif
ZhuangJia::ZhuangJia()
{
huiDu = 210;
diColor = RED;
imgwidth = 640;
imgheight = 480;
juLiYuZhi = 15;
jianCeBianChang = 2;
isFar = false;
//读取模板图片
Mat img = imread("template.bmp");
vector<Mat> vimg;
split(img,vimg);
threshold(vimg[1],imgTemplate,210,255,THRESH_BINARY);
// imshow("aa",imgTemplate);
}
void ZhuangJia::erZhiHua(const Mat &src, Mat &dst, uchar yz)
{
vector<Mat> bgr;
Mat m;
//亮度调整
liangDuTiaoZheng(src,m,1,-120);
split(m, bgr);
dst.create(src.size(),CV_8UC1);
imgG = bgr[1];
imgD = diColor == RED ? bgr[2] : bgr[0];
//二值化
uchar *ptrg = imgG.data, *ptrd = imgD.data, *ptrer = dst.data;
const uchar *ptrend = imgG.data + imgG.rows*imgG.cols;
for(; ptrg != ptrend; ptrg++,ptrd++,ptrer++)
{
*ptrer = (*ptrd - *ptrg) > yz ? 255 : 0;
}
// imshow("huidu",dst);
}
void ZhuangJia::liangDuTiaoZheng(const Mat &src, Mat &dst, double k, double b)
{
liangDu = b;
dst.create(src.size(),src.type());
uchar *ptrsrc = src.data, *ptrdst = dst.data;
const uchar *ptrend = src.data + src.cols*src.rows *3;
for(; ptrsrc != ptrend; ptrsrc++, ptrdst++)
{
int val =(int)(k * (*ptrsrc) + b);
if(val < 0)
val = 0;
if(val > 255)
{
val = 255;
cout<<val;
}
(*ptrdst) = val;
}
}
vector<RotatedRect> ZhuangJia::xunZhaoJuXing(const Mat &img)
{
vector<RotatedRect> vr;
Mat temp,cz;
vector<vector<Point> > contour;
bool bflag = false;
RotatedRect rect;
Mat ele(5,5,CV_8U,Scalar(1));
dilate(img,temp,ele);//膨胀
erode(temp,cz,ele);//腐蚀
// imshow("cz",cz);
//查找轮廓
findContours(cz,contour,RETR_CCOMP , CHAIN_APPROX_SIMPLE);
//如果轮廓过小,表明装甲距离过远,多做几次闭运算
int sum = 0,count = 0;
for(int i = 0; i < (int)contour.size(); i++){
if(contour[i].size() > 5){
sum += contour[i].size();
count++;
}
}
if(sum / count < 15){
// cout<<sum / (int)contour.size();
for(int i = 0; i < 10; i++){
dilate(cz,temp,ele);//膨胀
erode(temp,cz,ele);//腐蚀
}
findContours(cz,contour,RETR_CCOMP , CHAIN_APPROX_SIMPLE);
juLiYuZhi = 70;
jianCeBianChang = 0;
isFar = true;//距离过远标志
}else{
isFar = false;
}
// imshow("cz",cz);
// cout<<contour.size()<<endl;
// Mat tt = Mat::zeros(cz.rows, cz.cols, CV_8UC3);
// tt.create(cz.size(),CV_8UC3);
// drawContours(tt,contour,-1,Scalar(0,0,255));
// imshow("tt",tt);
// for(int i = 0; i < (int)contour.size(); i++){
// cout<<contour[i].size()<<" ";
// }
for(int i = 0; i < (int)contour.size();i++){
// cout<<contour[i].size()<<endl;
if(contour[i].size() > 5){
bflag = true;
//寻找符合条件的轮廓
rect = fitEllipse(contour[i]);
// cout<< rect.center<<" ";
int rc = rect.center.x, rr = rect.center.y;
if(rc-1>0 && rc+1<imgwidth && rr-1>0 && rr+1<imgheight){
for(int ri = -1; ri < jianCeBianChang; ri++){
for(int rj = -1; rj < jianCeBianChang; rj++){
// cout<<(int)imgD.at<uchar>(rr+ri,rc+rj)<<endl;
if(imgD.at<uchar>(rr+ri,rc+rj) < (255+liangDu-juLiYuZhi)){
// cout<<(int)imgD.at<uchar>(rr+ri,rc+rj)<<" ";
bflag = false;
break;
}
}
if(!bflag)
break;
}
if(bflag){
// cout<<endl<<i<<"; ";
vr.push_back(rect);
// Mat tt;
// for(int i = 0; i < vr.size();i++){
// tt.create(cz.size(),CV_8UC3);
// huaChuMuBiao(tt,rect);
// imshow("tt",tt);
// }
bflag = false;
}
}
}
}
#ifdef DEBUG
cout<<"轮廓个数"<<vr.size()<<endl;
#endif
if(isFar){
juLiYuZhi = 15;
jianCeBianChang = 2;
}
// cout<<isFar<<endl;
return vr;
}
vector<RotatedRect> ZhuangJia::niHeMuBiao(vector<RotatedRect> vr)
{
vector<RotatedRect> vrect;
RotatedRect rect;
int nL, nW;
const double tAngleThre = 15, tSizeThre = 3;
double dAngle;
vrect.clear();
if (vr.size() < 2) //如果检测到的旋转矩形个数小于2,则直接返回
return vrect;
//寻找目标
for (int ni = 0; ni < (int)vr.size() - 1; ni++) //求任意两个旋转矩形的夹角
{
for (int nj = ni + 1; nj < (int)vr.size(); nj++)
{
dAngle = abs(vr[ni].angle - vr[nj].angle);
while (dAngle > 180)
dAngle -= 180;
//判断这两个旋转矩形是否是一个装甲的两个LED等条
// cout<<dAngle;
if ((dAngle < tAngleThre || 180 - dAngle < tAngleThre)
&& abs(vr[ni].size.height - vr[nj].size.height)
< (vr[ni].size.height + vr[nj].size.height) / tSizeThre
&& abs(vr[ni].size.width - vr[nj].size.width)
< (vr[ni].size.width + vr[nj].size.width) / tSizeThre)
{
//cout<<"here";
rect.center.x = (vr[ni].center.x + vr[nj].center.x) / 2; //装甲中心的x坐标
rect.center.y = (vr[ni].center.y + vr[nj].center.y) / 2; //装甲中心的y坐标
rect.angle = (vr[ni].angle + vr[nj].angle) / 2; //装甲所在旋转矩形的旋转角度
if (180 - dAngle < tAngleThre)
rect.angle += 90;
nL = (vr[ni].size.height + vr[nj].size.height) / 2; //装甲的高度
nW = sqrt((vr[ni].center.x - vr[nj].center.x)
* (vr[ni].center.x - vr[nj].center.x)
+ (vr[ni].center.y - vr[nj].center.y)
* (vr[ni].center.y - vr[nj].center.y)); //装甲的宽度等于两侧LED所在旋转矩形中心坐标的距离
// if (nL < nW)
// {
rect.size.height = nL;
rect.size.width = nW;
// }
// else
// {
// rect.size.height = nW;
// rect.size.width = nL;
// }
if(rect.size.width/rect.size.height < 4 && rect.size.width/rect.size.height > 2){
// cout<<"宽高比"<<rect.size.width/rect.size.height<<endl;
vrect.push_back(rect); //将找出的装甲的旋转矩形保存到vector
}
}
}
}
#ifdef DEBUG
cout<<"目标个数"<<vrect.size()<<endl;
#endif
return vrect;
}
RotatedRect ZhuangJia::xunZhaoZuiJia(vector<RotatedRect> vr)
{
RotatedRect rect;
if(vr.size() < 1)
return rect;
if(vr.size() == 1){
rect = vr[0];
return rect;
}
//利用模板寻找最佳目标
Mat roi;
int dist = imgTemplate.cols * imgTemplate.rows;
const double pi = 3.1415926;
if(imgTemplate.cols < 5 && imgTemplate.rows < 5){
cout<<"no template"<<endl;
return rect;
}
double dangle, a, l;
int ic1,ic2,ir1,ir2;
int yuZhi = isFar ? 70 : 15;
//模板与目标根据最小距离得到最佳
for(int i = 0; i < (int)vr.size(); i++){
// cout<<i<<endl;
//选出目标区域
dangle = vr[i].angle;
while(dangle > 90)
dangle -= 180;
a = dangle*pi/180;
l = vr[i].size.width / (cos(a) * 2);
// cout<<"yizu"<<vr[i].size.width<<endl<<vr[i].size.height<<endl<<vr[i].angle<<endl;
// cout<<vr[i].size.width<<endl<<a<<endl<<cos(a)<<endl<<l<<endl<<endl;
ic1 = vr[i].center.x - l;
ic2 = vr[i].center.x + l;
ir1 = vr[i].center.y - vr[i].size.height/4;
ir2 = vr[i].center.y + vr[i].size.height/4;
if(ic1 < 0 || ic2 > imgwidth || ir1 < 0 || ir2 > imgheight){
continue;
}
roi = imgG(Range(ir1,ir2),Range(ic1,ic2));
// imshow("roi",roi);
//计算距离
int d = jiSuanjuLi(roi,yuZhi);
if(dist > d){
rect = vr[i];
dist = d;
}
}
#ifdef DEBUG
cout<<"找到最佳"<<endl;
#endif
return rect;
}
void ZhuangJia::huaChuMuBiao(Mat img, RotatedRect vr)
{
Point2f pt[4];
vr.points(pt); //计算二维盒子顶点
line(img, pt[0], pt[1], CV_RGB(0, 0, 255), 2, 8, 0);
line(img, pt[1], pt[2], CV_RGB(0, 0, 255), 2, 8, 0);
line(img, pt[2], pt[3], CV_RGB(0, 0, 255), 2, 8, 0);
line(img, pt[3], pt[0], CV_RGB(0, 0, 255), 2, 8, 0);
}
int ZhuangJia::jiSuanjuLi(const Mat &imgroi, int &yuzhi)
{
int dist = 0;
Mat roi,binary;
//大小设置为模板大小
resize(imgroi,roi,Size(imgTemplate.cols,imgTemplate.rows));
threshold(roi,binary,255-yuzhi+liangDu,255,THRESH_BINARY);
// imshow("ss",binary);
uchar *ptrtemp = imgTemplate.data, *ptrb = binary.data;
const uchar *ptrend = imgTemplate.data + imgTemplate.cols*imgTemplate.rows;
for(; ptrtemp != ptrend; ptrtemp++,ptrb++){
//对应点不同距离加1
dist += *ptrtemp == *ptrb ? 0 : 1;
}
return dist;
}
3.main文件
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/features2d/features2d.hpp>
#include <iostream>
#include "zhuangjia.hpp"
#include "jiaodujiesuan.hpp"
using namespace std;
using namespace cv;
int main()
{
Mat img = imread("2.jpg");
// VideoCapture cap("RedCar.avi");
// VideoCapture cap("1.avi");
// Mat img;
// while(1){
// cap >> img;
// imshow("aa",img);
// imwrite("2.jpg",img);
// if(waitKey(20));
//// break;
// }
Mat imggray;
ZhuangJia zj;
// int num = 0;
// while(1){
// cap >> img;
zj.erZhiHua(img,imggray,15);
//imshow("aa",imggray);
vector<RotatedRect> vr;
vr = zj.xunZhaoJuXing(imggray);
vr = zj.niHeMuBiao(vr);
// cout<<vr.size();
// for(int i = 0; i < vr.size(); i++){
// zj.huaChuMuBiao(img,vr[i]);
// cout<<num++;
// }
RotatedRect rr;
JiaoDuJieSuan jdjs;
float pitch, yaw;
if(vr.size()>0){
rr = zj.xunZhaoZuiJia(vr);
// zj.huaChuMuBiao(img,rr);
jdjs.huoDeJuXingJiaoDian(rr,img);
jdjs.qiuJieJiaoDu(pitch,yaw);
jdjs.drawPoints(img);
cout<<pitch<<" "<<yaw<<endl;
}
imshow("main",img);
// waitKey(50);
// }
waitKey(0);
return 0;
}