본문 바로가기

영상처리/OpenCV 및 영상처리 이론

OpenCV 기초 - 4. 영상 이진화 + 관심 영역(ROI) 정의

 

영상 이진화, 관심영역(ROI)에 대해 알아보고 Image Watch를 사용하여 디버깅하는 방법을 알아보겠습니다.

 

- ROI, addWeighted, copyTo

 

 


 

 
 
 <lenna.jpg>     <lenna_text.jpg>

 

   

                                                               

 


 

먼저 ROI란 관심 영역이라는 뜻으로 영상 내에서 작업을 수행하고자 하는 특정 영역을 의미합니다. lenna이미지에 글씨를 넣기 위해서 글씨를 넣고자 하는 영역을 39번째 줄을 통해 설정하였습니다. 

 

- Image Watch

 

이전에 언급한 적이 있던 Image Watch 기능을 통해 영역이 잘 설정되었는지 확인해봅시다.

 

 

Image Watch를 사용하기 위해서는 디버그를 수행하여야 합니다. 이를 위해서 imageROI가 설정된 이후 부분에 브레이크 포인트를 잡아주고 디버깅을 통해 imageROI의 내부값을 확인해보겠습니다.

 

 

디버깅 상태에서 Mat 객체의 이름위에 마우스를 올리면 위와 같은 창이 나타나는 것을 볼 수 있습니다. 이 때 빨간 상자로 표시한 돋보기 아이콘을 누르면 해당 객체의 데이터를 볼 수 있게 됩니다.

 

 

Image Watch를 통해 ROI영역이 (10,10)부터 logo의 사이즈 만큼의 영역이 설정된 것을 확인할 수 있습니다. 

 

 

- void addWeighted( InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst )

addWeighted 함수를 통해 두 영상을 합칠 수 있습니다. 이때 결과 dst는

 

dst(I) = saturate( src1(I)*alpha + src2(I)*beta + gamma )

 

의 꼴로 결정됩니다.

 

- void Mat::copyTo( OutputArray m, InputArray mask ) const

설명드린적 있던 copyTo를 이용해서 두 이미지를 합치는 방법도 있습니다. 이때 기존에 사용한 단순 복사에서 마스크에 해당하는 파라미터를 추가해주어 복사해오게 됩니다. 마스크의 특정 픽셀의 값을 확인하여 해당 지점의 값이 0이 아니라면 그에 대응되는 지점의 값을 복사하게 됩니다.

 

 

 

- 결과

 

 

 

결과 사진을 보면 with logo 2 결과에 문제가 있음을 알 수 있습니다. 이는 마스크에 잡음이 있기 때문에 0이 아닌 지점들이 있고 이러한 영향이 결과에 나타난 것이라 볼 수 있습니다. 

이러한 문제를 해결하기 마스크에서 어중간한 값들을 가지는 픽셀을 전부 0 혹은 255로 만드는 이진화(Binarization, Thresholding)를 해보겠습니다.

 


 

- 이진화 (thresholding, binarization)

 

 

- double cv::threshold( InputArray src, OutputArray dst, double thresh, double maxval, int type )

입력 이미지의 각 픽셀을 thresh 값과 비교하여 크다면 maxval로 해당 픽셀의 값을 수정합니다. 

type에 따라서 값의 설정에 차이가 있기 때문에 docs에서 확인 후 원하는 형태로 사용하시길 바랍니다. 

 

http://docs.opencv.org/3.1.0/d7/d1b/group__imgproc__misc.html#gaa9e58d2860d4afa658ef70a9b1115576

 

예제의 경우 THRESH_BINARY로 타입이 설정되어 있기 때문에 작은값은 전부 0이 되게 됩니다. 이렇게 처리를 마친 새로운 이미지를 이용해 로고를 넣었고 결과가 잘 나오는 것을 볼 수 있습니다.

 

-결과

 

 

 

※ 참고 : 노이즈를 잡기 위해 저장하는 새로운 이미지의 파일형을 보면 *.bmp임을 확인할 수 있습니다. 

bmp 파일은 데이터의 변화이 없이 그대로 저장되기 때문에 이렇게 작업하였으며 jpg와 같은 파일형으로 저장을 하실 경우 저장되는 과정에서 이미지의 용량을 줄이기 위해 영상의 데이터를 약간씩 변화시키게 됩니다. 따라서 jpg이미지로 저장을 하고 다시 수행을 할 경우 변화된 데이터에 의해 이전처럼 노이즈 낀 결과를 얻게 됩니다.

 

