最近忙着做大作业没有写过东西,就把做的大作业整理一下贴出来吧。
背景简介
某城市远郊公共汽车运营公司发现司售人员上交的票款额与实际乘客人数严重不符,造成公司长年亏损。因此,该公司希望能开发一套可以实时清点乘客人数的系统,车上的乘客的数量进行统计最终与司售人员所缴款项进行比对看是否相符合,以达到监视的效果防止公司公共汽车公司财产损失。这是一个实际的问题有着客观的现实背景开发出一套经济实用的乘客检测系统将能帮助该公司避免损失,与此问题类似的实际问题还有很多,比如车站监控系统,认证系统等,因此该系统有着广阔的应用范围。
问题分析
分析该问题可以发现:基于图像的对车内乘客检测问题的本质是检测图像中某一区域是否存在满足人头部的特征,如果有这些特征则判断该区域有乘客,进行计数即可,若无这些特征则判断为该区域无乘客。归根到底是一个分类问题,且是两类问题,可以考虑使用模式识别中相关的分类的方法进行解决。
具体而言:乘客坐在车内座位上,从车顶上方向下拍照,由于大多数人的头发较黑,摄像头采集到的照片乘客头部处比较暗,另外乘客的头部轮廓较为独特,大多为圆形或者近似圆形,再者在摄像头位置较为固定的情况下乘客的头部大小较为固定,这些信息都可以用来对某一座位区域内是否含有乘客进行判断。方法探究
由于本人近期接触了一些关于Adaboost 相关的一些算法,所以开始就想使用adaboost算法来解决此问题。先做好了前期的准备工作:手工选取正样本(将乘客头部从图片中切割出来保存),选取负样本(车内杂物如车座,车窗,扶手等),训练分类器,由于正样本和负样本都比较少且差异不是特别明显,训练时一直进入死循环,不能产生最后想要的分类器,后来降低分类器阶数虽能跳出循环,产生分类器,但性能很差误检漏检非常多,所以排除此方案。
后又设想利用之前分析的一些特征有针对性的进行判断,对于本问题可利用的特征目前大致有:区域灰度值,区域面积,区域周长,区域的面积与周长之比。如果对每一个特征的取值范围进行限定,则可以排除干扰区域选择出满足条件的区域,即有乘客的区域。
方法简介
关于求区域周长和面积比较简单不再赘述,在此重点介绍一下面积与周长之比。
一般地,周长一定,面积有这样的关系:圆>正多边形>不规则多边形其中,正多边形边数越多,越接近圆,所以面积越大;正方形大于长方形;长方形中边长越接近面积越大。周长相等的平面图形中,圆的面积是最大的。
在此定义圆度概念:一个图形的面积与周长的比值除以与该图形等面积相等的圆的半径。
所以对于图像中的某一区域若圆度越接近0.5越可能是圆,因此可以利用此特征寻找区域内比较圆和饱满的区域。如下图所示右侧区域的圆度比左侧区域的圆度大且更接近于0. 5
本次作业采用VC++6.0和Opencv 1.0在Windows7系统下完成。
// convert_raw2Ipl.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "cv.h"
#include "highgui.h"
#define BYET unsigned char
#define fnum 50
#include "iostream"
#include "string.h"
#include "stdlib.h"
#include "stdio.h"
using namespace std;
void ImageBlendEnhance(IplImage *src, IplImage *dst);
int ImAdjust(IplImage* src, IplImage* dst);
int main(int argc, char* argv[])
{
//IplImage *mid_img;
IplConvKernel *element=0;//声明一个结构元素
int element_shape=CV_SHAPE_ELLIPSE ;//长方形形状的元素
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq* contour = 0;
element = cvCreateStructuringElementEx(3*2+1, 3*2+1,3,3,element_shape,0);//创建结构元素
CvMoments moments;
CvMat *region;
CvPoint pt1,pt2;
double m00 = 0, m10, m01, mu20, mu11, mu02, inv_m00;
double a, b, c;
int xc, yc;
long int area=0;
long int length=0;
float ratio=0.0;
float rad=0;
BYTE *image;
int width=0;
int height=0;
IplImage *mid_img;
FILE *fpImg;//创建文件指针
fpImg = fopen("F://Program Files//Microsoft Visual Studio//MyProjects//convert_raw2Ipl//data//28.raw", "rb");//打开文件
if( fpImg==0 ) //如果不成功输出消
{
printf("不能打开文件");
return 0;
}
fread( &width, sizeof(int), 1, fpImg);
fread( &height, sizeof(int), 1, fpImg);
image = new BYTE[width*height];
fread( image, sizeof(BYTE), width*height, fpImg);
fclose(fpImg);
CvSize wholesize,region2size;//整幅图的大小,区域2大小
wholesize.width=width;
wholesize.height=height;
region2size.width=150;
region2size.height=100;
mid_img=cvCreateImage(wholesize,8,1);
mid_img->imageData=(char*)image;//转换完成
IplImage* temp = cvCreateImage(region2size,8,1);
IplImage* dst = cvCreateImage(region2size,8,3);
int i,j;
for(i=0;i<temp->height;i++)
for(j=0;j<temp->width;j++)
{
((uchar*)(temp->imageData+i*temp->widthStep))[j]=
((uchar*)( mid_img->imageData+(i+30)*mid_img->widthStep))[j+55];
}
ImageBlendEnhance(temp, temp);//增强
ImAdjust(temp,temp);
cvEqualizeHist(temp,temp); //直方图均衡
cvEqualizeHist(temp,temp);
double ave=0.0;
int n=0;
for(i=0;i<temp->height;i++)
for(j=0;j<temp->width;j++)
{
ave+=((uchar*)(temp->imageData+i*temp->widthStep))[j];
n++;
}
ave=ave/n;
cvThreshold( temp, temp, ave*0.42, 255, CV_THRESH_BINARY );//二值化
//cvAdaptiveThreshold( temp, temp, 255, CV_ADAPTIVE_THRESH_MEAN_C,CV_THRESH_BINARY, 5, -9);
for(int k=0;k<3;k++)
{
cvDilate(temp,temp,element,1);//膨胀图像
cvErode(temp,temp,element,1);//腐蚀图像
}
cvNot( temp, temp ); //取反
cvZero( dst );
cvFindContours(
temp,
storage,
&contour,
sizeof(CvContour),
CV_RETR_TREE,
CV_CHAIN_APPROX_SIMPLE,
cvPoint(0,0)
);
//提取轮廓并保存到序列contour中
//轮廓
for( ; contour != 0; contour = contour->h_next )
{
CvScalar color = CV_RGB( 255, 255,255 ); //定义区域填充色为白色
cvDrawContours( dst, contour, color, color, -1, CV_FILLED, 8,cvPoint(0,0) );//画轮廓
contour = cvApproxPoly( contour, sizeof(CvContour), storage, CV_POLY_APPROX_DP, 3, 1 );
//用多边形逼近轮廓
area=fabs(cvContourArea(contour));//算面积
length= cvArcLength(contour);//算周长
rad=sqrt(area/3.14);//算等面积圆的半径
ratio =area/length;//算ratio
if((area>=370)&&(ratio>0.238*rad)&&(length<=600)&&(length>=110))//判决表达式
{ region=(CvMat*)contour;
cvMoments( region, &moments,0 );
/
//计算距,确定轮廓中心
m00 = moments.m00;
m10 = moments.m10;
m01 = moments.m01;
mu11 = moments.mu11;
mu20 = moments.mu20;
mu02 = moments.mu02;
inv_m00 = 1. / m00;
xc = cvRound( m10 * inv_m00 );
yc = cvRound( m01 * inv_m00 );
a = mu20 * inv_m00;
b = mu11 * inv_m00;
c = mu02 * inv_m00;
pt1.x=xc-1;pt1.y=yc;
pt2.x=xc+1;pt2.y=yc;
cvLine( dst, pt1, pt2, CV_RGB(0,255,0), 2, CV_AA, 0 ); //画出中心
pt1.x=xc;pt1.y=yc-1;
pt2.x=xc;pt2.y=yc+1;
cvLine( dst, pt1, pt2, CV_RGB(0,255,0), 2, CV_AA, 0 );//画出中心
}
}
CvScalar circle_color = CV_RGB( 0, 255,0 );
CvPoint center;
center.x=xc+55;
center.y=yc+30;
IplImage *Multi_channels_img=cvCreateImage(wholesize, IPL_DEPTH_8U, 3);
cvCvtColor(mid_img, Multi_channels_img, CV_GRAY2BGR);
cvCircle(Multi_channels_img,center,30,circle_color,2,8);
cvEqualizeHist(mid_img,mid_img);
cvNamedWindow("zhuan");
cvNamedWindow("cut");
cvNamedWindow("dst");
cvShowImage("dst",dst);
cvShowImage("zhuan",Multi_channels_img);
cvShowImage("cut",temp);
cvWaitKey(0);
cvReleaseImage(&temp);
cvReleaseImage(&mid_img);
return 0;
}
void ImageBlendEnhance(IplImage *src, IplImage *dst)
{
IplImage *gray=NULL;
gray=cvCreateImage(cvGetSize(src), 8, 1);
if (src->nChannels!=1)
cvCvtColor(src, gray, CV_BGR2GRAY);
else
cvCopy(src,gray);
IplImage *tempimg=NULL;
tempimg=cvCreateImage(cvGetSize(src), 8, 1);
//直接对比度增强
double maxpixel=-10;
int h=src->height;
int w=src->width;
int i, j;
for (i=0; i<h; i++)
{
for (j=0; j<w; j++)
{
CvScalar s;
s=cvGet2D(gray, i, j);
if (s.val[0]>maxpixel)
maxpixel=(double)s.val[0];
}
}
double sum=0.0;
double ave;//3*3窗口像素的均值
double c;//对比度
double c1;//非线性变换后的对比度
for (i=1; i<h-1; i++)
{
for (j=1; j<w-1; j++)
{
sum=0.0;
for (int m=-1; m<2; m++)
{
for (int n=-1; n<2; n++)
{
CvScalar pix;
pix=cvGet2D(gray, i+m, j+n);
sum+=pix.val[0];
}
}
ave=(double)sum/9;
CvScalar pixel;
pixel=cvGet2D(gray, i, j);
c=abs((pixel.val[0]-ave))/(pixel.val[0]+ave);
c1=4*c-6*pow(c,2)+4*pow(c,3)-pow(c,4);//求非线性变换后的对比度,c1的函数是经过试验得出的效果比较好处理函数,并不是唯一的
CvScalar s;
double temp;
if (pixel.val[0]<=ave)
{
temp=ave*(1-c1)/(1+c1);
s.val[0]=temp;
cvSet2D(tempimg, i, j, s);
}
else
{
temp=maxpixel-(maxpixel-ave)*(1-c1)/(1+c1);
s.val[0]=temp;
cvSet2D(tempimg, i, j, s);
}
}
}
}
int ImAdjust(IplImage* src, IplImage* dst)
{
double low1,high1,bottom1,top1;
int N[256];
double CN[256];
double low,high,gamma;
double val = 0;
double sum = 0;
int x,y,z;
bool flag;
gamma = 1;
low = 0.01;
high = 0.99;
bottom1 = 0;
top1 = 1;
flag = FALSE;
int k = 0,j = 0;
memset(N,0,256*4);
memset(CN,0,256*4);
for(y = 0; y < src->height; y++)
{
for (x = 0; x < src->width; x++)
{
val = ((uchar*)(src->imageData + src->widthStep*y))[x];
int i = (uchar)val;
N[i]++;
}
}
for (z = 0; z < 256; z++)
{
sum += N[z];
CN[z] = sum;
}
for (z =0; z < 256; z++)
{
CN[z] /= sum;
if (flag == false)
{
if(CN[z] >= 0.01)
{
k = z;
flag = true;
}
}
else
{
if (CN[z] >= 0.99)
{
j = z ;
break;
}
}
}
if(k == j)
{
low1 = 0;
high1 = 1;
}
else
{
low1 = k;
high1 = j;
}
double low2 = low1;
double high2 = high1;
double bottom2 = bottom1*255;
double top2 = top1*255;
double err_in = high2 - low2;
double err_out = top2 - bottom2;
// intensity transform
for( y = 0; y < src->height; y++)
{
for (x = 0; x < src->width; x++)
{
val = ((uchar*)(src->imageData + src->widthStep*y))[x];
val = pow((val - low2)/err_in, gamma) * err_out + bottom2;
val = val + 0.5;
if(val>255) val=255; if(val<0) val=0; // Make sure src is in the range [low,high]
((uchar*)(dst->imageData + dst->widthStep*y))[x] = (uchar) val;
}
}
return 0;
}