//来自网络并非原创
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#include "cxcore.h"
#include "omp.h"
#include "iostream"
using namespace cv;
using namespace std;
#define T_ANGLE_THRE 10
#define T_SIZE_THRE 5
void BrightAdjust(IplImage* src, IplImage* dst, //亮度调节函数
double dContrast, double dBright)
{
int nVal;
unsigned char* SrcData = (unsigned char*)src->imageData; //原图像数据区指针
unsigned char* DstData = (unsigned char*)dst->imageData; //目的图像数据区指针
int step = src->widthStep / sizeof(unsigned char) / 3; //一行有多少个像素
omp_set_num_threads(8);
#pragma omp parallel for
for (int nI = 0; nIheight; nI++)
{
for (int nJ = 0; nJ width; nJ++)
{
for (int nK = 0; nK < 3; nK++)
{
nVal = (int)(dContrast * SrcData[(nI*step + nJ) * 3 + nK] + dBright); //每个像素的每个通道的值都进行线性变换
if (nVal < 0)
nVal = 0;
if (nVal > 255)
nVal = 255;
DstData[(nI*step + nJ) * 3 + nK] = nVal;
}
}
}
}
void GetDiffImage(IplImage* src1, IplImage* src2, IplImage* dst, int nThre)
{
unsigned char* SrcData1 = (unsigned char*)src1->imageData;
unsigned char* SrcData2 = (unsigned char*)src2->imageData;
unsigned char* DstData = (unsigned char*)dst->imageData;
int step = src1->widthStep / sizeof(unsigned char);
omp_set_num_threads(8);
#pragma omp parallel for
for (int nI = 0; nIheight; nI++)
{
for (int nJ = 0; nJ width; nJ++)
{
if (SrcData1[nI*step + nJ] - SrcData2[nI*step + nJ]> nThre) //
{
DstData[nI*step + nJ] = 255;
}
else
{
DstData[nI*step + nJ] = 0;
}
}
}
}
vector ArmorDetect(vector vEllipse)
{
vector vRlt;
CvBox2D Armor; //定义装甲区域的旋转矩形
int nL, nW;
double dAngle;
vRlt.clear();
if (vEllipse.size() < 2) //如果检测到的旋转矩形个数小于2,则直接返回
return vRlt;
for (unsigned int nI = 0; nI < vEllipse.size() - 1; nI++) //求任意两个旋转矩形的夹角
{
for (unsigned int nJ = nI + 1; nJ < vEllipse.size(); nJ++)
{
dAngle = abs(vEllipse[nI].angle - vEllipse[nJ].angle);
while (dAngle > 180)
dAngle -= 180;
if ((dAngle < T_ANGLE_THRE || 180 - dAngle < T_ANGLE_THRE) && abs(vEllipse[nI].size.height - vEllipse[nJ].size.height) < (vEllipse[nI].size.height + vEllipse[nJ].size.height) / T_SIZE_THRE && abs(vEllipse[nI].size.width - vEllipse[nJ].size.width) < (vEllipse[nI].size.width + vEllipse[nJ].size.width) / T_SIZE_THRE) //判断这两个旋转矩形是否是一个装甲的两个LED等条
{
Armor.center.x = (vEllipse[nI].center.x + vEllipse[nJ].center.x) / 2; //装甲中心的x坐标
Armor.center.y = (vEllipse[nI].center.y + vEllipse[nJ].center.y) / 2; //装甲中心的y坐标
Armor.angle = (vEllipse[nI].angle + vEllipse[nJ].angle) / 2; //装甲所在旋转矩形的旋转角度
if (180 - dAngle < T_ANGLE_THRE)
Armor.angle += 90;
nL = (vEllipse[nI].size.height + vEllipse[nJ].size.height) / 2; //装甲的高度
nW = sqrt((vEllipse[nI].center.x - vEllipse[nJ].center.x) * (vEllipse[nI].center.x - vEllipse[nJ].center.x) + (vEllipse[nI].center.y - vEllipse[nJ].center.y) * (vEllipse[nI].center.y - vEllipse[nJ].center.y)); //装甲的宽度等于两侧LED所在旋转矩形中心坐标的距离
if (nL < nW)
{
Armor.size.height = nL;
Armor.size.width = nW;
}
else
{
Armor.size.height = nW;
Armor.size.width = nL;
}
vRlt.push_back(Armor); //将找出的装甲的旋转矩形保存到vector
}
}
}
return vRlt;
}
void DrawBox(CvBox2D box, IplImage* img)
{
CvPoint2D32f point[4];
int i;
for (i = 0; i<4; i++)
{
point[i].x = 0;
point[i].y = 0;
}
cvBoxPoints(box, point); //计算二维盒子顶点
CvPoint pt[4];
for (i = 0; i<4; i++)
{
pt[i].x = (int)point[i].x;
pt[i].y = (int)point[i].y;
}
cvLine(img, pt[0], pt[1], CV_RGB(0, 0, 255), 2, 8, 0);
cvLine(img, pt[1], pt[2], CV_RGB(0, 0, 255), 2, 8, 0);
cvLine(img, pt[2], pt[3], CV_RGB(0, 0, 255), 2, 8, 0);
cvLine(img, pt[3], pt[0], CV_RGB(0, 0, 255), 2, 8, 0);
}
int main()
{
CvCapture* pCapture0 = cvCreateFileCapture("RawImage\\RedCar.avi");
//CvCapture* pCapture0 = cvCreateCameraCapture(0);
IplImage* pFrame0 = NULL;
CvSize pImgSize;
CvBox2D s; //定义旋转矩形
vector vEllipse; //定以旋转矩形的向量,用于存储发现的目标区域
vector vRlt;
vector vArmor;
CvScalar sl;
bool bFlag = false;
CvSeq *pContour = NULL;
CvMemStorage *pStorage = cvCreateMemStorage(0);
pFrame0 = cvQueryFrame(pCapture0);
pImgSize = cvGetSize(pFrame0);
IplImage *pRawImg = cvCreateImage(pImgSize, IPL_DEPTH_8U, 3);
IplImage* pGrayImage = cvCreateImage(pImgSize, IPL_DEPTH_8U, 1);
IplImage* pRImage = cvCreateImage(pImgSize, IPL_DEPTH_8U, 1);
IplImage* pGImage = cvCreateImage(pImgSize, IPL_DEPTH_8U, 1);
IplImage *pBImage = cvCreateImage(pImgSize, IPL_DEPTH_8U, 1);
IplImage *pBinary = cvCreateImage(pImgSize, IPL_DEPTH_8U, 1);
IplImage *pRlt = cvCreateImage(pImgSize, IPL_DEPTH_8U, 1);
CvSeq* lines = NULL;
CvMemStorage* storage = cvCreateMemStorage(0);
while (1)
{
if (pFrame0)
{
BrightAdjust(pFrame0, pRawImg, 1, -120); //每个像素每个通道的值都减去120
cvSplit(pRawImg, pBImage, pGImage, pRImage, 0); //将三个通道的像素值分离
//如果像素R值-G值大于25,则返回的二值图像的值为255,否则为0
GetDiffImage(pRImage, pGImage, pBinary, 25);
cvDilate(pBinary, pGrayImage, NULL, 3); //图像膨胀
cvErode(pGrayImage, pRlt, NULL, 1); //图像腐蚀,先膨胀在腐蚀属于闭运算
cvFindContours(pRlt, pStorage, &pContour, sizeof(CvContour), CV_RETR_CCOMP, CV_CHAIN_APPROX_SIMPLE); //在二值图像中寻找轮廓
for (; pContour != NULL; pContour = pContour->h_next)
{
if (pContour->total > 10) //判断当前轮廓是否大于10个像素点
{
bFlag = true; //如果大于10个,则检测到目标区域
//拟合目标区域成为椭圆,返回一个旋转矩形(中心、角度、尺寸)
s = cvFitEllipse2(pContour);
for (int nI = 0; nI < 5; nI++)
{
for (int nJ = 0; nJ < 5; nJ++) //遍历以旋转矩形中心点为中心的5*5的像素块
{
if (s.center.y - 2 + nJ > 0 && s.center.y - 2 + nJ < 480 && s.center.x - 2 + nI > 0 && s.center.x - 2 + nI < 640) //判断该像素是否在有效的位置
{
sl = cvGet2D(pFrame0, (int)(s.center.y - 2 + nJ), (int)(s.center.x - 2 + nI)); //获取遍历点点像素值
//判断中心点是否接近白色
if (sl.val[0] < 200 || sl.val[1] < 200 || sl.val[2] < 200)
bFlag = false; //如果中心不是白色,则不是目标区域
}
}
}
if (bFlag)
{
vEllipse.push_back(s); //将发现的目标保存
//cvEllipseBox(pFrame0, s, CV_RGB(255, 0, 0), 2, 8, 0);
}
}
}
//调用子程序,在输入的LED所在旋转矩形的vector中找出装甲的位置,并包装成旋转矩形,存入vector并返回
vRlt = ArmorDetect(vEllipse);
for (unsigned int nI = 0; nI < vRlt.size(); nI++) //在当前图像中标出装甲的位置
DrawBox(vRlt[nI], pFrame0);
cvShowImage("Raw", pFrame0);
if (cvWaitKey(50) == 27)
{
break;
}
vEllipse.clear();
vRlt.clear();
vArmor.clear();
pFrame0 = cvQueryFrame(pCapture0);
}
else
{
break;
}
}
cvReleaseCapture(&pCapture0);
return 0;
}
//根据以上代码将其在opencv3.0.0中编写
#include "stdafx.h"
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/imgproc.hpp"
#include "iostream"
#include "omp.h"
using namespace cv;
using namespace std;
#define T_ANGLE_THRE 10
#define T_SIZE_THRE 5
void brightAdjust(Mat src, Mat dst, double dContrast, double dBright); //亮度调节函数
void getDiffImage(Mat src1, Mat src2, Mat dst, int nThre); //二值化
vector armorDetect(vector vEllipse); //检测装甲
void drawBox(RotatedRect box, Mat img); //标记装甲
int main()
{
VideoCapture cap0("RawImage\\RedCar.avi");
Mat frame0;
Size imgSize;
RotatedRect s; //定义旋转矩形
vector vEllipse; //定以旋转矩形的向量,用于存储发现的目标区域
vector vRlt;
vector vArmor;
bool bFlag = false;
vector<vector > contour;
cap0 >> frame0;
imgSize = frame0.size();
Mat rawImg = Mat(imgSize, CV_8UC3);
Mat grayImage = Mat(imgSize, CV_8UC1);
Mat rImage = Mat(imgSize, CV_8UC1);
Mat gImage = Mat(imgSize, CV_8UC1);
Mat bImage = Mat(imgSize, CV_8UC1);
Mat binary = Mat(imgSize, CV_8UC1);
Mat rlt = Mat(imgSize, CV_8UC1);
namedWindow("Raw");
while (1)
{
if (cap0.read(frame0))
{
brightAdjust(frame0, rawImg, 1, -120); //每个像素每个通道的值都减去120
Mat bgr[3];
split(rawImg, bgr); //将三个通道的像素值分离
bImage = bgr[0];
gImage = bgr[1];
rImage = bgr[2];
//如果像素R值-G值大于25,则返回的二值图像的值为255,否则为0
getDiffImage(rImage, gImage, binary, 25);
dilate(binary, grayImage, Mat(), Point(-1,-1), 3); //图像膨胀
erode(grayImage, rlt, Mat(), Point(-1,-1), 1); //图像腐蚀,先膨胀在腐蚀属于闭运算
findContours(rlt, contour, RETR_CCOMP , CHAIN_APPROX_SIMPLE); //在二值图像中寻找轮廓
for (int i=0; iif (contour[i].size()> 10) //判断当前轮廓是否大于10个像素点
{
bFlag = true; //如果大于10个,则检测到目标区域
//拟合目标区域成为椭圆,返回一个旋转矩形(中心、角度、尺寸)
s = fitEllipse(contour[i]);
for (int nI = 0; nI < 5; nI++)
{
for (int nJ = 0; nJ < 5; nJ++) //遍历以旋转矩形中心点为中心的5*5的像素块
{
if (s.center.y - 2 + nJ > 0 && s.center.y - 2 + nJ < 480 && s.center.x - 2 + nI > 0 && s.center.x - 2 + nI < 640) //判断该像素是否在有效的位置
{
Vec3b v3b = frame0.at((int)(s.center.y - 2 + nJ), (int)(s.center.x - 2 + nI)); //获取遍历点点像素值
//判断中心点是否接近白色
if (v3b[0] < 200 || v3b[1] < 200 || v3b[2] < 200)
bFlag = false; //如果中心不是白色,则不是目标区域
}
}
}
if (bFlag)
{
vEllipse.push_back(s); //将发现的目标保存
}
}
}
//调用子程序,在输入的LED所在旋转矩形的vector中找出装甲的位置,并包装成旋转矩形,存入vector并返回
vRlt = armorDetect(vEllipse);
for (unsigned int nI = 0; nI < vRlt.size(); nI++) //在当前图像中标出装甲的位置
drawBox(vRlt[nI], frame0);
imshow("Raw", frame0);
if (waitKey(50) == 27)
{
break;
}
vEllipse.clear();
vRlt.clear();
vArmor.clear();
}
else
{
break;
}
}
cap0.release();
return 0;
}
void brightAdjust(Mat src, Mat dst, double dContrast, double dBright)
{
int nVal;
omp_set_num_threads(8);
#pragma omp parallel for
for (int nI = 0; nI(nI);
Vec3b* p2 = dst.ptr(nI);
for (int nJ = 0; nJ for (int nK = 0; nK < 3; nK++)
{
//每个像素的每个通道的值都进行线性变换
nVal = (int)(dContrast * p1[nJ][nK] + dBright);
if (nVal < 0)
nVal = 0;
if (nVal > 255)
nVal = 255;
p2[nJ][nK] = nVal;
}
}
}
}
void getDiffImage(Mat src1, Mat src2, Mat dst, int nThre)
{
omp_set_num_threads(8);
#pragma omp parallel for
for (int nI = 0; nI(nI);
uchar* pchar2 = src2.ptr(nI);
uchar* pchar3 = dst.ptr(nI);
for (int nJ = 0; nJ if (pchar1[nJ] - pchar2[nJ]> nThre) //
{
pchar3[nJ] = 255;
}
else
{
pchar3[nJ] = 0;
}
}
}
}
vector armorDetect(vector vEllipse)
{
vector vRlt;
RotatedRect armor; //定义装甲区域的旋转矩形
int nL, nW;
double dAngle;
vRlt.clear();
if (vEllipse.size() < 2) //如果检测到的旋转矩形个数小于2,则直接返回
return vRlt;
for (unsigned int nI = 0; nI < vEllipse.size() - 1; nI++) //求任意两个旋转矩形的夹角
{
for (unsigned int nJ = nI + 1; nJ < vEllipse.size(); nJ++)
{
dAngle = abs(vEllipse[nI].angle - vEllipse[nJ].angle);
while (dAngle > 180)
dAngle -= 180;
//判断这两个旋转矩形是否是一个装甲的两个LED等条
if ((dAngle < T_ANGLE_THRE || 180 - dAngle < T_ANGLE_THRE) && abs(vEllipse[nI].size.height - vEllipse[nJ].size.height) < (vEllipse[nI].size.height + vEllipse[nJ].size.height) / T_SIZE_THRE && abs(vEllipse[nI].size.width - vEllipse[nJ].size.width) < (vEllipse[nI].size.width + vEllipse[nJ].size.width) / T_SIZE_THRE)
{
armor.center.x = (vEllipse[nI].center.x + vEllipse[nJ].center.x) / 2; //装甲中心的x坐标
armor.center.y = (vEllipse[nI].center.y + vEllipse[nJ].center.y) / 2; //装甲中心的y坐标
armor.angle = (vEllipse[nI].angle + vEllipse[nJ].angle) / 2; //装甲所在旋转矩形的旋转角度
if (180 - dAngle < T_ANGLE_THRE)
armor.angle += 90;
nL = (vEllipse[nI].size.height + vEllipse[nJ].size.height) / 2; //装甲的高度
nW = sqrt((vEllipse[nI].center.x - vEllipse[nJ].center.x) * (vEllipse[nI].center.x - vEllipse[nJ].center.x) + (vEllipse[nI].center.y - vEllipse[nJ].center.y) * (vEllipse[nI].center.y - vEllipse[nJ].center.y)); //装甲的宽度等于两侧LED所在旋转矩形中心坐标的距离
if (nL < nW)
{
armor.size.height = nL;
armor.size.width = nW;
}
else
{
armor.size.height = nW;
armor.size.width = nL;
}
vRlt.push_back(armor); //将找出的装甲的旋转矩形保存到vector
}
}
}
return vRlt;
}
void drawBox(RotatedRect box, Mat img)
{
Point2f pt[4];
int i;
for (i = 0; i<4; i++)
{
pt[i].x = 0;
pt[i].y = 0;
}
box.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);
}
OpenMp提供了对并行算法的高层的抽象描述,程序员通过在源代码中加入专用的pragma来指明自己的意图,由此编译器可以自动将程序进行并行化,并在必要之处加入同步互斥以及通信。
要在Visual C++ 中使用OpenMP其实不难,只要将 Project 的Properties中C/C++里Language的OpenMP Support开启(参数为 /openmp),就可以让VC++2005 在编译时支持OpenMP 的语法了;而在编写使用OpenMP 的程序时,则需要先include OpenMP的头文件:omp.h。
在for循环前面添加如下代码,可实现并行计算。
omp_set_num_threads(NUM_THREADS); //NUM_THREADS为并行计算的线程数
#pragma omp parallel for
首先检测出LED灯带的位置,检测的方法:图像二值化,提取轮廓,拟合成旋转矩形,判断旋转矩形的中心是否接近白色,若接近白色,则说明是LED等待所在位置,然后利用检测出的LED灯带的旋转矩形的是否是平行关系来检测装甲。
一帧图像为例,说明其检测过程,
原图片
亮度调整。每个通道的数值-120,小于零=0,大于255则=255,用于突出LED灯带所在区域
二值化。将上图RGB,三个通道分离,R值减去G值,若大于25,则在二值图像中为255,否则为0.
二值化后可以看到断断续续的LED灯区域的轮廓
膨胀之后(下图),将轮廓较为平滑,连通区域变得粗重。
腐蚀操作后见下图,先膨胀在经腐蚀相当于闭运算,有去除空洞,使得轮廓变得平滑的功能
提取轮廓后
遍历所有轮廓,找出满足条件(轮廓要大于10个像素点)的轮廓,拟合成旋转矩形,然后在判断该旋转矩形的中心 5×5 的像素块(要从二值化之前的图像看)是否接近白色,如何是则说明可能是装甲LED灯带所在区域,将其添加至一个向量vEllipse中,为下一步定位装甲做准备。下图展示了这些旋转矩形
下一步是检测装甲的位置,在以上旋转矩形中,两两判断,根据下载原则定位装甲:
然后求出装甲的宽度和高度,高度取LED旋转矩形height的平均值,宽度等于两个旋转矩形的中心距离。最后标出装甲位置
//opencv2.4.x版本中的旋转矩形类
CvBox2D
Public Member Functions //成员函数
CvBox2D (CvPoint2D32f c=CvPoint2D32f(), CvSize2D32f s=CvSize2D32f(), float a=0)
CvBox2D (const cv::RotatedRect &rr)
operator cv::RotatedRect () const
Public Attributes //成员变量
float angle
CvPoint2D32f center
CvSize2D32f size
//opencv3.0以上版本中的旋转矩形类
RotatedRect
Public Member Functions //成员函数
RotatedRect () //无参数构造函数
RotatedRect (const Point2f ¢er, const Size2f &size, float angle)//构造函数
RotatedRect (const Point2f &point1, const Point2f &point2, const Point2f &point3)
Rect boundingRect () const//获取包围旋转矩形的最小的直立矩形
void points (Point2f pts[]) const//获取旋转矩形的四个定点坐标
Public Attributes //成员变量
float angle//旋转角度
Point2f center//中心坐标
Size2f size//尺寸
//
//opencv2.4.x版本中的通道分离函数
void cvSplit ( const CvArr * src,
CvArr * dst0,
CvArr * dst1,
CvArr * dst2,
CvArr * dst3
)
//opencv3.0以上版本中的通道分离函数
void cv::split ( const Mat & src,
Mat * mvbegin //输出的Mat数组
)
//opencv2.4.x版本中的膨胀函数
void cvDilate ( const CvArr * src,
CvArr * dst,
IplConvKernel * element = NULL,
int iterations = 1
)
//opencv3.0以上版本中的膨胀函数
void cv::dilate ( InputArray src,
OutputArray dst,
InputArray kernel,//膨胀所用的结构元,默认为3*3的矩形
Point anchor = Point(-1,-1),//结构元的中心
int iterations = 1,//膨胀操作的次数
int borderType = BORDER_CONSTANT,
const Scalar & borderValue = morphologyDefaultBorderValue()
)
//opencv2.4.x版本中的腐蚀函数
void cvErode ( const CvArr * src,
CvArr * dst,
IplConvKernel * element = NULL,
int iterations = 1
)
//opencv3.0以上版本中的腐蚀函数
void cv::erode ( InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor = Point(-1,-1),
int iterations = 1,
int borderType = BORDER_CONSTANT,
const Scalar & borderValue = morphologyDefaultBorderValue()
)
//opencv2.4.x版本中的提取轮廓函数
int cvFindContours ( CvArr * image,
CvMemStorage * storage,
CvSeq ** first_contour,
int header_size = sizeof(CvContour),
int mode = CV_RETR_LIST,
int method = CV_CHAIN_APPROX_SIMPLE,
CvPoint offset = cvPoint(0, 0)
)
//opencv3.0以上版本中的提取轮廓函数
void cv::findContours ( InputOutputArray image,
OutputArrayOfArrays contours,
OutputArray hierarchy,
int mode,
int method,
Point offset = Point()
)
//注意该函数会修改输入的二值化图像,必要时要做备份
//opencv2.4.x版本中的拟合旋转矩形函数
CvBox2D cvFitEllipse2 ( const CvArr * points )
//opencv3.0以上版本中的拟合旋转矩形函数
RotatedRect cv::fitEllipse ( InputArray points )
//opencv2.4.x版本中的获取指定位置处像素的函数
CvScalar cvGet2D ( const CvArr * arr,
int idx0,
int idx1
)