图像处理方面的一些探索

最近有个需求好比手机拍照制做证件照

若是不用第三方提供的收费服务好比face++的抠图,想到的大体步骤应该是ios

1:找一个白色的背景墙,衣服最好和背景墙区分开来 2:而后扣出来 3:最后和其余底色或者图片融合到一块儿git

一下就想到了OpenCV 这个处理图片的框架,由于里面内置了不少成熟的算法

iOS方面安装方法如今能够用Pods 进来,值得注意的是因为须要C++ 代码和iOS混编,因此当前文件改成.mm后缀,并引入相应的头文件例如:github

#import <opencv2/opencv.hpp>
#import <opencv2/imgcodecs/ios.h>
复制代码

抠图OpenCV有相应的算法用到的是grabCut方法大体步骤是:

1:基于交互式界面由用户选择前景区域; 2:定义一个单通道的输出掩码,0为背景,1为前景,2为可能的背景,3为可能的前景; 3:grabCut抠图;将输出结果与可能的前景做比较获得可能的前景; 4:定义三通道的结果图像; 5:从原图中拷贝可能的前景到结果图像;算法

API函数

grabCut( InputArray img, InputOutputArray mask, Rect rect,
                           InputOutputArray bgdModel, InputOutputArray fgdModel,
                           int iterCount, int mode = GC_EVAL );
复制代码

解释:

img:输入原图像;bash

mask:输出掩码;框架

rect:用户选择的前景矩形区域;ide

bgModel:输出背景图像;函数

fgModel:输出前景图像;post

iterCount:迭代次数;ui

知道大体步骤开始干,因为C++ image 和 iOS image表示方法不同,因此大体有下面两个转化方法

