투명한 PNG 파일 오버레이(알파 블렌딩)
Study/OpenCV 2018. 5. 2. 21:32 |
문득 알파 채널이 있는, 투명한 PNG 파일을 합성하는 방법이 궁금해져서 인터넷을 찾아봤습니다. 역시나 잘 정리되어 있는 사이트가 꽤 있더군요.
https://www.learnopencv.com/alpha-blending-using-opencv-cpp-python/
여기는 저도 자주 참고하는 learn opencv 블로그구요.. 일단 PNG 파일을 채널 분리하여 multiply(), add() 함수를 조합해서 합성하는 방법과 직접 픽셀 값에 접근해서 합성하는 방법을 비교해놓았습니다. 픽셀 값 접근할 때 Mat::data 멤버 함수를 직접 참조하는데, 음.. 개인적으로는 좋아하지 않는 방법입니다.
http://jepsonsblog.blogspot.kr/2012/10/overlay-transparent-image-in-opencv.html
큰 배경 영상의 일부분에 작은 크기의 PNG 파일을 합성하는 코드를 잘 만들어둔 블로그입니다. 역시나 data 멤버 변수를 직접 사용했구요..
https://blog.naver.com/h2ohyukhyuk/220935134665
한글로 된 블로그인데, 예제로 사용한 그림이 꽤 재미있었습니다.
일단 위에 나타난 코드와 예제를 참고하여, 이번 포스트에서는 입력 영상에서 얼굴을 검출하고, 검출한 얼굴 위에 고양이 귀를 합성하는 예제를 만들어보았습니다. 일단 전체 코드를 공개하겠습니다.
#include "opencv2/opencv.hpp" #include <iostream> using namespace cv; using namespace std; void overlayImage(Mat& src, const Mat& over, const Point& pos) { CV_Assert(src.type() == CV_8UC3); CV_Assert(over.type() == CV_8UC4); int sx = std::max(pos.x, 0); int sy = std::max(pos.y, 0); int ex = std::min(pos.x + over.cols, src.cols); int ey = std::min(pos.y + over.rows, src.rows); for (int y = sy; y < ey; y++) { int y2 = y - pos.y; // y coordinate in overlay image Vec3b* pSrc = src.ptr<Vec3b>(y); const Vec4b* pOvr = over.ptr<Vec4b>(y2); for (int x = sx; x < ex; x++) { int x2 = x - pos.x; // x coordinate in overlay image float alpha = (float)pOvr[x2][3] / 255.f; if (alpha > 0.f) { pSrc[x][0] = saturate_cast<uchar>(pSrc[x][0] * (1.f - alpha) + pOvr[x2][0] * alpha); pSrc[x][1] = saturate_cast<uchar>(pSrc[x][1] * (1.f - alpha) + pOvr[x2][1] * alpha); pSrc[x][2] = saturate_cast<uchar>(pSrc[x][2] * (1.f - alpha) + pOvr[x2][2] * alpha); } } } } int main() { Mat src = imread("dlwlrma.jpg", IMREAD_COLOR); // Mat src = imread("iujelly.jpg", IMREAD_COLOR); Mat cat = imread("cat.png", IMREAD_UNCHANGED); if (src.empty() || cat.empty()) { cerr << "Image load failed!" << endl; return -1; } CascadeClassifier face_cascade("haarcascade_frontalface_default.xml"); if (face_cascade.empty()) { cerr << "Failed to open (face) xml file!" << endl; return -1; } vector<Rect> faces; face_cascade.detectMultiScale(src, faces); for (Rect face : faces) { float fx = float(face.width) / cat.cols; Mat tmp; resize(cat, tmp, Size(), fx, fx); Point pos(face.x, face.y - face.height / 3); overlayImage(src, tmp, pos); } imshow("src", src); waitKey(0); return 0; }
위 예제에서 overlayImage(src, over, pos); 함수는 3채널 컬러 영상 src에, 4채널 PNG 파일로부터 만들어진 over 영상을 pos 위치에 합성하여 src에 저장합니다. 제가 작성한 overlayImage() 함수가 앞서 소개한 블로그에서 사용한 함수와 다른 점은,
- data 멤버를 직접 참조하는 방법 대신, 좀 더 안전한 Mat::ptr() 함수를 사용하였고..
- for 루프 시작 전에 실제 합성이 이루어지는 영역을 계산하였고..
- 한 장의 입력 영상에 여러 번 합성을 할 수 있도록 구조를 변경했다는 점입니다.
얼굴을 검출하는 코드에 대한 자세한 설명은 생략하구요.. ranged-for 루프에서 합성할 때에는 검출된 얼굴 영역의 가로 크기에 맞게 고양이 귀 영상 크기를 축소한 후, 얼굴 위 적당한 위치에 고양이 귀를 합성합니다. 얼굴이 2개 있으면 2번 합성하게 됩니다. 실제 결과는 아래와 같습니다.
참고로 제 Desktop에서 512x512 영상에 합성하는데 0.1ms도 걸리지 않았습니다. 꽤 빠르네요.
'Study > OpenCV' 카테고리의 다른 글
Ubuntu 18.04 에서 OpenCV 설치하기 (8) | 2018.05.28 |
---|---|
Visual Studio 2017에서 CUDA 9.2 활성화하여 OpenCV 빌드하기 (0) | 2018.05.24 |
OpenCV Freetype 모듈을 이용하여 영상에 한글 출력하기 (8) | 2018.04.04 |
OpenCV 3.4.1 릴리즈 (0) | 2018.02.27 |
Visual Studio 2013에서 OpenCV 3.4.0 빌드하기 (2) | 2018.01.21 |