1. 좌표계와 행렬
-
w×h 영상에서 사용되는 좌표계
(0, 0) | (0, 1) | ⋯ | (0, w-1) |
---|
(1, 0) | (1, 1) | ⋯ | (1, w-1) |
⋮ | ⋮ | ⋱ | ⋮ |
(h-1, 0) | (h-1, 1) | ⋯ | (h-1, w-1) |
-
M×N 행렬로도 표현 가능하다. 위 좌표계를 Transpose한 형태이므로, 혼동을 막기 위해 작업 전에 정의를 명확히 하자.
⎣⎢⎢⎢⎢⎡a1,1a2,1⋮aM,1a1,2a2,2⋮aM,2⋯⋯⋱⋯a1,Na2,N⋮aM,N⎦⎥⎥⎥⎥⎤
2. C++에서 픽셀 표현
2.1. Grayscale Image
typedef unsigned char BYTE;
typedef unsigned char uint_8t;
typedef unsigned char uchar;
- 한 픽셀의 범위 [0, 255]
- unsigned char 데이터 타입으로 표현(1Byte)
2.2. Color Image
class RGB
{
unsigned char R;
unsigned char G;
unsigned char B;
};
- unsigned char 자료형 3개를 멤버 변수로 가지는 배열/구조체(3Byte)
3. C++에서 영상 표현
3.1. 정적 2차원 배열의 생성
unsigned char a[480][640] {};
{}
: 초기값을 default값으로 설정. 배열의 모든 원소값이 0으로 초기화
- 단점
- 배열의 크기를 미리 알고 있어야 하기 때문에 다양한 크기의 영상을 표현하기에 부적절
- 대략 1MB까지 가능한 Stack 영역에 메모리 할당하기 때문에 대략 2MB인 FHD는 저장을 할 수 없음
3.2. 동적 2차원 배열
[출처: 영상 데이터 구조와 표현 방법 - 황선규 강사]
3.2.1. 생성
int w = 640;
int h = 480;
unsigned char** p;
p = new unsigned char*[h];
for (int i = 0; i < h; i++) {
p[i] = new unsigned char[w] {};
}
- new 연산자는 동적 메모리 할당을 수행하며, 이를 통해 Heap 영역에 메모리를 할당한다. x86는 2GB, x64는 8TB까지 할당 가능하다. (?)메모리 사용이 끝난 후에는 해제한다.
- 변수 w에 640, h에 480이 할당된다. p는 2차원 포인터이며, unsigned char 유형을 가리키는 포인터 배열을 나타낸다. p 내부에는 h개의 unsigned char* 포인터가 있다. 각 포인터는 w개의 unsigned char를 가리키는 배열을 가리킨다. p는 h행, w열을 갖는 2차원 배열의 구조를 나타낸다. 반복문을 사용하여 p 내부의 각 포인터를 동적으로 할당하고, w 크기의 배열로 초기화하였으므로, p 내부의 각 unsigned char 배열은 w개의 요소를 가지며, 이 요소는 초기화되어 0 또는 기본값으로 설정된다.
3.2.2. 원소에 접근
for (int y = 0; y < h; y++) {
for (int x = 0; x < w; x++) {
p[y][x] = p[y][x] + 10;
}
}
→→→→→→
→→→→→→
→→→→→→
→→→→→→
- 위 화살표 방향대로 2차원 배열 p의 모든 원소 값을 10씩 증가시킨다. p가 만들어진 과정을 생각해보면 이 방향이 ↓ 방향보다 효율적이다.
3.2.3. 메모리 해제
for (int y = 0; y < h; y++) {
delete[] p[i];
}
delete[] p;
- 동적 2차원 배열 생성의 역순으로 해제
- delete 구문에서 괄호 연산자
[]
를 반드시 사용
3.3. 대용량 1차원 메모리
3.3.1. 메모리 할당 후 영상 데이터 저장하기
int w = 10, h = 10;
unsigned char* data = new unsigned char[w * h] {};
...
delete[] data;
3.3.2. 특정 좌표 (x, y) 위치의 픽셀 값 참조하기
unsigned char& p1 = *(data + y*w + x);
- p1은 참조변수이다.
data + y*w + x
은 data의 y*w + x번째 요소의 주소이다.
*(data + y*w + x)
은 data의 y*w + x번째 요소의 값이다.
3.4. 영상 데이터 저장 클래스(간단한)
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;
};
- 동적 할당과 픽셀 값 참조를 이용해서 영상 데이터를 저장할 수 있는 MyImage 클래스
- 편의를 위해 모든 데이터 멤버를 public 지시자로 선언
data
: 픽셀 데이터를 저장하기 위해서 동적 할당한 메모리 공간의 시작 주소를 가리킬 포인터 변수
- MyImage 클래스의 디폴트 생성자를 보면 w, h, data를 0으로 초기화한다.
- 2개의 정수값을 생성자 인자로 받는 형태의 MyImage 생성자에서는, w와 h를 초기화하고, data 포인터 변수는 new 연산자를 이용해서 데이터 공간을 할당하고 그 시작 주소를 가리키도록 설정한다.
- 소멸자에서는 만약 메모리 할당이 있었다면, 해당 메모리를 해제한다.
- at 함수를 이용해서 (x, y) 좌표에 있는 픽셀 값을 참조로 반환해서, 해당 픽셀 값을 읽어올 뿐만 아니라 픽셀 값을 설정할 수도 있도록 한다.
📙강의 - 강사 황선규