When will you grow up?

4. BitPlane 함수 만들기(Bit-Plane Slicing) 본문

02. Study/Computer Vision(openframworks&opencv)

4. BitPlane 함수 만들기(Bit-Plane Slicing)

미카이 2016. 9. 17. 15:03

이번에는 비트플레인 함수를 만들겠습니다


BitPlane 이란?

디지털 정보 용어로써, 우리말로는 비트 평면이라고 얘기를 한다.

8bit 그레이 영상의 한 픽셀은 8개의 bit로 구성됩니다^^
즉, 픽셀값은 00000000(0)~11111111(255)까지의 값을 가질 수 있죠
8개의 bit중에서 최상위 비트를 MSB(Most Significant Bit), 최하위 비트를 LSB(Least Significant Bit)라고 합니다

위의 표는 이진수를 간단히 십진수로 변환할 수 있는 방법입니다..^^
특히 4bit짜리는 8-4-2-1 코드로 잘 알려져 있죠..^^

예를들어, 이진수 0110은 8과 1의 값은 0이고, 4와 2의 값은 1이기 때문에 4 + 2 = 6이 됩니다^^
이진수 '1100 0000'값은 128 + 64 = 192가 되는거죠..^^


사실 이런 변환 방법이 중요한게 아니라, 상위 비트로 갈수록 값이 커진다는 것이 핵심입니다

쉽게 말하자면, 최상위 비트는 가장 큰 값이므로 가장 중요하다 (Most Significant)
최하위 비트는 가장 작은 값이므로 가장 중요하지 않다 (Least Significant)가 되는 것이죠..^^

0~255의 범위를 놓고 봤을때, 예를 들자면..
'0001 0100'(20)과 '0001 0101'(21)의 최하위 1비트는 1차이가 나므로 별로 중요하지 않다는 것이죠^^
하지만 '0001 0100'(20)과 '1001 0100'(148)은 128차이가 나버립니다..
영상의 픽셀값으로 치자면 거의 검은색(20)과 중간보다 밝은 회색(148)이 되어버리는 거죠..^^;
이런 차이는 사람눈으로도 쉽게 구별할 수 있을만큼 확연한 차이입니다


실제로 색을 비교해볼까요?^^
픽셀값 20()과 21()은 거의 구분이 불가능하지만,
픽셀값 20()과 148()은 확연한 차이를 보입니다..^^

둘 다 똑같이 한 비트의 값만 달라졌는데도 완전히 다른 결과를 보입니다^^
값이 바뀌어도 원본과 큰 차이가 없기 때문에 별로 신경쓸 필요없는 (즉, 중요하지 않은)
하위 몇 비트 정도는 잘라내 버려도 상관없다는 것이 바로 절단 부호화의 포인트인 것이죠..^^

하위 4bit정도를 잘라내버려도 최대 1111(15)정도의 손실밖에 없으며,
사람눈으로 볼 때는 영상을 알아보는데 별 문제가 없습니다 (물론, 화질의 손상은 있습니다만..)
대신 데이터 양은 절반으로 줄어들게 되는 것이죠..^^

반면 상위 4bit를 잘라버리면 최대 11110000(240)의 손실이 발생합니다..
8bit 그레이 영상의 픽셀값이 0~255사이의 값을 갖는데..
원본과 240이 차이가 난다면, 그건 원본 데이터와는 아예 달라져버리는 것이죠..^^;;


-출처 : http://egloos.zum.com/realheart/v/2254010     (하얀추억 블로그) 위 내용 문제가 되면 바로 지우겠습니다.


핵심 함수는 이래와 같습니다.


void getBitPlane(Mat &srcImg, Mat &dstImg, int n)//(원본이미지 , bitplane될 이미지,n칸 

{

uchar mask = 0x01 << n; //쉬프트연산자를 이용하여 n칸 밀어버림니다. 나머지 밀                                           //어져 버린 부분은 0으로 숫자가 초기화 됨니다.


Mat_<uchar> s = Mat_<uchar>(srcImg);

Mat_<uchar> d = Mat_<uchar>(dstImg);


/*for (int r = 0; r < srcImg.rows; r++)

{

for (int c = 0; c < srcImg.cols; c++)

{

uchar pixelvalue = s(r, c);

uchar onezero = pixelvalue & mask;

if (onezero > 0) d(r, c) = 255;

else d(r, c) = 0;

}

}*/

for (int r = 0; r < srcImg.rows; r++)

{

uchar *p = srcImg.ptr<uchar>(r);//첫번째행의 값을 가져온다.

uchar *dp = dstImg.ptr<uchar>(r);

for (int c = 0; c < srcImg.cols; c++)

{

uchar pixelvalue = p[c];

uchar one_zero = pixelvalue & mask;

if (one_zero > 0)

dp[c] = 255;

else

dp[c] = 0;

//dp[c] = one_zero ? 255 : 0;

}

}

}


하나는 주석처리 되어있는 부분을 주석을 푸시고 아래꺼 for문을 주석처리하셔도 같은 결과가 나옴니다. 


원본 이미지


단계별로 bitplane 된 이미지 0~7




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



#include "ofApp.h"



const int width = 720;

const int height = 480;

unsigned char pixel[width*height * 3];

ofImage img, result;

ofImage photo;



Mat srcImg;

Mat bitPlane[8];



void getBitPlane(Mat &srcImg, Mat &dstImg, int n)

{

	uchar mask = 0x01 << n;

	



	Mat_ s = Mat_(srcImg);

	Mat_ d = Mat_(dstImg);



	/*for (int r = 0; r < srcImg.rows; r++)

	{

		for (int c = 0; c < srcImg.cols; c++)

		{

			uchar pixelvalue = s(r, c);

			uchar onezero = pixelvalue & mask;

			if (onezero > 0) d(r, c) = 255;

			else d(r, c) = 0;

		}

	}*/

	for (int r = 0; r < srcImg.rows; r++)

	{

		uchar *p = srcImg.ptr(r);//첫번째행의 값을 가져온다.

		uchar *dp = dstImg.ptr(r);

		for (int c = 0; c < srcImg.cols; c++)

		{

			uchar pixelvalue = p[c];

			uchar one_zero = pixelvalue & mask;

			if (one_zero > 0)

				dp[c] = 255;

			else

				dp[c] = 0;

			//dp[c] = one_zero ? 255 : 0;

		}

	}

}





//--------------------------------------------------------------

void ofApp::setup() {





	photo.load("myimage2.jpg");

	

	



	srcImg = Mat(photo.getHeight(),photo.getWidth(),CV_8UC3,photo.getPixels().getData());

	Mat grayImg;

	cvtColor(srcImg, grayImg, COLOR_RGB2GRAY);

	cv::imshow("grayImage", grayImg);



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

	{

		bitPlane[i] = grayImg.clone();

		getBitPlane(grayImg, bitPlane[i], i);

		string name = "bitplane" + std::to_string(i);

		cv::imshow(name, bitPlane[i]);

	}







}


Comments