이러한 특징은 Image Watch를 통해 픽셀 데이터를 확인하고 데이터의 변화를 봄으로써 손쉽게 확인할 수 있습니다.

 

 

- 코드 첨부

더보기

/*------------------------------------------------------------------------------------------*\

   This file contains material supporting chapter 2 of the cookbook:  

   Computer Vision Programming using the OpenCV Library. 

   by Robert Laganiere, Packt Publishing, 2011.

 

   This program is free software; permission is hereby granted to use, copy, modify, 

   and distribute this source code, or portions thereof, for any purpose, without fee, 

   subject to the restriction that the copyright notice may not be removed 

   or altered from any source or altered source distribution. 

   The software is released on an as-is basis and without any warranties of any kind. 

   In particular, the software is not guaranteed to be fault-tolerant or free from failure. 

   The author disclaims all warranties with regard to this software, any use, 

   and any consequent failure, is purely the responsibility of the user.

 

   Copyright (C) 2010-2011 Robert Laganiere, www.laganiere.name

\*------------------------------------------------------------------------------------------*/

 

 

#include <vector>

#include <opencv2/core/core.hpp>

#include <opencv2/highgui/highgui.hpp>

 

 

int main()

{

cv::Mat image1;

cv::Mat image2;

 

image1= cv::imread("boldt.jpg");

image2= cv::imread("rain.jpg");

if (!image1.data)

return 0; 

if (!image2.data)

return 0; 

 

cv::namedWindow("Image 1");

cv::imshow("Image 1",image1);

cv::namedWindow("Image 2");

cv::imshow("Image 2",image2);

 

cv::Mat result;

cv::addWeighted(image1,0.7,image2,0.9,0.,result);

 

cv::namedWindow("result");

cv::imshow("result",result);

 

// 오버로드한 연산자 사용하기

result= 0.7*image1+0.9*image2;

 

cv::namedWindow("result with operators");

cv::imshow("result with operators",result);

 

image2= cv::imread("rain.jpg",0);

 

// 세 영상의 벡터 생성

std::vector<cv::Mat> planes;

// 3-채널 영상을 3개의 1채널 영상의 분리

cv::split(image1,planes);

// 파랑 채널 더하기

planes[0]+= image2;

// 3개의 1-채널을 1개의 3-3채널로 합치기

cv::merge(planes,result);

 

cv::namedWindow("Result on blue channel");

cv::imshow("Result on blue channel",result);

 

// 영상 읽기

cv::Mat image= cv::imread("boldt.jpg");

cv::Mat logo= cv::imread("logo.bmp");

 

// ROI 영상 정의

cv::Mat imageROI;

imageROI= image(cv::Rect(385,270,logo.cols,logo.rows));

 

// 로고를 영상에 더하기 

cv::addWeighted(imageROI,1.0,logo,0.3,0.,imageROI);

 

// 결과 보기

cv::namedWindow("with logo");

cv::imshow("with logo",image);

 

// 영상 읽기

image= cv::imread("boldt.jpg");

logo= cv::imread("logo.bmp");

 

// ROI 정의

imageROI= image(cv::Rect(385,270,logo.cols,logo.rows));

 

// 마스크 불러 들이기(명암도 영상이어야 함)

cv::Mat mask= cv::imread("logo.bmp",0);

 

// 마스크를 ROI로 복사

logo.copyTo(imageROI,mask);

 

// 결과 보기

cv::namedWindow("with logo 2");

cv::imshow("with logo 2",image);

 

// 영상 읽기

logo= cv::imread("logo.bmp",0);

image1= cv::imread("boldt.jpg");

 

// 3-채널 영상을 3개의 1-채널 영상으로 분리

std::vector<cv::Mat> channels;

cv::split(image1,channels);

 

imageROI= channels.at(1);

 

cv::addWeighted(imageROI(cv::Rect(385,270,logo.cols,logo.rows)),1.0,

       logo,0.5,0.,imageROI(cv::Rect(385,270,logo.cols,logo.rows)));

 

cv::merge(channels,image1);

 

cv::namedWindow("with logo 3");

cv::imshow("with logo 3",image1);

 

cv::waitKey();

 

return 0;

 

}