본문 바로가기

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

[OpenCV][C++] 영상에 다른 영상 ( 로고 ) 삽입하기(2) - mask 생성, copyTo(), bitwise_and() 합성 합치기 두영상 더하기

 

이번에는 로고를 좀 더 멋지게 삽입해 봐요.

오늘은 좀 Quick 하게 진행할께요.

copyTo() 이용하기

이번에도 같은 영상을 가지고 로고를 넣어 볼께요.

1. 영상 준비하기

원본 영상과 로고 영상을 준비합니다.

cv::resize()를 이용하여 영상 사이즈를 적절히 조절해줍니다.

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);
 

2. 로고 영상을 gray로 변환하기

로고 영상의 mask를 만들기 위한 작업이에요.

cv::Mat src_logo_gray;
cv::cvtColor(src_logo, src_logo_gray, cv::COLOR_BGR2GRAY);
 

3. mask 영상 생성하기

mask 영상을 생성하겠습니다. mask는 일종의 filter라고 생각하시면 되는데요.

내가 관심있는 전경(foreground)는 white로, 내가 없애고 싶은 배경(background)는 black으로 만든 영상을 말해요.

gray 영상을 cv::threshold()를 이용하여 180 이상인 값을 255 값으로 만드는 연산을 수행합니다.

cv::Mat src_logo_mask;
cv::threshold(src_logo_gray, src_logo_mask, 180, 255, cv::THRESH_BINARY);
 

그럼 아래와 같은 영상을 얻을 수 있는데요.

전경이 black, 배경이 white인 영상이 만들어 졌네요.

반전을 시켜줘야 겠죠?

전 유식하게 cv::bitwise_not()을 사용해볼께요.

cv::Mat src_logo_mask_inv;
cv::bitwise_not(src_logo_mask, src_logo_mask_inv);
 

그럼 아래와 같이

그럴싸한 마스크가 만들어져요.

사실, cv::threshold() 함수에 이런 기능이 없겠습니까? threshold 옵션 cv::THRESH_BINARY_INV가 있어요.

cv::Mat src_logo_mask;
cv::threshold(src_logo_gray, src_logo_mask, 180, 255, cv::THRESH_BINARY_INV);
 

이렇게 하면 같은 영상을 얻을 수 있습니다.

Thresholding 관련 내용은 아래 참조하시기 바랍니다.

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

 

4. ROI 설정하기(로고 삽입할 영역)

 

이번에는 로고를 우하단에 넣어 볼께요.

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

자. 어떤 뜻인가요? (670, 670) 위치에서 로고의 width, height 만큼 ROI를 설정한다는 뜻이에요.

5. 원본 영상에서 ROI 추출 후 로고 삽입하기

위에서 설정한 rectROI 영역인 src_lena_roi 영상을 생성합니다.

그리고 copyTo()를 이용하여 src_logo 영상을 src_logo_mask를 이용하여 src_lena_roi 영상에 copy 합니다.

cv::Mat src_lena_roi(src_lena, rectROI);
src_logo.copyTo(src_lena_roi, src_logo_mask);
 

그 결과는 아래와 같습니다.

copyTo()는 아래와 같이 src1에서 mask 영상의 white 부분에 해당하는 픽셀들만 src2에 복사하라라는 뜻이에요.

src_lena_roi 영상은 원본 영상의 우하단 부분을 가리키는 ROI이기 때문에 원본 영상에 아래와 같이

로고가 잘 들어갔어요.

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

#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::Mat src_logo_gray;
    cv::cvtColor(src_logo, src_logo_gray, cv::COLOR_BGR2GRAY);

    cv::Mat src_logo_mask;
    cv::threshold(src_logo_gray, src_logo_mask, 180, 255, cv::THRESH_BINARY_INV);

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

    cv::Mat src_lena_roi(src_lena, rectROI);
    src_logo.copyTo(src_lena_roi, src_logo_mask);    

    return 0;
}
 

같은 결과를 내겠지만 이번엔 bitwise_and()를 이용한 로고 삽입하기를 알아볼께요.

