IOS 用openCv实现简单的扣人像的
最近要实现人像扣图的功能,我在网上查到的扣图的方式主要有两种,一种是coreImage 色域,一种是openCv边缘检测
//coreImage 扣图 createCubeMap(值1,值2)值范围0~360 扣掉值1到值2范围内的颜色
CubeMap myCube = createCubeMap(self.slider1.value, self.slider2.value);
NSData *myData = [[NSData alloc]initWithBytesNoCopy:myCube.data length:myCube.length freeWhenDone:true];
CIFilter *colorCubeFilter = [CIFilter filterWithName:@"CIColorCube"];
[colorCubeFilter setValue:[NSNumber numberWithFloat:myCube.dimension] forKey:@"inputCubeDimension"];
[colorCubeFilter setValue:myData forKey:@"inputCubeData"];
[colorCubeFilter setValue:[CIImage imageWithCGImage:_preview.image.CGImage] forKey:kCIInputImageKey];
CIImage *outputImage = colorCubeFilter.outputImage;
CIFilter *sourceOverCompositingFilter = [CIFilter filterWithName:@"CISourceOverCompositing"];
[sourceOverCompositingFilter setValue:outputImage forKey:kCIInputImageKey];
[sourceOverCompositingFilter setValue:[CIImage imageWithCGImage:backgroundImage.CGImage] forKey:kCIInputBackgroundImageKey];
outputImage = sourceOverCompositingFilter.outputImage;
CGImage *cgImage = [[CIContext contextWithOptions: nil]createCGImage:outputImage fromRect:outputImage.extent];
头文件导入下面几个
#import <opencv2/opencv.hpp>
#import "UIImage+OpenCV.h"
UIImage+OpenCV类
//
// UIImage+OpenCV.h
#import <UIKit/UIKit.h>
#import <opencv2/opencv.hpp>
@interface UIImage (UIImage_OpenCV)
+(UIImage *)imageWithCVMat:(constcv::Mat&)cvMat;
-(id)initWithCVMat:(constcv::Mat&)cvMat;
@property(nonatomic,readonly) cv::Mat CVMat;
@property(nonatomic,readonly) cv::Mat CVGrayscaleMat;
@end
//
// UIImage+OpenCV.mm
#import "UIImage+OpenCV.h"
staticvoid ProviderReleaseDataNOP(void *info,const void *data,size_t size)
{
// Do not release memory
return;
}
@implementation UIImage (UIImage_OpenCV)
-(cv::Mat)CVMat
{
CGColorSpaceRef colorSpace =CGImageGetColorSpace(self.CGImage);
CGFloat cols =self.size.width;
CGFloat rows =self.size.height;
cv::Mat cvMat(rows, cols,CV_8UC4); // 8 bits per component, 4 channels
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNoneSkipLast |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), self.CGImage);
CGContextRelease(contextRef);
return cvMat;
}
-(cv::Mat)CVGrayscaleMat
{
CGColorSpaceRef colorSpace =CGColorSpaceCreateDeviceGray();
CGFloat cols =self.size.width;
CGFloat rows =self.size.height;
cv::Mat cvMat =cv::Mat(rows, cols,CV_8UC1); // 8 bits per component, 1 channel
CGContextRef contextRef = CGBitmapContextCreate(cvMat.data, // Pointer to backing data
cols, // Width of bitmap
rows, // Height of bitmap
8, // Bits per component
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNone |
kCGBitmapByteOrderDefault); // Bitmap info flags
CGContextDrawImage(contextRef, CGRectMake(0, 0, cols, rows), self.CGImage);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpace);
return cvMat;
}
+ (UIImage *)imageWithCVMat:(constcv::Mat&)cvMat
{
return [[[UIImagealloc] initWithCVMat:cvMat]autorelease];
}
- (id)initWithCVMat:(constcv::Mat&)cvMat
{
NSData *data = [NSDatadataWithBytes:cvMat.datalength:cvMat.elemSize() * cvMat.total()];
CGColorSpaceRef colorSpace;
if (cvMat.elemSize() == 1)
{
colorSpace = CGColorSpaceCreateDeviceGray();
}
else
{
colorSpace = CGColorSpaceCreateDeviceRGB();
}
CGDataProviderRef provider =CGDataProviderCreateWithCFData((CFDataRef)data);
CGImageRef imageRef = CGImageCreate(cvMat.cols, // Width
cvMat.rows, // Height
8, // Bits per component
8 * cvMat.elemSize(), // Bits per pixel
cvMat.step[0], // Bytes per row
colorSpace, // Colorspace
kCGImageAlphaNone | kCGBitmapByteOrderDefault, // Bitmap info flags
provider, // CGDataProviderRef
NULL, // Decode
false, // Should interpolate
kCGRenderingIntentDefault); // Intent
self = [selfinitWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return self;
}
@end
好了,上面的都是准备工作,具体的代码其实很简单
cv::Mat grayFrame,_lastFrame, mask,bgModel,fgModel;
_lastFrame = [self.preview.imageCVMat];
cv::cvtColor(_lastFrame, grayFrame,cv::COLOR_RGBA2BGR);//转换成三通道bgr
cv::Rect rectangle(1,1,grayFrame.cols-2,grayFrame.rows -2);//检测的范围
//分割图像
cv::grabCut(grayFrame, mask, rectangle, bgModel, fgModel, 3,cv::GC_INIT_WITH_RECT);//openCv强大的扣图功能
int nrow = grayFrame.rows;
int ncol = grayFrame.cols * grayFrame.channels();
for(int j=0; j<nrow; j++){//扣图,不知道这样写对不对,我也是新手,请大家多多指教
for(int i=0; i<ncol; i++){
uchar val = mask.at<uchar>(j,i);
if(val==cv::GC_PR_BGD){
grayFrame.at<cv::Vec3b>(j,i)[0]= ‘\255‘;
grayFrame.at<cv::Vec3b>(j,i)[1]= ‘\255‘;
grayFrame.at<cv::Vec3b>(j,i)[2]= ‘\255‘;
}
}
}
cv::cvtColor(grayFrame, grayFrame,cv::COLOR_BGR2RGB); //转换成彩色图片
_preview.image = [[UIImagealloc] initWithCVMat:grayFrame];//显示结果
上面的代码测试可用,其实这里最关键的代码是使用了opencv的grabCut 图像分割函数
grabCut函数的API说明如下:
void cv::grabCut( InputArray _img, InputOutputArray _mask, Rect rect,
InputOutputArray _bgdModel, InputOutputArray _fgdModel,
int iterCount, int mode )
/*
****参数说明:
img——待分割的源图像,必须是8位3通道(CV_8UC3)图像,在处理的过程中不会被修改;
mask——掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;在执行分割的时候,也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果。mask只能取以下四种值:
GCD_BGD(=0),背景;
GCD_FGD(=1),前景;
GCD_PR_BGD(=2),可能的背景;
GCD_PR_FGD(=3),可能的前景。
如果没有手工标记GCD_BGD或者GCD_FGD,那么结果只会有GCD_PR_BGD或GCD_PR_FGD;
rect——用于限定需要进行分割的图像范围,只有该矩形窗口内的图像部分才被处理;
bgdModel——背景模型,如果为null,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;
fgdModel——前景模型,如果为null,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;
iterCount——迭代次数,必须大于0;
mode——用于指示grabCut函数进行什么操作,可选的值有:
GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;
GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut;
GC_EVAL(=2),执行分割。
*/
参考文章:http://blog.csdn.net/zouxy09/article/details/8534954
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。