영상 데이터 구조와 표현 방법

영상(image)이란?

  • 픽셀이 바둑판 모양의 격자에 나열되어있는 형태 (2차원 행렬)
    • 픽셀은 영상의 기본 단위로 Picture Element의 줄임말로 화소라고도 부름
그레이스케일 영상(Grayscale Image)
  • 흑백 사진 처럼 색상 정보가 없이 오직 밝기 정보만으로 구성된 영상
    • 밝기 정보를 256단계로 표현 [0,255]
    • C/C++ 에서 unsigned char로 표현함 : 1Byte
typedef unsigned char BYTE; // Windows
typedef unsigned char uint8_t; // Linux
typedef unsigned char uchar; // OpenCV

image

트루컬러 영상(Truecolor Image)
  • 컬러 사진 처럼 다양한 색상을 표현할 수 있는 영상
    • RGB 색 성분으로 256단계로 표현 256 * 256 * 256 = 16,777,216 색상 표현 가능
    • unsigned char 자료형 3개 있는 배열 또는 구조체로 표현 : 3Byte
class RGB
{
    unsigned char R;
    unsigned char G;
    unsigned char B;
};

image

영상에서 사용되는 좌표계

image

  • 그레이스케일 영상에서 픽셀 값 분포

image

  • 트루컬러 영상에서 픽셀 값 분포

image

영상 데이터 표현 방법
정적 2차원 배열의 생성
unsigned char a[480][640] {};
  • unsigned char : 1Byte 사용
  • 2차원 배열 전체의 크기 만큼 메모리공간이 연속적으로 할당되므로 640 * 480 = 307200 Byte
    • 배열의 크기를 미리 알고 있어야 함
    • 다양한 크기의 영상을 표현하기에 부적절함
동적 2차원 배열의 생성
int w = 640;
int h = 480;

unsigned char** p; // 
p = new signed char*[h];
for (int i = 0; i<h; i++) {
	p[i] = new unsigned char[w] {};
}
  • 행 단위로만 연속된 메모리 공간이 보장됨
  • 프로그램 동작 중 다양한 크기의 영상을 생성할 수 있음
  • new 연산자를 사용하면, Heap 영역에 메모리를 할당 : x86의 경우 2GB 까지 할당 가능하며 x64의 경우 8TB 까지 가능

image

  • 동적 2차원 배열의 원소에 접근하는 방법
    • 2차원 배열 p의 모든 원소 값을 10씩 증가하기
for (int y=0; y<h; y++){
	for (int x = 0; x<w; x++) {
		p[y][x] = p[y][x] + 10;
	}
}

image

  • 동적 2차원 배열의 메모리 해제
    • 동적 2차원 배열 생성의 역순으로 해제하며 delete 구문에서 괄호 연산자 [] 를 반드시 사용해야 함
for(int y =0; y<h; y++)
	delete[] p[i]; // 각각의 행에 대한 메모리 공간을 먼저 해제
delete[] p; // 각각의 행을 가리키기 위한 포인터 변수 공간을 다음으로 해제
대용량 1차원 메모리 할당 후 영상 데이터 저장하기
  • 2차원 배열이 아닌 1차원 배열에 영상의 픽셀 값을 차례대로 이어 붙여서 저장하여 사용
int w = 10, h = 10;
unsigned char * data = new unsigned char[w*h] {};
...

delete [] data;

image

  • 특정 좌표(x,y) 위치의 픽셀 값 참조하기
unsigned char& p1 = *(data + y*w + x); // w는 영상의 가로 크기 , (x,y)는 좌표,  data는 시작 좌표

image

  • 간단한 형태의 영상 데이터 저장 클래스
class MyImage
{
public : 
	MyImage() : w(0), h(0), data(0) {}
	
	MyImage(int _w, int _h) : w(_w), h(_h) {
		data = new unsigned char[w*h] {} ;
	}
	
	~MyImage(){
		if(data) delete[] data;
	}
	
	unsigned char& at(int x, int y) {
		return *(data + y * w + x);
	}
		
public :
	int w, h;
	unsigned char* data;
};