본문 바로가기

프로그래밍 강좌/C++ - OpenCV

[OpenCV][C++] 영상에 다른 영상(로고) 삽입하기(1) - ROI 설정, 영상 가중합 로고 넣기 합성 addWeighted() roi

지난번까지는 OpenCV의 꽃인 cv::Mat 클래스와 UMat 클래스에 대해 알아봤습니다. 원소 접근 속도 비교까지 해 드렸어요.

https://blog.naver.com/dorergiverny/223088700257

 

[OpenCV][C++] cv::Mat 클래스 총정리(6) - OpenCL과 cv::UMat 사용하기 OpenMP tbb gpu 사용

cv::Mat 클래스와 관련되어 벌써 6번째 시리즈네요. 그만큼 영상처리에 많이 사용되면서도 가장 중요한 O...

blog.naver.com

이번에는 영상에 다른 영상을 삽입하는 방법에 대해 알아볼께요.

ROI(Region Of Interest, 관심영역) 설정

한 영상에 로고와 같은 다른 영상을 삽입하려고 하면 ROI를 먼저 알아야 합니다.

ROI(Region Of Interest) 관심영역이란 뜻으로, 영상 내에서 특정 작업을 하고자 하는 일부 영역을 뜻합니다.

에지를 찾거나 특정 물체를 찾는 시간을 단축시키기 위해 ROI를 설정하여 찾기도 합니다. 만약 얼굴 영역

(좌표가 (225, 230)이고, width: 130, height: 160인 사각형) 을 관심영역으로 설정한다면 아래와 같이 하면 됩니다.

#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
    cv::Mat src_lena_org = cv::imread("../lena_color.bmp", cv::IMREAD_UNCHANGED);

    cv::Rect roiRect = cv::Rect(225, 230, 130, 160);
    cv::Mat lenaFace = src_lena_org(roiRect);    

    cv::rectangle(src_lena_org, roiRect, cv::Scalar(0, 0, 255));
    return 0;
}
 

이왕 cv::Rect에 대해 알아보는 김에 ROI 영역만 Processing을 해 봅시다. 얼굴 영역만 모자이크 처리를 하는 소스를

간단히 구현해 보면,

#include <iostream>
#include <opencv2/opencv.hpp>

void makeMosaicImage(cv::Mat& src)
{
    cv::Mat src_resized;
    cv::resize(src, src_resized, cv::Size(src.cols * 0.2, src.rows * 0.2));
    cv::resize(src_resized, src, cv::Size(src_resized.cols * 5, src_resized.rows * 5), cv::INTER_NEAREST);
}

int main()
{
    cv::Mat src_lena_org = cv::imread("../lena_color.bmp", cv::IMREAD_UNCHANGED);

    cv::Rect roiRect = cv::Rect(225, 230, 130, 160);
    cv::Mat lenaFace = src_lena_org(roiRect);    

    makeMosaicImage(lenaFace);

    return 0;
}
 

결과는 아래와 같습니다.

참고로, 모자이크 영상을 만들기 위해서는 영상 사이즈를 줄였다가 다시 늘리면 모자이크 효과를 볼 수 있습니다.

여기에서, 분명히 ROI 영역만 모자이크 처리 했는데, 원본 영상에서 얼굴 영역만 모자이크 처리 된 것처럼 나오네요.

왜 일까요?

cv::Mat lenaFace = src_lena_org(cv::Rect(225, 230, 130, 160));
 

lenaFace 영상이 Deep Copy아닌 Shallow Copy가 된 영상이어서 그렇습니다.

얕은 복사(Shallow Copy) 실제로 메모리를 별도로 만들어서 Data를 저장한 것이 아니라 lena 영상의 rect 부분을 lenaFace가 pointing(가리키고) 하고 있는 것이기 때문입니다.

 

만약 원본 영상이 바뀌지 않길 바란다면, 아래와 같이 .clone()을 사용하여 Deep Copy를 하면 됩니다.

#include <iostream>
#include <opencv2/opencv.hpp>

void makeMosaicImage(cv::Mat& src)
{
    cv::Mat src_resized;
    cv::resize(src, src_resized, cv::Size(src.cols * 0.2, src.rows * 0.2));
    cv::resize(src_resized, src, cv::Size(src_resized.cols * 5, src_resized.rows * 5), cv::INTER_NEAREST);
}

