DevelopmentTool/OpenCV

[OpenCV] Mat 클래스의 픽셀 접근 방법

유제필 2022. 11. 17. 08:46

OpenCV 에서 제공하는 픽셀(원소) 접근 방법은 여러 가지가 있다.

Mat::at();

template<typename _Tp> _Tp& Mat::at(int y, int x)

y : 참조할 행 번호
x : 참조할 열 번호
반환값 : (_Tp& 타입으로 형 변환된) y번째 행, x번째 열의 원소 값(참조)

Mat::at() 함수는 보통 행과 열을 나타내는 2개의 정수를 인자로 받아 해당 위치의 픽셀 값을 참조 형식으로 반환한다.

템플릿을 사용하는 템플릿 함수로서 여러 가지 형태로 재정의가 되어 있다.

템플릿 함수로 지정되어 있기 때문에 사용할 때 자료형을 명시적으로 지정해야 한다.

예를 들어 Mat 행렬의 타입이 CV_8UC1이면 uchar 자료형을, CV_32FC1 타입이라면 float 자료형을 지정한다.

만약 CV_8UC3 타입을 사용하는 3채널 컬러 영상이라면, OpenCV에서 정의한 Vec3b 자료형을 명시해서 사용한다.

1번 째 인자는 (x, y) 좌표계에서 x를, 2번 째 인자는 y 좌표에 해당한다.

그러므로 Mat 타입의 변소 img에 그레이스케일 영상이 저장되어 있을 경우 (x, y) 좌표 픽셀 값을 참조하려면

img.at<uchar>(y, x) 형태로 코드를 작성한다. 만약 함수로 전달된 인자의 좌표가 행렬 크기를 벗어날 경우 에러가 발생한다.

Mat mat 1 = Mat::zeros(3, 4, CV_8UC1);

for(j = 0; j < mat1.rows; j++) {
   for(i = 0; i < mat1.cols; i++) {
       mat1.at<uchar>(j, i)++;
     }
}

위 코드는 모든 원소 값이 0으로 초기화(Mat::zeros)된 CV_8UC1 타입의 행렬 1mat1을 정의하고 모든 원소 값을 1만큼 증가시킨다.

mat1은 0으로 초기화된 3 x 4 행렬이며, 각 원소는 uchar 자료형을 사용한다.

for문 중 바깥 쪽(변수 j) for문은 행렬의 전체 행에 대한 반복이고, 변수 j는 각 행의 번호를 나타낸다.

그러므로 안쪽 for문은 j 번째 행의 전체 열에 대한 반복이고, 변수 i는 각 열의 번호를 나타낸다.

Mat::at() 함수가 행렬 원소를 참조로 반환하기 때문에 Mat::at() 함수의 반환값을 변경하려면 mat1 행렬 원소도 함께 변경된다.

Mat 행렬에서 특정 원소의 위치를 나타낼 때 행과 열 번호를 0- 기반으로 표현하기 때문에,

mat1 행렬에서 먼 1번 째 원소는 mat1.at<uchar>(0, 0) 형태로 참조한다.

행 단위로 수행하는 것이 아닌 임의 좌표 원소에 자주 접근하는 경우 편리하다.

Mat::ptr()

template<typename _Tp>
_Tp* Mat::ptr(int y)

y : 참조할 행 번호
반환값 : (_Tp* 타입으로 형 변환된) y번째 행의 시작 주소

Mat::ptr() 함수는 Mat 행렬에서 특정 행의 1번 째 원소 주소를 반환한다.

템플릿 함수로 정의되어 있어 Mat::ptr() 함수를 사용할 때 행렬 원소의 자료형을 지정해야 한다.

Mat::ptr() 함수는 지정한 자료형의 포인터를 반환하며, 이 포인터를 이용하여 지정한 행의 원소에 접근할 수 있다.

for(j = 0; j < mat1.rows; j++) {
   uchar* p = mat1.ptr<uchar>(j);
   for(i = 0; i < mat1.cols; i++) {
      p[i]++;
   }
}

 

바깥쪽 for문은 행렬의 전체 행에 대한 반복이고, 변수 j는 각 행의 번호를 나타낸다.

즉, mat1.ptr<uchar>(j) 코드는 j 번째 행 원소의 시작 주소를 반환한다.

이 주소를 포인터형 변수 p에 저장하면 이후 p를 1차원 배열처럼 사용하여 해당 행의 원소에 접근할 수 있다.

즉, j번째 행의 0번째 열 원소는 p[0]이고, 1번째 열은 p[1], 마지막은 p[mat1.cols - 1] 형식으로 접근할 수 있다.

Mat::ptr() 함수를 통해 얻은 행의 시작 주소를 이용하여 각 행의 모든 픽셀을 1차원 배열처럼 접근한 경우,

행렬의 가로 크기를 벗어나는 위치에 접근하지 않도록 주의해야 한다.

Mat::ptr() 함수를 이용해서 픽셀 값에 접근하는 방법 경우에 행 단위로 행렬 원소를 참조하는 경우 유용하다.

MatIterator_ 반복자

Mat::at() 또는 Mat::ptr 함수를 사용하여 행렬의 원소를 참조할 경우, 함수 인자로 전달된 값이

행렬의 크기를 벗어나게 되면 에러가 발생한다.

이러한 단점을 해소하기 위해 OpenCV는 반복자(iterator) 개념을 도입하여 행열 원소를 참조할 수 있다.

즉, Mat 행렬 원소 참조를 위한 반복자 변수를 만들어서 행렬 크기에 상관없이 행렬 전체 원소를 차례대로 참조하는 방식이다.

반복자 클래스의 이름은 MatIterator_ 이며, 템플릿으로 정의된 클래스이므로 사용할 때에는

Mat 행렬 타입에 맞는 자료형을 지정해주어야 한다.

MatIterator_ 클래스를 사용하는 방식은 C++ 참조자의 방식과 유사하다.

Mat::begin() 함수를 이용하여 첫 번째 원소 위치를 얻을 수 있고,

Mat::end() 함수를 이용하여 마지막 원소 바로 다음 위치를 얻을 수 있다.

for (MatIterator_<uchar> it = mat1.begin<uchar>(); it != mat1.end<uchar>(); ++it) {
   (*it)++;
}

MatIterator_<uchar> 타입의 변수 it를 선언하고, mat1.begin<uchar>() 함수의 반환값으로 초기화한다.

it 값이 mat1.end<uchar>() 와 같아질 때까지 위치를 증가시키면서, 해당 위치 원소 값을 1씩 증가시킨다.

이때 반복자 변수 it가 가리키는 원소 값을 참조하기 위해 (*it) 형태로 코드를 작성한다.