想做个去水印的APP,第一个想到的就是CV里的inpaint图像修复技术。就想着把CV框架放在IOS中用,由于第一次接触IOS的开发,就看了两本实习时候导师大神推荐的书,很多东西都不太了解,虽然CV官网给了IOS的frame框架但做的过程中还是遇到不少坑,并且网上关于在IOS下使用OpenCV进行图像处理的资料比较少,所以就记录下做的过程中爬过的一些坑,一来由于经常搬CSND的砖,也算为D友们做点贡献。二来也是记录下自己,不然一段时间又全忘完了。由于是互联网小白纸,基本上都是靠着优快云和GitHub各种搬砖,有写错的地方还烦请各位大佬指出来便于成长,感激不尽,话不多说上硬货。
一、OpenCV框架集成
方法1,在OpenCV官网的下载IOS pack,直接将framework拖入项目;
方法2,使用CocoaPods直接集成OpenCV。
两种方法都可以顺利集成OpenCV框架,但在编译的时候遇到了第一个坑!
坑:提示了一堆文件not found
解决方法:opencv需要执行c++编译,xcode的.m文件并不支持相应的编译条件,需要将所有直接或者间接用到opencv头文件的所有.m文件变成.mm文件后缀,告诉编译器是oc++文件执行编译。
这个坑如果不处理好是很坑的...因为所有直接或者间接用到opencv头文件的.m文件都得改文件后缀,就会形成连锁反应导致所有的.m文件都得改掉,所以正确的方法应该是将仅利用opencv的实现.mm文件进行封装,仅保留.h的接口提供外部使用。
二、图像格式转换
OpenCV的图像通用格式是Mat类型,而在IOS中经常直接使用的图像类型是UIImage,这就牵扯到图像类型转换的问题,对于类型转换官网给了直接的Demo可以使用:
-(cv::Mat)CVMat:(UIImage*)imageSrc
{
CGColorSpaceRef colorSpace = CGImageGetColorSpace(imageSrc.CGImage);
CGFloat cols = imageSrc.size.width;
CGFloat rows = imageSrc.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), imageSrc.CGImage);
CGContextRelease(contextRef);
return cvMat;
}
-(UIImage *)UIImageFromCVMat:(cv::Mat)cvMat
{
NSData *data = [NSData dataWithBytes:cvMat.data length:cvMat.elemSize()*cvMat.total()];
CGColorSpaceRef colorSpace;
if (cvMat.elemSize() == 1) {
colorSpace = CGColorSpaceCreateDeviceGray();
} else {
colorSpace = CGColorSpaceCreateDeviceRGB();
}
CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
// Creating CGImage from cv::Mat
CGImageRef imageRef = CGImageCreate(cvMat.cols, //width
cvMat.rows, //height
8, //bits per component
8 * cvMat.elemSize(), //bits per pixel
cvMat.step[0], //bytesPerRow
colorSpace, //colorspace
kCGImageAlphaNone|kCGBitmapByteOrderDefault,// bitmap info
provider, //CGDataProviderRef
NULL, //decode
false, //should interpolate
kCGRenderingIntentDefault //intent
);
UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CGDataProviderRelease(provider);
CGColorSpaceRelease(colorSpace);
return finalImage;
}
但在这里使用时就遇到了第二个坑!直接使用这两个函数对一个UIImage进行处理,即先用(cv::Mat)CVMat:(UIImage*)imageSrc转换成Mat,再用(UIImage *)UIImageFromCVMat:(cv::Mat)cvMat,你会发现转换后的图像显示时跟转换前的图像方向发生了变化。分析后发现因为转换函数使用了CGImage进行类型转换,而IOS中的CGImage的坐标系跟UIImage的坐标系不一致导致的,所以可以在执行转换成Mat的函数里先根据图像方向进行旋转校正,校正函数如下:
- (UIImage *)fixOrientation {
// No-op if the orientation is already correct
if (self.imageOrientation == UIImageOrientationUp) return self;
// We need to calculate the proper transformation to make the image upright.
// We do it in 2 steps: Rotate if Left/Right/Down, and then flip if Mirrored.
CGAffineTransform transform = CGAffineTransformIdentity;
switch (self.imageOrientation) {
case UIImageOrientationDown:
case UIImageOrientationDownMirrored:
transform = CGAffineTransformTranslate(transform, self.size.width, self.size.height);
transform = CGAffineTransformRotate(transform, M_PI);
break;
case UIImageOrientationLeft:
case UIImageOrientationLeftMirrored:
transform = CGAffineTransformTranslate(transform, self