其实自己也没有完全搞懂,但是还是作为学习,记录下来吧
/**
创建一个IplImage
@param image 目标image
@returns 返回一个IplImage对象
*/
- (IplImage *)CreateIplImageFromUIImage:(UIImage *)image
{
//将image转为CGImage
CGImageRef imageRef = image.CGImage;
//可以获取设备无关的RGB颜色空间,这个颜色空间需要调用CGColorSpaceRelease()进行释放。
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
//创建一个IplImage
IplImage *iplimage = cvCreateImage(cvSize(image.size.width, image.size.height), IPL_DEPTH_8U, 4);
/**
CGBitmapContextCreate 创建图形设备上下文的缓存
CGContextRef CGBitmapContextCreate(void *data, size_t width,size_t height, size_t bitsPerComponent, size_t bytesPerRow,CGColorSpaceRef space, CGBitmapInfo bitmapInfo)
参数data指向绘图操作被渲染的内存区域,这个内存区域大小应该为(bytesPerRow*height)个字节。如果对绘制操作被渲染的内存区域并无特别的要求,那么可以传递NULL给参数data。
参数width代表被渲染内存区域的宽度。
参数height代表被渲染内存区域的高度。
参数bitsPerComponent被渲染内存区域中组件在屏幕每个像素点上需要使用的bits位,举例来说,如果使用32-bit像素和RGB颜色格式,那么RGBA颜色格式中每个组件在屏幕每个像素点上需要使用的bits位就为32/4=8。
参数bytesPerRow代表被渲染内存区域中每行所使用的bytes位数。
参数colorspace用于被渲染内存区域的“位图上下文”。
参数bitmapInfo指定被渲染内存区域的“视图”是否包含一个alpha(透视)通道以及每个像素相应的位置,除此之外还可以指定组件式是浮点值还是整数值。
*/
CGContextRef contextRef = CGBitmapContextCreate(iplimage->imageData, iplimage->width, iplimage->height,
iplimage->depth, iplimage->widthStep,
colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);
//将图像画在图形上
CGContextDrawImage(contextRef, CGRectMake(0, 0, image.size.width, image.size.height), imageRef);
//释放
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace);
IplImage *ret = cvCreateImage(cvGetSize(iplimage), IPL_DEPTH_8U, 3);
//颜色空间转换函数,可以实现rgb颜色向HSV,HSI等颜色空间的转换,也可以转换为灰度图像。
cvCvtColor(iplimage, ret, CV_RGBA2BGR);
//释放
cvReleaseImage(&iplimage);
return ret;
}
/**
人脸识别的方法
@param overlayImage 覆盖在人脸上的image
@param img 目标image
@returns 返回一个合成好的image
*/
- (UIImage *) opencvFaceDetect:(UIImage *)overlayImage WithImg:(UIImage *)img
{
if(img) {
//设置错误类型
cvSetErrMode(CV_ErrModeParent);
//创建一个IplImage对象
IplImage *image = [self CreateIplImageFromUIImage:img];
// Scaling down
IplImage *small_image = cvCreateImage(cvSize(image->width/2,image->height/2), IPL_DEPTH_8U, 3);
//输入图像向下采样
cvPyrDown(image, small_image, CV_GAUSSIAN_5x5);
int scale = 2;
// Load XML
NSString *path = [[NSBundle mainBundle] pathForResource:@"haarcascade_frontalface_default" ofType:@"xml"];
//读取 CvHaarClassifierCascade 分类器文件进行人脸检测
CvHaarClassifierCascade* cascade = (CvHaarClassifierCascade*)cvLoad([path cStringUsingEncoding:NSASCIIStringEncoding], NULL, NULL, NULL);
//用来创建一个内存存储器,来统一管理各种动态对象的内存,比如说序列,这个函数返回一个新创建的内存存储器指针。
CvMemStorage* storage = cvCreateMemStorage(0);
// Detect faces and draw rectangle on them
//人脸检测
/*
CVAPI(CvSeq*) cvHaarDetectObjects( const CvArr* image,CvHaarClassifierCascade* cascade,CvMemStorage* storage, double scale_factor CV_DEFAULT(1.1),int min_neighbors CV_DEFAULT(3), int flags CV_DEFAULT(0),CvSize min_size CV_DEFAULT(cvSize(0,0)), CvSize max_size CV_DEFAULT(cvSize(0,0)));
image 被检图像
cascade haar 分类器级联的内部标识形式
storage 用来存储检测到的一序列候选目标矩形框的内存区域。
scale_factor 在前后两次相继的扫描中,搜索窗口的比例系数。例如1.1指将搜索窗口依次扩大10%
min_neighbors 构成检测目标的相邻矩形的最小个数(缺省-1)。如果组成检测目标的小矩形的个数和小于min_neighbors-1 都会被排除。如果min_neighbors 为 0, 则函数不做任何操作就返回所有的被检候选矩形框,这种设定值一般用在用户自定义对检测结果的组合程序上。
flags 操作方式。当前唯一可以定义的操作方式是 CV_HAAR_DO_CANNY_PRUNING。如果被设定,函数利用Canny边缘检测器来排除一些边缘很少或者很多的图像区域,因为这样的区域一般不含被检目标。人脸检测中通过设定阈值使用了这种方法,并因此提高了检测速度。
min_size 检测窗口的最小尺寸。缺省的情况下被设为分类器训练时采用的样本尺寸(人脸检测中缺省大小是~20×20)。
*/
CvSeq* faces = cvHaarDetectObjects(small_image, cascade, storage, 1.2f, 2, CV_HAAR_DO_CANNY_PRUNING, cvSize(0,0), cvSize(20, 20));
cvReleaseImage(&small_image);
// Create canvas to show the results
//创建结果的image
CGImageRef imageRef = img.CGImage;
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef contextRef = CGBitmapContextCreate(NULL, img.size.width, img.size.height,
8, img.size.width * 4,
colorSpace, kCGImageAlphaPremultipliedLast|kCGBitmapByteOrderDefault);
CGContextDrawImage(contextRef, CGRectMake(0, 0, img.size.width, img.size.height), imageRef);
CGContextSetLineWidth(contextRef, 4);
CGContextSetRGBStrokeColor(contextRef, 0.0, 0.0, 1.0, 0.5);//设置颜色,蓝色,透明度0.5
// Draw results on the iamge
for(int i = 0; i < faces->total; i++) {
// Calc the rect of faces
//cvGetSeqElem : 返回索引指定的元素指针
//CvRect : 通过方形左上角坐标和方形的高和宽来确定一个矩形区域
CvRect cvrect = *(CvRect*)cvGetSeqElem(faces, i);
//返回一个矩形,从用户空间坐标转换为设备坐标空间。
CGRect face_rect = CGContextConvertRectToDeviceSpace(contextRef, CGRectMake(cvrect.x * scale, cvrect.y * scale, cvrect.width * scale, cvrect.height * scale));
//覆盖图像
if(overlayImage) {
CGContextDrawImage(contextRef, face_rect, overlayImage.CGImage);
} else {
CGContextStrokeRect(contextRef, face_rect);
}
}
img = [UIImage imageWithCGImage:CGBitmapContextCreateImage(contextRef)];
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace);
cvReleaseMemStorage(&storage);
cvReleaseHaarClassifierCascade(&cascade);
}
NSLog(@"%@",@"opencv image ok~~~");
return img;
}
- <feature>
- <rects>
<_>6 4 12 9 -1.</_>
//矩阵。前四个数值是矩阵四个点的位置,最后一个数值是矩阵像素和的权值
<_>6 7 12 3 3.</_>
//矩阵。前四个数值是矩阵四个点的位置,最后一个是像素和的权值,这样两个矩阵就形成了一个Haar特征
</rects>
<tilted>0</tilted> //是否是倾斜的Haar特征
</feature>
<threshold>-0.0315119996666908</threshold> //阈值
<left_val>2.0875380039215088</left_val> //小于阈值时取左值
<right_val>-2.2172100543975830</right_val> //大于阈值时取右值
结果是这样的
最后,再附上判断人脸的xml的一些解释,具体得可以看我下面链接的文章
参考文章:cvCvtColor 解析iPhone屏幕双缓冲技术 Adaboost原理、算法以及应用 CvMemStorage动态内存存储及操作函数 cvHaarDetectObjects