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