bitwise_and() 사용하기

앞쪽 Process는 위 설명과 같으므로 생략할께요.

이번엔 다른 로고 영상을 가져왔어요.

1. 영상 준비하기

2. 로고 영상을 gray로 변환하기

3. mask 영상 생성하기

4. ROI 영역 설정하기(로고 삽입할 영역)

여기까지 소스는 아래와 같습니다.

mask 영상을 생성하기 위해 이번에는 cv::inRange() 함수를 사용했는데요.

이는 10~190 사이의 밝기 값은 255로, 나머지 밝기 값은 0으로 만들어 주는 함수에요.

// 원본 영상 읽어오고 resize 하기
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);    
// 로고 영상 읽어오고 resize 하기
cv::Mat src_logo_org = cv::imread("../JKGarden_Logo_2.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);
//로고영상 gray로 만들기
cv::Mat src_logo_gray;
cv::cvtColor(src_logo, src_logo_gray, cv::COLOR_BGR2GRAY);
//mask 영상 만들기
// 10~190 사이의 값을 255로, 나머지 값을 0으로 만들기
cv::Mat src_logo_mask;
cv::inRange(src_logo_gray, 10, 190, src_logo_mask);
// ROI 영역 설정하기
cv::Rect rectROI = cv::Rect(src_lena.cols - src_logo.cols, src_lena.rows - src_logo.rows, src_logo.cols, src_logo.rows);
 

로고 영상과 mask 영상은 아래와 같아요.

5. 배경이 제거된 로고 영상 만들기

배경이 제거된 로고 영상을 만들어 봅시다.

cv::Mat src_logo_removed;
cv::bitwise_and(src_logo, src_logo, src_logo_removed, src_logo_mask);
 

cv::bitwise_and()mask의 white 영역 내에서 src1과 src2의 bit 연산(and, &) 결과를 dst에 저장하는 것 입니다.

그럼 src_logo_removed 영상을 보면, 아래와 같이 생성되었을꺼에요.

6. mask의 반전 영상을 생성하여 로고의 전경을 black 처리 하기

배경이 지워진 로고 영상을

더하기 위해 로고 영역만 black 처리 된 영상을 만듭니다.

cv::Mat src_logo_mask_inv;
cv::bitwise_not(src_logo_mask, src_logo_mask_inv);

cv::Mat src_lena_logo_black;
cv::bitwise_and(src_lena_roi, src_lena_roi, src_lena_logo_black, src_logo_mask_inv);
 

7. 배경 제거 로고와 전경 black 영상 더하여 ROI에 복사하기

배경이 제거된 로고 영상(src_logo_removed) 과 전경 black으로 만든 roi 영상을 더하여 tmp 영상을 만듭니다.

cv::Mat tmp;
tmp = src_logo_removed + src_lena_logo_black;    
tmp.copyTo(src_lena(rectROI));
 

그리고 src_lena에 복사해주면 끝!!

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

#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_2.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::Mat src_logo_gray;
    cv::cvtColor(src_logo, src_logo_gray, cv::COLOR_BGR2GRAY);

    cv::Mat src_logo_mask;    
    cv::inRange(src_logo_gray, 10, 190, src_logo_mask);     
    
    cv::Rect rectROI = cv::Rect(src_lena.cols - src_logo.cols, src_lena.rows - src_logo.rows, src_logo.cols, src_logo.rows);
    cv::Mat src_lena_roi = src_lena(rectROI);

    cv::Mat src_logo_removed;
    cv::bitwise_and(src_logo, src_logo, src_logo_removed, src_logo_mask);  

    cv::Mat src_logo_mask_inv;
    cv::bitwise_not(src_logo_mask, src_logo_mask_inv);

    cv::Mat src_lena_logo_black;
    cv::bitwise_and(src_lena_roi, src_lena_roi, src_lena_logo_black, src_logo_mask_inv);

    cv::Mat tmp;
    tmp = src_logo_removed + src_lena_logo_black;    
    tmp.copyTo(src_lena(rectROI));
    return 0;
}