質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

1794閲覧

【OpenCV】前景画像(被写体)に影を付けたい

yamaji1108

総合スコア19

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2020/04/27 04:35

前提・実現したいこと

現在、OpenCVによって読み込んだ画像を前景と後景に分離し、後景を白背景にして出力するというプログラムを作成中です。

しかし、そのまま前景(該当ソースコードのfgMat)をそのまま抽出して白背景にするだけだと、下記のような不自然な画像が出来上がってしまいます。

イメージ説明
↑が元画像

イメージ説明
↑が加工後画像

そこで、抽出した前景画像に影を加えたいのですが、どのように実装すれば良いかが分かりません。
調べると、「OpenCVによって影を取り除く」という項目ばかりが出てきてしまいます。

https://apptopi.jp/2019/10/15/shade-and-frame-edit/
こちらのサイトで説明している通り、アイビスペイントXというアプリではドロップシャドウという機能によって、被写体に影をつけることが可能です。
イメージ説明

この機能の実装がどうしても分かりません。
どうかヒントだけでも構いませんので、ご教授をお願いできませんか。
よろしくお願いします。

該当のソースコード

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 cv::Mat bgMat, fgMat, result; 178 179 NSString *path = [[NSBundle mainBundle] pathForResource:@"白背景" ofType:@"png"]; 180 cv::Mat whtimg = cv::imread([path UTF8String], cv::IMREAD_COLOR); 181 resize(whtimg,whtimg,sourceMat.size(),1); 182 183 whtimg.copyTo(bgMat, bgMask); 184 sourceMat.copyTo(fgMat, fgMask); 185 cv::add(bgMat, fgMat, result); 186 187// //DOF比較用 188// cv::Mat bgWithoutGradientMat; 189// blurMat.copyTo(bgWithoutGradientMat, bgMask); 190// cv::add(bgWithoutGradientMat, fgMat, blurWithoutGradientMat); 191 192 return MatToUIImage(result); 193} 194 195 196-(UIImage*)inpaintingImage { 197 cv::Mat inpaintingMat = inpaintMat; 198 return MatToUIImage(inpaintingMat); 199} 200 201-(UIImage*)blurWithoutGradientImage { 202 cv::Mat blurMat = blurWithoutGradientMat; 203 return MatToUIImage(blurMat); 204} 205 206//MARK: reset 207-(void)resetManager { 208 maskMat.setTo(cv::GC_PR_BGD); 209 bgModel.setTo(0); 210 fgModel.setTo(0); 211} 212 213@end 214

補足情報(FW/ツールのバージョンなど)

Xcode Version 10.3 (10G8)
iOS 12.4.6
Swift Version 5.0.1
OpenCV 3.4.10

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

「影を加える」の意味がよくわからんですが……

3枚目の絵は,単に
「前景領域の形状を灰色でべた塗した絵にてきとーなぼかし(ガウシアンか何か)をかけたもの」と,
前景画像とを,位置をちょっとずらして重ねているだけに見えます.

投稿2020/04/27 06:21

fana

総合スコア11660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yamaji1108

2020/04/27 12:49

ご回答いただき、ありがとうございます! 前景写真を使って作っているのですね。 参考にさせていただき、早速実装させてみます。 ただ、画像の重ね合わせについても壁にぶつかっております。。 https://teratail.com/questions/256492
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問