Lasiyan
Vision

이미지를 데이터로 표현하는 방법 2가지

#영상처리#C++#이미지#배열#동적 할당

본 문은 C++ 11 기반으로 작성되었습니다.


배열을 활용한 이미지 표현

이미지 처리를 하다보면 배열을 주로 사용한다. 이미지의 경우 2차원 배열이라고 생각하면 쉽고 따라서 1차원 배열보다 2차원 배열을 사용하는 것이 처리의 직관성을 높일 수 있다.

먼저 배열의 경우 동적할당을 통해 사용하는데 사용하는 코드는 다음과 같다.

BYTE** image = new BYTE*[ (이미지)세로 ];
image[0] = new BYTE[ 세로 * (이미지)가로 ];

for( int i = 1; i < 세로; i++ )
{
    image[i] = image[i - 1] + 가로;
}

memset( image, 0, 가로*세로 );

// 사용이 끝난 후
delete[] image[0];
delete[] image;

코드를 살펴보면 1번 라인에서 BYTE 더블 포인터형 변수 image를 생성하고 세로의 길이만큼 BYTE* 형을 생성한다.

현재까지 생성된 것은 실질적인 데이터를 담는 것이 아닌, BYTE 자료형을 가르킬 포인터를 생성한 것이다.

그리고 2번 라인에서 전체적인 이미지 데이터를 담을 배열을 생성한다. 위에서 BYTE* 형을 세로 개수만큼 생성했는데

첫 번째 포인터에 전체 이미지 크기(가로*세로) 만큼 공간을 할당한다.

그리고 4번 라인~7번 라인이 중요한데

세로 개수만큼 생성된 BYTE* 중 0 index, 즉 첫 번째 포인터는 이미지 전체를 가리키도록 했다.

그러므로 다음 두 번째 포인터, 즉 인덱스로는 1 번(i = 1)부터 가로 길이만큼 포인터를 이동시키며 배열을 가리키도록 한다.

이것을 그림으로 나타내면 다음과 같다.

이미지를 데이터로 표현하는 방법 2가지

벡터를 활용한 이미지 표현

이미지를 배열로 표현하는 방법과 동일하게 벡터로 표현하는 방법이 있다. 이것의 장점은 시스템 상 조금 더 안정적이며 new를 통해 발생할 메모리 누수를 방지할 수 있다는 장점이 있다.

벡터로 이미지를 표현하는 방법은 다음과 같다.

#include <vector>
using namespace std;

vector< vector<픽셀자료형>> 벡터명( 세로, vector<픽셀자료형>(가로) );

세로 길이만큼 먼저 벡터를 생성하고 각 벡터의 인자에 가로 길이만큼의 벡터들을 생성한 것이다.

아래 예시는 2*2의 이미지를 2차원 벡터로 표현한 것이다.

#include <iostream>
#include <vector>

using namespace std;

int w = 2;
int h = 2;

int main()
{
    vector< vector<int> > img( h, vector<int>(w) );

    img[0][0] = 1;
    img[0][1] = 2;
    img[1][0] = 3;
    img[1][1] = 4;

    for(int j = 0; j < h; j++)
    {
        for(int i = 0; i < w; i++)
        {
            cout << img[j][i] << " ";
        }
        cout << endl;
    }
    
    return 0;
}

확실히 사용도 간편하고 직관적인 장점이 있다.

그러나 push_back을 사용하는 경우 주의할 점이 있다. 먼저 다음 코드를 보자

#include <vector>

using namespace std;

int w = 2;
int h = 2;

int main()
{
    vector< vector<int> > img( h, vector<int>(w) );

    img[0].push_back(1);
    img[0].push_back(2);
    img[1].push_back(3);
    img[1].push_back(4);

    for(int j = 0; j < h; j++)
    {
        for(int i = 0; i < w; i++)
        {
            cout << img[j][i] << " ";
        }
        cout << endl;
    }
    
    return 0;
}

위 코드는 이전 코드에서 데이터를 삽입하는 부분을 = 대신 push_back으로만 변경한 것이다.

그러나 이렇게 사용할 경우 출력해보면 0(또는 쓰레기) 값만 출력하는 문제가 발생한다.

이는 벡터의 특성에서 문제를 찾을 수 있는데 10번 라인에서 벡터를 생성할 때 vector(w) 를 사용하였다.

이렇게 하면 벡터 내부에는 w 만큼의 빈 공간이 할당된다. 따라서 해당 코드의 경우

0이 2개(w값) 만큼 할당 된 이후 push_back을 통해 1과 2 또는 3과 4가 삽입된다. 즉

0 0 1 2 또는 0 0 3 4가 되는 셈이다.

따라서 push_back을 통해 데이터를 삽입하고자 할 경우 vector를 생성할 때 2번째 인자를 주지 않음으로 해결할 수 있다.

#include <iostream>
#include <vector>

using namespace std;

int w = 2;
int h = 2;

int main()
{
    vector< vector<int> > img( h );

    img[0].push_back(1);
    img[0].push_back(2);
    img[1].push_back(3);
    img[1].push_back(4);

    for(int j = 0; j < h; j++)
    {
        for(int i = 0; i < w; i++)
        {
            cout << img[j][i] << " ";
        }
        cout << endl;
    }

    return 0;
}