int main()
{
    cv::Mat src_lena_org = cv::imread("../lena_color.bmp", cv::IMREAD_UNCHANGED);

    cv::Rect roiRect = cv::Rect(225, 230, 130, 160);
//    cv::Mat lenaFace_shallow = src_lena_org(roiRect);    
    cv::Mat lenaFace_deep = src_lena_org(roiRect).clone();
        
    makeMosaicImage(lenaFace_deep);

    return 0;
}
 
로고를 블렌딩(Blending)하여 삽입하기

이제는 영상에 다른 영상을 삽입하는 방법에 대해 알아볼께요.

1. 원본 영상과 로고 영상 읽어 오기

먼저 원본 영상과 로고 영상을 읽어와야 합니다. 여기에서, 원본영상으로 사용할 lena 영상은 512x512이고, 로고 영상은 706 x 670 이니까 원본 영상 사이즈를 늘리고, 로고 영상 사이즈를 줄여보겠습니다.

cv::Mat src_lena_org = cv::imread("../lena_color.bmp", cv::IMREAD_UNCHANGED);

cv::Mat src_lena;
cv::resize(src_lena_org, src_lena, cv::Size(src_lena_org.cols * 2, src_lena_org.rows * 2), cv::INTER_AREA);

cv::Mat src_logo_org = cv::imread("../JKGarden_Logo_1.bmp", cv::IMREAD_UNCHANGED);
cv::Mat src_logo;
cv::resize(src_logo_org, src_logo, cv::Size(src_logo_org.cols * 0.5, src_logo_org.rows * 0.5), cv::INTER_LINEAR);
 

참고로 JK 가든은 조경수를 정말 사랑하는 사람이 운영하는 회사에요. (제가 운영하는... ㅋㅋ)

2. 로고를 삽입할 ROI 설정

저는 좌상단인 (0, 0) 에 로고를 넣어 볼께요. 그럼 아래와 같이 할 수 있겠죠?

cv::Rect rectROI = cv::Rect(0, 0, src_logo.cols, src_logo.rows);
 

ROI 설정은 아래와 같은 방식으로 할 수 있어요.

3. ROI 영상 생성

로고를 삽입할 ROI 영상을 생성합니다.

cv::Mat src_lena_roi = src_lena_org(rectROI);
 

그럼 아래와 같은 src_lena_roi 영상이 생성되어요.

여기서 src_lena_roishallow copy라는 거! 잊지 않으셨죠?

4. addWeighted() 로 두 영상 Blending 하기

두 영상을 Blending하는 함수인 addWeighted() 를 사용하여 src_lena_roi 영상과 src_logo 영상을 0.7과 0.5 의 비율로 합쳐서 src_lena_roi 영상에 다시 넣어 줍니다.

cv::addWeighted(src_lena_roi, 0.7, src_logo, 0.5, 0., src_lena_roi);
 

그럼 아래와 같이 blending된 영상을 얻을 수 있고,

src_lena_roi는 shallow copy를 했으니 원본 영상의 그 ROI 부분도 위의 영상으로 대체되었습니다.

addWeighted()함수는

위와 같은 형태이고, src1 alpha 가중치를, src2 beta 가중치를 곱해서 gamma 값을 더한 영상을 dst에 저장하라

란 뜻이에요. dst = src1 * 0.7 + src2 * 0.5 + gamma; 라고 직관적으로 써도 똑같은 결과가 나옵니다.

짜잔~!!

전체 소스는 아래와 같습니다.

#include <iostream>
#include <opencv2/opencv.hpp>

int main()
{
    cv::Mat src_lena_org = cv::imread("../lena_color.bmp", cv::IMREAD_UNCHANGED);

    cv::Mat src_lena;
    cv::resize(src_lena_org, src_lena, cv::Size(src_lena_org.cols * 2, src_lena_org.rows * 2), cv::INTER_AREA);    

    cv::Mat src_logo_org = cv::imread("../JKGarden_Logo_1.bmp", cv::IMREAD_UNCHANGED);
    cv::Mat src_logo;
    cv::resize(src_logo_org, src_logo, cv::Size(src_logo_org.cols * 0.5, src_logo_org.rows * 0.5), cv::INTER_LINEAR);

    //cv::Rect rectROI = cv::Rect(0, 0, src_logo.cols, src_logo.rows);
    cv::Rect rectROI(0, 0, src_logo.cols, src_logo.rows);
    cv::Mat src_lena_roi = src_lena(rectROI);
        
    // cv::addWeighted(src_lena_roi, 0.7, src_logo, 0.5, 0., src_lena_roi);
    src_lena_roi = src_lena_roi * 0.7 + src_logo * 0.5 + 0;

    return 0;
}