-(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
                                        );
    
    
    // Getting UIImage from CGImage
    UIImage *finalImage = [UIImage imageWithCGImage:imageRef];
    CGImageRelease(imageRef);
    CGDataProviderRelease(provider);
    CGColorSpaceRelease(colorSpace);
    
    return finalImage;
}
复制代码
- (cv::Mat)cvMatFromUIImage:(UIImage *)image
{
    CGColorSpaceRef colorSpace = CGImageGetColorSpace(image.CGImage);
    CGFloat cols = image.size.width;
    CGFloat rows = image.size.height;
    
    cv::Mat cvMat(rows, cols, CV_8UC4); // 8 bits per component, 4 channels (color channels + alpha)
    
    CGContextRef contextRef = CGBitmapContextCreate(cvMat.data,                 // Pointer to  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), image.CGImage);
    CGContextRelease(contextRef);
    
    return cvMat;
}
复制代码

注意:网上找的一些其余平台的写法在iOS 中要加上cv:: 前缀,好比变量的声明或者方法的调用。

主要的方法:

-(UIImage*) doGrabCutWithMask:(UIImage*)sourceImage maskImage:(UIImage*)maskImage iterationCount:(int) iterCount{

    cv::Mat img=[self cvMatFromUIImage:sourceImage];
    cv::cvtColor(img , img , CV_RGBA2RGB);
    
    cv::Mat1b markers=[self cvMatMaskerFromUIImage:maskImage];
    cv::Rect rectangle(0,0,0,0);
    // GrabCut segmentation
    cv::grabCut(img, markers, rectangle, bgModel, fgModel, iterCount, cv::GC_INIT_WITH_MASK);
    
    cv::Mat tempMask;
    cv::compare(mask,cv::GC_PR_FGD,tempMask,cv::CMP_EQ);
    // Generate output image
    cv::Mat foreground(img.size(),CV_8UC3,
                       cv::Scalar(255,255,255));
    
    tempMask=tempMask&1;
    img.copyTo(foreground, tempMask);
    
    
    UIImage* resultImage=[self UIImageFromCVMat:foreground];
    
    
    return resultImage;
}
复制代码

解释:

1:从iOS平台导入image转化为C++ 的结构 2:调用grabCut抠图 3:比较mask的值为可能的前景像素才输出到mask中 4:产生输出图像 5:将原图区域copy到foreground中 6:转化为iOS平台的image结构

看效果

处理前
处理后

总结

因为背景色和前景色有明显对比因此看着有锯齿的边缘,解决思路是把当前图像和背景图融合的时候边缘作腐蚀加高斯模糊处理。 总结下上述作法适合比较规范的拍照姿式,若是不规范就会很难看,因此打算放弃这个思路。

后来忽然发现一个提供制做证件的API,可是返回的图像是有水印的,这是我想直接把水印去掉不就OK了吗?

仍是用OpenCV这个框架用了里面的inpaint修复算法。

大体步骤:

1:标定噪声的特征,使用cv2.inRange二值化标识噪声对图片进行二值化处理,具体代码:cv2.inRange(img, np.array([240, 240, 240]), np.array([255, 255, 255])),把[240, 240, 240]~[255, 255, 255]之外的颜色处理为0; 2:使用inpaint方法,把噪声的mask做为参数,推理并修复图片;

代码以下:

cv::Mat img=[self cvMatFromUIImage:[UIImage imageNamed:@"test.jpg"]];
    cv::cvtColor(img , img , CV_RGBA2RGB);
//    UIImage *testImage = [UIImage imageNamed:@"test.jpg"];
//    UIImage *reslutsImage = [testImage WaterMarkDelete:CGRectMake(0, 0, 320, 320)];

//
//
//     //获取mask
//
//    cv::Mat mask;
//
//    cv::inRange(img, cv::Scalar(0, 0, 250), cv::Scalar(0, 0, 255), mask);
//
//
//
//    // 修复
//
//    cv::Mat dst;
//
//    cv::inpaint(img, mask, dst, 3, CV_INPAINT_TELEA);
//
//   UIImage *resultImage =[self UIImageFromCVMat:dst];
//   UIImageWriteToSavedPhotosAlbum(resultImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
//
    
    cv::Mat wm; // 水印文字


    cv::inRange(img, cv::Scalar(204, 115, 122), cv::Scalar(246, 103, 115), wm);
    
    UIImage *wmImage =[self UIImageFromCVMat:wm];
    UIImageWriteToSavedPhotosAlbum(wmImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);
//
    // 形态学操做

    cv::Mat kernel = getStructuringElement(MORPH_RECT, cv::Size(3, 3), cv::Point(-1, -1));

    morphologyEx(wm, wm, MORPH_DILATE, kernel, cv::Point(-1, -1), 2);



    // 去水印结果

    cv::Mat tywwm;

    inpaint(img, wm, tywwm, 3, CV_INPAINT_NS);
       UIImage *resultImage =[self UIImageFromCVMat:tywwm];
       UIImageWriteToSavedPhotosAlbum(resultImage, self, @selector(image:didFinishSavingWithError:contextInfo:), NULL);

复制代码

看效果:

处理的过程图

注意的是

图像形态学操做膨胀获得的是第二张的图片,若是水印的颜色是其余颜色则须要调整cv2.inRange 颜色范围。

总结

因为时间有限我感受没个一个月时间是不能达到产品的效果,因此这个方式我也暂时放弃,之后能够做为本身的一个App来作。

再探索的过程当中发现了一些不错的框架,或者思路处理图片。

1:好比iOS平台的coreimage,能够作直播主播的绿幕替换,其实主播背景都是后期处理上去的,这样的状况比较适合比较单纯的纯色,若是有其余亮度的效果,可能效果不佳给个连接juejin.im/post/5a3a10…

2:除去水印谷歌貌似研究出来了一个新的算法能够比PS还要高效的处理去掉水印,www.jiqizhixin.com/articles/20…

3:还有一个不错的框架CPUImage对图片和视频的处理也是一个很强大的框架

4:推荐一个不错的图片处理系列教程:www.cnblogs.com/Imageshop/

参考连接:

blog.csdn.net/huanghuangj…

blog.csdn.net/qq_24946843…

blog.csdn.net/ahgdwang/ar…

blog.csdn.net/qq_26907755…

cloud.tencent.com/developer/a…

www.jiqizhixin.com/articles/20…

www.icefox.org/2017/02/10/…

github.com/naver/grabc…

github.com/ahgdwang/Wa…

相关文章
相关标签/搜索