2018-02-15 15:52:10

▶ 영상 내 컬러 개수 감소시키기


컬러 영상의 경우 각 픽셀당 세개의 색으로 표현된다. R, G, B 모두 256개의 값을 가지고 있으므로 총 256 * 256 * 256 = 16,777,216개의 색을 표현할 수 있다. 1600만개가 넘는 수다. 때로는 분석 복잡도를 줄이기 위해서 컬러 개수를 감소시키는 것이 좋다. 컬러를 감소시키는 방법에는 여러가지가 있을 수 있지만 가장 직관적이면서도 간단한 방법을 공부해보려고 한다. 


R, G, B 각 차원당 256개(0~255)인 것을 128개, 64개, 32개, 16개 등으로 줄이기 위한 가장 간단한 방법은 비슷한 밝기세기들을 하나로 취급하는 것이다. 예를 들어 32개로 줄인다고 하면, 0~7에 해당하는 세기는 전부 다 중간세기값에 해당하는 4로, 8~15는 12로, 16~23은 20으로 바꾼다. 이런 식으로 모든 차원에 대해서 감소시키면, 256 * 256 * 256 = 약1600만개였던 컬러개수가 32 * 32 * 32 = 32,768개로 약 3만개로 줄어든다. 그만큼 분석 복잡도가 많이 줄어든다. 이것을 코드로 구현한 것은 아래와 같다. 



#include <iostream>

#include <opencv2/core.hpp>

#include <opencv2/highgui.hpp>


void colorReduce(const cv::Mat &image, cv::Mat &result, int div); 


int main()

{

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


cv::Mat result;


result.create(image.rows, image.cols, image.type());


colorReduce(image, result, 8); % 채널당 밝기레벨을 32개로 줄임, 256/8 = 32


cv::imshow("before", image);

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


cv::waitKey(0);


    return 0;

}


void colorReduce(const cv::Mat &image, cv::Mat &result, int div)

{

int nl = image.rows; // 행 개수


std::cout << image.channels() << std::endl; // 이미지의 채널 수 확인, 컬러 영상인 경우 3, 흑백 영상인 경우 1. 


// 각 행의 원소 총 개수

int nc = image.cols * image.channels();


for (int j = 0; j < nl; j++)

{

// j행의 주소 얻기

const uchar* data_in = image.ptr<uchar>(j);

uchar* data_out = result.ptr<uchar>(j);


for (int i = 0; i < nc; i++)

{

// 각 화소 처리

data_out[i] = data_in[i] / div * div + div / 2;

}

}

}




코드 구현결과 약 1600만개의 컬러개수로 표현되었던 이미지가 3만개의 컬러개수로 표현되었다. 외관상 큰 차이를 알아차리기 어렵다. 



원본이미지


div = 8일 때 결과이미지



만약 채널당 컬러개수를 좀더 감소시켜서 4개가 되게 하면, 4 * 4 * 4 = 64개의 컬러로 영상이 표현된다. 쉽게 말해 1300만개의 크래용 대신 64개의 크래용으로 그림을 그린다는 것이다. 아래 결과이미지를 보면 이번에는 원본이미지와 꽤 차이가 있음을 알 수 있다. 좀 더 만화틱해졌다고나 할까. 


colorReduce(image, result, 64); % 채널당 밝기 레벨을 4개로 줄임, 256/64 = 4


div = 64일 때 결과이미지



좀 더 극단적으로 채널당 밝기레벨을 2개로 감소시켜보자. 0부터 127은 모두 64로, 128부터 255는 모두 192으로 매핑된다. 컬러개수는 1300만개였던 것이 2 * 2 * 2 = 8개가 된다. 카메라로 찍은 사진이 마치 손으로 그린 그림과 같아졌다. 


colorReduce(image, result, 128); % 채널당 밝기 레벨을 2개로 줄임, 256/128 = 2


div = 128일 때 결과이미지




▶ 좀 더 알고 넘어갈 것들


1) 컬러 감소 공식


컬러를 감소시키기 위한 코드를 좀 더 자세히 살펴보자. 


data_out[i] = data_in[i] / div * div + div / 2;


입력 영상의 각 채널의 밝기값들을 div로 나눠주는데 div가 정수값이기 때문에 나머지들은 잃게 된다. 그리고 div를 곱해준 후 div/2를 더해준다. 따라서 div가 8인 경우 0부터 7은 모두 4의 값을 갖게 되고, 8부터 15는 모두 12의 값을 갖게 된다. 


0-7 => 4

8-15 => 12

16-23 => 20

...

248-255 => 252


결과적으로 이미지를 표현하기 위해 사용되는 컬러의 갯수가 줄어든다. 



2) const 참조


함수 내에서 참조자를 통한 값의 변경을 진행하지 않을 경우, 참조자를 const로 선언한다. 이렇게 하면 함수의 몸체를 보지 않고 함수의 원형만 봐도 값의 변경이 일어나지 않음을 알 수 있다는 장점이 있다. 



3) create 메소드


create 메소드는 새로운 크기와 타입으로 영상을 다시 할당해야 할 때 사용한다.


result.create(image.rows, image.cols, image.type());


result 영상의 크기와 타입을 원본이미지의 크기와 타입으로 할당해주었다. 컬러 감소시킨 영상을 넣어주기 위함이다.  




<참고자료>

[1] 로버트 라가니에 지음, 이문호 옮김, "OpenCV를 활용한 컴퓨터 비전 프로그래밍 3/e", 에이콘