主要思想来自颜色分类论文《Goal Evaluation of Segmentation Algorithms for Traffic Sign Recognition》
RGB空间是通常的初始的空间,如果简单的分割过程是其目的,使用RGB空间是比较好的选择。然而,三个颜色之间的高相关性组件和光照变化颜色的效果信息,使得它很难找到正确的阈值。在这个空间上,一个解决办法是使用RGB的相对于归一化,即除以R + G+ B。
这种方式,阈值在这个空间里很容易地找到。然而,出现了一些问题:这个标准化的空间,因为照度低(低RGB值),该转换是不稳定的,并在接近零的值,噪声被放大。
检测所用的阈值如下
其中ThR=0.4;ThG=0.3,ThB=0.4,thy=0.85
程序代码为
// 交通标志检测.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "stdafx.h"
#include "cv.h"
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <iostream>//这里不要.h!
#include <string>
#include<windows.h>
using namespace std;
int ifred=0;
int ifgreen=0;
int ifblue=0;
int whetherRed(int rData,int gData,int bData,int T);
int whetherBlue(int rData,int gData,int bData,int T);
int whetherYellow(int rData,int gData,int bData,int T);
int rednum=0;
int bluenum=0;
int yellownum=0;
float rij,bij,gij;
CvRect processImg(IplImage* img);//函数初始化
int main( )
{
cvNamedWindow( "window1", 1 ); //创建窗口
cvNamedWindow( "window2", 1 ); //创建窗口
IplImage* pImg; //声明IplImage指针
pImg = cvLoadImage( "P6250063.TIF"/*argv[1]*/, 1);//需要检测的图片名称
IplImage *RChannel,*GChannel,*BChannel,*tempImg,*middleImg;
CvSize Size1;//OpenCV的基本数据类型之一。表示矩阵框大小,以像素为精度。与CvPoint结构类似,但数据成员是integer类型的width和height。
Size1 = cvGetSize(pImg);//OpenCV提供的一种操作矩阵图像的函数。得到二维的数组的尺寸,以CvSize返回。
RChannel = cvCreateImage(Size1, IPL_DEPTH_8U, 1);//创建头并分配数据,IPL_DEPTH_8U - 无符号8位整型
GChannel = cvCreateImage(Size1, IPL_DEPTH_8U, 1);
BChannel = cvCreateImage(Size1, IPL_DEPTH_8U, 1);
cvSplit(pImg, BChannel, GChannel, RChannel, NULL);//分割多通道数组成几个单通道数组或者从数组中提取一个通道
tempImg = cvCreateImage(Size1, IPL_DEPTH_8U, 1);//CreateImage创建头并分配数据
CvMat mat = cvMat(Size1.height,Size1.width,IPL_DEPTH_8U,NULL);//CvMat 多通道矩阵
CvMat mat2 = cvMat(Size1.height,Size1.width,IPL_DEPTH_8U,NULL);
CvMat mat3 = cvMat(Size1.height,Size1.width,IPL_DEPTH_8U,NULL);
CvMat* Rmat;//指针
CvMat* Gmat;
CvMat* Bmat;
int rData;
int gData;
int bData;
int temp=0;
CvRect resultRect;//矩形框的偏移和大小
Rmat = cvGetMat(RChannel, &mat, 0, 0);//转换成向量
Gmat = cvGetMat(GChannel, &mat2, 0, 0);
Bmat = cvGetMat(BChannel, &mat3, 0, 0);
for (int i = 0;i<pImg->height;i++)
{
for (int j = 0;j<pImg->width;j++)
{ int ifred=0;//重新赋值!坑爹啊!
int ifgreen=0;
int ifyellow=0;
int temp=0;
rData = (int)CV_MAT_ELEM(*Rmat, uchar, i, j);//opencv中用来访问矩阵每个元素的宏,这个宏只对单通道矩阵有效,多通道会报错
gData = (int)CV_MAT_ELEM(*Gmat, uchar, i, j);
bData = (int)CV_MAT_ELEM(*Bmat, uchar, i, j);
float sum=rData+gData+bData;
float rr=rData;
float gg=gData;
float bb=bData;
rij=rData/sum;
gij=gData/sum;
bij=bData/sum;
ifred=whetherRed(rData,gData,bData,50);
ifblue=whetherBlue(rData,gData,bData,50);
ifyellow=whetherYellow(rData,gData,bData,50);
if((ifred==255)||(ifblue==255)||(ifyellow==255))
{
temp=255;
}
cvSet2D(&mat,i,j,cvScalar(temp));//Set*D修改指定的数组 cvSet2D 给某个点赋值。cvGet2D 获得某个点的值, idx0=hight 行值, idx1=width 列值。函数返回的是一个CvScalar 容器,其参数中也有两个方向的坐标;CvScalar定义可存放1—4个数值的数值,
}
}
middleImg = cvGetImage(&mat,tempImg);//GetImage从不确定数组返回图像头
cvShowImage( "window2", middleImg ); //显示图像
resultRect = processImg(middleImg);
if((rednum>=yellownum)&&(rednum>=bluenum))
cout<<"检测到红色标志!"<<endl;
if((bluenum>=yellownum)&&(bluenum>=rednum))
cout<<"检测到蓝色标志!"<<endl;
if((yellownum>=bluenum)&&(yellownum>=rednum))
cout<<"检测到黄色标志!"<<endl;
cvRectangle( pImg, cvPoint(resultRect.x,resultRect.y), cvPoint(resultRect.x + resultRect.width,resultRect.y + resultRect.height), CV_RGB(0,255,0),1, 8, 0 );//Rectangle绘制简单、指定粗细或者带填充的 矩形
cvShowImage( "window1", pImg ); //显示图像
cvWaitKey(0); //等待按键
cvDestroyWindow( "window1" );//销毁窗口
cvDestroyWindow( "window2" );//销毁窗口
cvReleaseImage( &pImg ); //释放图像
return 0;
}
int whetherRed(int rData,int gData,int bData,int T)
{
//int deltaRG, deltaRB;
//deltaRG = rData - gData;
//deltaRB = rData - bData;
if ((rij >= 0.4)&&(gij <= 0.3))
{
rednum++;
return 255;
}
//return 0;
}
int whetherYellow(int rData,int gData,int bData,int T)
{
//int deltaGR, deltaGB;
//deltaGR = gData - rData;
//deltaGB = gData - bData;
if ((rij+gij)>=0.85)
{
yellownum++;
return 255;
}
//return 0;
}
int whetherBlue(int rData,int gData,int bData,int T)
{
//int deltaBR, deltaBG;
//deltaBR = bData - rData;
//deltaBG = bData - gData;
if (bij>=0.4 )
{
bluenum++;
return 255;
}
//return 0;
}
CvRect processImg(IplImage* img)
{
CvMemStorage *stor = 0;//内存存储器是一个可用来存储诸如序列,轮廓,图形,子划分等动态增长数据结构的底层结构。它是由一系列以同等大小的内存块构成,呈列表型
CvSeq * cont = NULL,*conttmp = NULL;//CvSeq可动态增长元素序列
CvSeq * contMax=NULL;
int Number_Object =0;
int contour_area_tmp = 0;
int contour_area_max = 0;
CvRect resultRect = {0,0,0,0};
cvSmooth(img,img,CV_MEDIAN,3);//各种方法的图像平滑
stor = cvCreateMemStorage(0);//存储块的大小以字节表示。如果大小是 0 byte, 则将该块设置成默认值
cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), stor); //存储面积最大轮廓数组
contMax = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), stor);
Number_Object = cvFindContours( img, stor, &cont, sizeof(CvContour), CV_RETR_EXTERNAL , CV_CHAIN_APPROX_NONE , cvPoint(0,0) );//在二值图像中寻找轮廓
conttmp = cont;
bool check = TRUE;//bool是布尔型变量,也就是逻辑型变量的定义符,类似于float,double等
CvPoint* pointTmp;//二维坐标系下的点,类型为整型
int mostLeft = img->width - 1,mostRight = 0,mostHigh = img->height-1,mostlow=0;
for(;cont;cont = cont->h_next)//h_next???????
{
if (CV_IS_SEQ_POLYGON( cont ))//POLYGON多边形;多角形物体
{
for (int i = 0; cvGetSeqElem(cont, i)!=0;i++)//返回索引所指定的元素指针
{
pointTmp = (CvPoint*)cvGetSeqElem(cont, i);
if (pointTmp->y < 2||pointTmp->x < 2||pointTmp->y > img->height-3||pointTmp->x > img->width)
{
check = FALSE;break;
}
}
}
if (check)
{
contour_area_tmp = (int)fabs(cvContourArea( cont, CV_WHOLE_SEQ )); //计算整个轮廓或部分轮廓的面积
if(contour_area_tmp > contour_area_max)
{
contour_area_max = contour_area_tmp;
contMax = cont;//把最大面积的轮廓赋值给conmax
}
}
}
if (conttmp&&contMax)
{
if (CV_IS_SEQ_POLYGON( contMax ))
{
for (int i = 0; cvGetSeqElem(contMax, i)!=0;i++)
{
pointTmp = (CvPoint*)cvGetSeqElem(contMax, i);
if (pointTmp->x < mostLeft)
{
mostLeft = pointTmp->x;
}
if (pointTmp->x > mostRight)
{
mostRight = pointTmp->x;
}
if (pointTmp->y < mostHigh)
{
mostHigh = pointTmp->y;
}
if(pointTmp->y>mostlow)
{
mostlow=pointTmp->y;
}
}
resultRect.x = mostLeft;
resultRect.y = mostHigh;
resultRect.width = mostRight - mostLeft;
resultRect.height= mostlow - mostHigh;
}
}
return resultRect;
}