圖像處理方面的一些探索

最近有個需求好比手機拍照製做證件照

若是不用第三方提供的收費服務好比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…

相關文章
相關標籤/搜索