前提・実現したいこと
度々失礼いたします。。一昨日もこちらでお世話になりました。
https://teratail.com/questions/255770
iOSアプリで動作する、読み込まれた画像を前景・後景に分離し、後景を(まずは)白い背景にするというプログラムを作っております。
前回はcv::add関数を使い、こちらを実装いたしましたが、今回はcv::warpAffine関数を使い、画像を重ね合わせることでこちらを実装したいです。
主にこちらのサイトを参考にさせていただきました。
https://qiita.com/locutus1990/items/d2d9a68a962b34b62888
下記のような靴の画像(ソースコードでいうと変数sourceMat)に対して実行をしたところ、結果として2枚目のような画像(ソースコードでいうと変数dstImg)が出力されました。
下記に該当のソースコードを載せます。(125行目から221行目までが対象となる関数doBlurです。)
私としては、「fgMatはfgMaskにより後景が隠された画像(つまり透過画像)」という認識だったので、白背景の上に重ねれば、当然背景は白になると考えておりました。
私の認識が間違いでしょうか。もし間違いだとすれば、どこの部分を修正すれば、背景を白く出力できるでしょうか。
該当のソースコード
mm
1// 2// OpenCVManager.m 3// Portrait 4// 5// Created by Rina Kotake on 2018/12/01. 6// Copyright © 2018年 koooootake. All rights reserved. 7// 8#import <opencv2/opencv.hpp> 9#import <opencv2/imgcodecs/ios.h> 10 11#import "OpenCVManager.h" 12#import <Foundation/Foundation.h> 13 14@implementation OpenCVManager 15 16//MARK: shared 17static OpenCVManager* sharedData_ = nil; 18 19+ (OpenCVManager*)sharedManager 20{ 21 static dispatch_once_t onceToken; 22 dispatch_once(&onceToken, ^{ 23 sharedData_ = [OpenCVManager new]; 24 }); 25 return sharedData_; 26} 27 28cv::Mat inputMat, maskMat, inpaintMat, blurWithoutGradientMat; 29cv::Mat resultMat, bgModel, fgModel; 30cv::Mat1b fgMaskMat, bgMaskMat; 31 32//MARK: GrabCut 33///前景RectからGrabCutで前景抽出 34-(UIImage*)doGrabCut:(UIImage*)sourceImage foregroundRect:(CGRect)rect iterationCount:(int)iterationCount { 35 //UIImageをMatに変換 36 cv::Mat sourceMat; 37 UIImageToMat(sourceImage, sourceMat); 38 //RGBA > RGB 39 cv::cvtColor(sourceMat , sourceMat , CV_RGBA2RGB); 40 inputMat = sourceMat; 41 //CGRectをRectに変換 42 cv::Rect rectangle(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height); 43 44 //GrabCut 45 //bgModel, fgMolde: 処理で使用する配列 46 //iterationCount: 処理を繰り返す回数 47 cv::grabCut(sourceMat, maskMat, rectangle, bgModel, fgModel, iterationCount, cv::GC_INIT_WITH_RECT); 48 49 //結果から、前景らしい(GC_PR_FGD)領域を抽出して2値化 50 cv::Mat1b fgMask; 51 cv::compare(maskMat, cv::GC_PR_FGD, fgMask, cv::CMP_EQ); 52 fgMaskMat = fgMask; 53 bitwise_not(fgMaskMat, bgMaskMat); 54 return MatToUIImage(fgMask); 55} 56 57///マーカーマスク画像からGrabCutで前景抽出 58-(UIImage*)doGrabCut:(UIImage*)sourceImage markersImage:(UIImage*)markersImage iterationCount:(int)iterationCount { 59 //新たに入力されたマーカーマスク画像を既存のマスクと合成する 60 cv::Mat1b markersMat = [self synthesizeMaskWithMarkersImage:markersImage]; 61 62 //GrabCut 63 cv::grabCut(inputMat, markersMat, cv::Rect(), bgModel, fgModel, iterationCount, cv::GC_INIT_WITH_MASK); 64 maskMat = markersMat; 65 66 //GC_FGDをGC_PR_FGDに変換 67 cv::MatIterator_<unsigned char> itd = markersMat.begin(); 68 cv::MatIterator_<unsigned char> itd_end = markersMat.end(); 69 for(int i=0; itd != itd_end; ++itd, ++i) { 70 if (*itd == cv::GC_FGD) { 71 *itd = cv::GC_PR_FGD; 72 } 73 } 74 75 cv::Mat1b fgMask; 76 cv::compare(markersMat, cv::GC_PR_FGD, fgMask, cv::CMP_EQ); 77 fgMaskMat = fgMask; 78 bitwise_not(fgMaskMat, bgMaskMat); 79 return MatToUIImage(fgMask); 80} 81 82///マスク組み合わせ 83-(cv::Mat1b)synthesizeMaskWithMarkersImage:(UIImage*)image { 84 //マーカーマスク画像の画素を抽出 85 CGImageRef imageRef = [image CGImage]; 86 NSUInteger width = CGImageGetWidth(imageRef); 87 NSUInteger height = CGImageGetHeight(imageRef); 88 CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); 89 unsigned char *rawData = (unsigned char*) calloc(height * width * 4, sizeof(unsigned char)); 90 NSUInteger bytesPerPixel = 4; 91 NSUInteger bytesPerRow = bytesPerPixel * width; 92 NSUInteger bitsPerComponent = 8; 93 CGContextRef context = CGBitmapContextCreate(rawData, width, height, bitsPerComponent, bytesPerRow, colorSpace, kCGImageAlphaPremultipliedLast | kCGBitmapByteOrder32Big); 94 CGColorSpaceRelease(colorSpace); 95 CGContextDrawImage(context, CGRectMake(0, 0, width, height), imageRef); 96 CGContextRelease(context); 97 98 //既存のマスクと合成 99 cv::Mat1b markersMat = maskMat; 100 uchar* data = markersMat.data; 101 int countFGD = 0, countBGD = 0, countRem = 0; 102 103 for(int x = 0; x < width; x++) { 104 for( int y = 0; y < height; y++) { 105 NSUInteger byteIndex = ((image.size.width * y) + x ) * 4; 106 UInt8 red = rawData[byteIndex]; 107 UInt8 green = rawData[byteIndex + 1]; 108 UInt8 blue = rawData[byteIndex + 2]; 109 UInt8 alpha = rawData[byteIndex + 3]; 110 if(red == 255 && green == 255 && blue == 255 && alpha == 255) {//白色領域を前景 111 data[width * y + x] = cv::GC_FGD; 112 countFGD++; 113 } else if(red == 0 && green == 0 && blue == 0 && alpha == 255) {//黒色領域を後景 114 data[width * y + x] = cv::GC_BGD; 115 countBGD++; 116 } else { 117 countRem++; 118 } 119 } 120 } 121 free(rawData); 122 return markersMat; 123} 124 125//MARK: Blur 126-(UIImage*)doBlur:(CGFloat)blurSize isUpdatedSegmentation:(BOOL)isUpdatedSegmentation gradientMaskImage:(UIImage*)gradientMaskImage { 127 cv::Mat sourceMat = inputMat; 128 cv::Mat fgMask = fgMaskMat; 129 cv::Mat1b bgMask = bgMaskMat; 130 cv::Mat inpaintingMat; 131 132 if(isUpdatedSegmentation) { 133 //fgマスクの縁を太くする 134 cv::Mat1b fgContoursMask; 135 fgMask.copyTo(fgContoursMask); 136 std::vector<std::vector<cv::Point>> contours; 137 std::vector<cv::Vec4i> hierarchy; 138 cv::findContours(fgContoursMask, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_TC89_L1); 139 cv::drawContours(fgContoursMask, contours, -1, cv::GC_PR_FGD, sourceMat.size().width / 120); 140 141 //Inpainting 自動補間 142 cv::inpaint(sourceMat, fgContoursMask, inpaintingMat, 3, cv::INPAINT_TELEA); 143 inpaintMat = inpaintingMat; 144 } else { 145 inpaintingMat = inpaintMat; 146 } 147 148 cv::Mat blurResultMat, blurMat; 149 double sigumaX = 0;//sigumaX: XYの標準偏差、X=Y=0の時blurSizeから決定する 150 cv::GaussianBlur(inpaintingMat, blurMat, cv::Size(blurSize, blurSize), sigumaX, sigumaX, cv::BORDER_REPLICATE); 151 blurWithoutGradientMat = blurMat; 152 153 154 if (gradientMaskImage) { 155 //グラデーションマスクを変換 156 cv::Mat gradientMaskMat; 157 UIImageToMat(gradientMaskImage, gradientMaskMat); 158 cv::cvtColor(gradientMaskMat , gradientMaskMat , CV_RGBA2GRAY); 159 bitwise_not(gradientMaskMat, gradientMaskMat); 160 161 //グラデーションマスクとBlur画像を合成 162 blurResultMat = cv::Mat(sourceMat.size(), sourceMat.type()); 163 for (int y = 0; y < blurResultMat.rows; ++y) { 164 for (int x = 0; x < blurResultMat.cols; ++x) { 165 cv::Vec3b pixelOrig = sourceMat.at<cv::Vec3b>(y, x); 166 cv::Vec3b pixelBlur = blurMat.at<cv::Vec3b>(y, x); 167 float blurVal = gradientMaskMat.at<unsigned char>(y, x) / 255.0f; 168 cv::Vec3b pixelOut = blurVal * pixelBlur + (1.0f - blurVal) * pixelOrig; 169 blurResultMat.at<cv::Vec3b>(y, x) = pixelOut; 170 } 171 } 172 } else { 173 blurResultMat = blurMat; 174 } 175 176// //↓追加した文 177// int cols = blurResultMat.cols; 178// int rows = blurResultMat.rows; 179// for (int j = 0; j < rows; j++) { 180// for (int i = 0; i < cols; i++) { 181// blurResultMat.at<cv::Vec3b>(j, i)[0] = 255; //青 182// blurResultMat.at<cv::Vec3b>(j, i)[1] = 255; //緑 183// blurResultMat.at<cv::Vec3b>(j, i)[2] = 255; //赤 184// } 185// } 186// //↑追加した文 187 188 189 //合成 190 cv::Mat bgMat, fgMat, result; 191 192 NSString *path = [[NSBundle mainBundle] pathForResource:@"白背景" ofType:@"png"]; 193 cv::Mat whtimg = cv::imread([path UTF8String], cv::IMREAD_COLOR); 194 resize(whtimg,whtimg,sourceMat.size(),1); 195// NSString *path2 = [[NSBundle mainBundle] pathForResource:@"キャット" ofType:@"png"]; 196// cv::Mat catimg = cv::imread([path2 UTF8String], cv::IMREAD_ANYCOLOR); 197 198 //whtimg.copyTo(bgMat, bgMask); 199 sourceMat.copyTo(fgMat, fgMask); 200 201 //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓追加した文↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ 202 203 //背景画像の作成 204 cv::Mat dstImg; 205 whtimg.copyTo(dstImg); 206 //前景画像の変形行列 207 cv::Mat mat1 = (cv::Mat_<double>(2,3)<<1.0, 0.0, 0.0, 0.0, 1.0, 0.0); 208 //アフィン変換の実行(前景画像fgMatを白背景に重ねる) 209 cv::warpAffine(fgMat, dstImg, mat1, dstImg.size(), CV_INTER_LINEAR, cv::BORDER_TRANSPARENT); 210 211 //↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑追加した文↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ 212 213 //cv::add(bgMat, fgMat, result); 214 215 //DOF比較用 216 cv::Mat bgWithoutGradientMat; 217 blurMat.copyTo(bgWithoutGradientMat, bgMask); 218 cv::add(bgWithoutGradientMat, fgMat, blurWithoutGradientMat); 219 220 return MatToUIImage(dstImg); 221} 222 223 224-(UIImage*)inpaintingImage { 225 cv::Mat inpaintingMat = inpaintMat; 226 return MatToUIImage(inpaintingMat); 227} 228 229-(UIImage*)blurWithoutGradientImage { 230 cv::Mat blurMat = blurWithoutGradientMat; 231 return MatToUIImage(blurMat); 232} 233 234//MARK: reset 235-(void)resetManager { 236 maskMat.setTo(cv::GC_PR_BGD); 237 bgModel.setTo(0); 238 fgModel.setTo(0); 239} 240 241@end 242
試したこと
上記のソースコードで、fgMatの代わりに猫の画像(catimg、背景は透過されている)で実行した際は、白背景の画像が出力されましたが、猫の色が若干変化しております。。
↑が元画像
![]
↑が出力画像(スクリーンショットにしております)
補足情報(FW/ツールのバージョンなど)
Xcode Version 10.3 (10G8)
iOS 12.4.6
Swift Version 5.0.1
OpenCV 3.4.10
あなたの回答
tips
プレビュー