반응형



OpenCV에서 투명한 부분이 포함되어 있는 이미지(transparent image)를 다른 이미지 또는 영상에 오버레이하는 방법을 찾아봤습니다.



http://jepsonsblog.blogspot.kr/2012/10/overlay-transparent-image-in-opencv.html 에서 소개하고 있는 overlayImage 함수를 사용하면 간단히 해결됩니다.



이걸 가지고 뭐할 수 있을 까 고민하다가 사람 얼굴에 안경을 오버레이 해보았습니다.

원본 이미지로 다음  두 장의 이미지를 사용했습니다.


각각 다음 링크에서 다운로드 가능합니다.

https://pixabay.com/ko/초상화-여자-얼굴-여성-여자-얼굴-아름-다운-여자-얼굴-2159177/


https://pixabay.com/ko/안경-액자-종범-렌즈-광섬유-사양-광학-311831/





우선 수작업으로 눈의 위치와 안경의 크기를 조절하여 결과를 얻었습니다.





조금만 고민해보면 자동으로 눈 위치를 찾아서 얼굴 사이즈에 맞게 안경 이미지를 줄여주어 출력해줄 수 있을 거 같습니다.


OpenCV에서 제공하는 얼굴 인식 코드를 사용하면 될 듯합니다.

https://github.com/opencv/opencv/blob/master/samples/cpp/tutorial_code/objectDetection/objectDetection.cpp




사용한 전체 코드입니다.


#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp> //for resize
#include <iostream>
#include <string>

using namespace cv;
using namespace std;


void overlayImage(const Mat &background, const Mat &foreground,
Mat &output, Point2i location);


int main(int argc, char** argv)
{

Mat image_background, image_foreground;

image_foreground = imread("glasses_960x480.png", IMREAD_UNCHANGED); //알파 채널 포함된 32비트 컬러 이미지, Transparent PNG
image_background = imread("face_1280x853.jpg", IMREAD_COLOR);  //채널 당 8비트인 24비트 컬러 이미지


if ( image_background.empty() || image_foreground.empty() )  // Check for invalid input
{
cout << "Could not open or find the image" << std::endl;
return -1;
}


namedWindow("Display window", WINDOW_AUTOSIZE); // Create a window for display.
//namedWindow("Display window", WINDOW_NORMAL|WINDOW_KEEPRATIO); // Create a window for display.


Mat image_resize_foreground;
resize(image_foreground, image_resize_foreground, Size(), 0.4, 0.4);


Mat image_result;
overlayImage(image_background, image_resize_foreground, image_result, cv::Point(440, 350));
imshow("Display window", image_result);

waitKey(0);


return 0;
}


void overlayImage(const Mat &background, const Mat &foreground,
Mat &output, Point2i location)
{
background.copyTo(output);


// start at the row indicated by location, or at row 0 if location.y is negative.
for (int y = std::max(location.y, 0); y < background.rows; ++y)
{
int fY = y - location.y; // because of the translation

// we are done of we have processed all rows of the foreground image.
if (fY >= foreground.rows)
break;

// start at the column indicated by location,

// or at column 0 if location.x is negative.
for (int x = std::max(location.x, 0); x < background.cols; ++x)
{
int fX = x - location.x; // because of the translation.

// we are done with this row if the column is outside of the foreground image.
if (fX >= foreground.cols)
break;

// determine the opacity of the foregrond pixel, using its fourth (alpha) channel.
double opacity =
((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3])

/ 255.;


// and now combine the background and foreground pixel, using the opacity,

// but only if opacity > 0.
for (int c = 0; opacity > 0 && c < output.channels(); ++c)
{
unsigned char foregroundPx =
foreground.data[fY * foreground.step + fX * foreground.channels() + c];
unsigned char backgroundPx =
background.data[y * background.step + x * background.channels() + c];
output.data[y*output.step + output.channels()*x + c] =
backgroundPx * (1. - opacity) + foregroundPx * opacity;
}
}
}
}






참고


https://docs.opencv.org/3.3.1/db/deb/tutorial_display_image.html


http://jepsonsblog.blogspot.kr/2012/10/overlay-transparent-image-in-opencv.html



문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts