[컴퓨터비전 STUDY / KOCW 한동대학교 황성수 교수님 강의 Review]
Memory Management 에는 Shallow copy, Deep copy 방식이 있다.
Mat(행렬) 데이터 구조는 header와 data 영역으로 구성되어 있다.
Shallow copy의 경우, data의 주소가 복사된다.
-> dest 행렬과 source 행렬이 동일한 유형인 경우 크기, copyTo는 대상 행렬의 주소를 변경하지 않는다.

deep copy에는 clone()이 사용된다.

다음은 Shallow/Deep copy를 수행하는 C++ 코드이다.
int main() {
// 3x3 크기의 double 타입 행렬 m1을 생성하고 초기화
Mat m1 = (Mat_ < double >(3, 3)
<< 1, 2, 3, 4, 5, 6, 7, 8, 9);
Mat m_shallow = m1; // shallow copy 방식
Mat m_deep = m1.clone(); // deep copy 방식
cout << "m1 =\n" << m1 << endl << endl; // m1 (원본)
cout << "m_shallow =\n" << m_shallow << endl << endl; // shallow copy (주소값)
cout << "m_deep =\n" << m_deep << endl << endl; // deep copy (data를 통째로 복사함)
// Update m1
m1.at < double >(0, 0) = 100; // m1 (원본) 에서 값 변경
cout << "m1 =\n" << m1 << endl << endl; // m1 (원본)
cout << "m_shallow =\n" << m_shallow << endl << endl; // shallow copy
cout << "m_deep =\n" << m_deep << endl << endl; // deep copy
waitKey(0);
}
결과는 다음과 같다.

Pixel access 에는 at operator, pointer, data member function 방식이 있다.
다음은 특정 위치의 픽셀 값을 읽어오는 함수이다.
image.at<DATA_TYPE>(WANT_ROW, WANT_COL)
at operator로 pixel access를 수행하는 것은 안전하다.
하지만 느리다는 단점이 있다.
다음은 at operator를 이용하여 픽셀 값을 얻어오는 C++ 코드이다.
int main() {
Mat image, image_gray;
int value, value_B, value_G, value_R, channels;
// lena.png를 컬러 이미지로 로드함
image = imread("lena.png");
// lena.png를 흑백 이미지로 로드함
image_gray = imread("lena.png", 0);
//try both image & image_gray
//channels = image_gray.channels(); 이미지의 채널 수를 결정함
channels = image.channels();
//At operator
switch (channels) {
case 1: // 채널 수가 1개일 때 (흑백)
value = image.at<uchar>(50, 100);
cout << "value: " << value;
break;
case 3: // 채널 수가 3개일 때 (칼라)
value_B = image.at<Vec3b>(50, 100)[0]; // B 값
value_G = image.at<Vec3b>(50, 100)[1]; // G 값
value_R = image.at<Vec3b>(50, 100)[2]; // R 값
// (50, 100) 픽셀 값을 읽어옴
cout << "value at (100,50): " << value_B
<< " " << value_G << " " << value_R << endl;
break;
}
waitKey(0);
}
결과는 다음과 같다.

다음은 pointer을 이용하여 특정 위치의 픽셀 값을 읽어오는 C++ 코드이다.
int main() {
// "lena.png" 이미지를 컬러 이미지로 로드함
Mat image = imread("lena.png");
int value, value_B, value_G, value_R, channels;
channels = image.channels();
//Pointer, 50번째 행에 대한 포인터를 얻음
uchar* p;
p = image.ptr<uchar>(50);
// (100, 50) 위치의 픽셀에 대한 BRG 색상 값을 조회함
value_B = p[100 * channels + 0]; // B 채널에 대한 인덱스
value_G = p[100 * channels + 1];
value_R = p[100 * channels + 2];
cout << "value at (100,50): " << value_B << " "
<< value_G << " " << value_R << endl;
waitKey(0);
}
결과는 다음과 같다.

다음은 member function을 이용해 pixel access를 하는 방법이다.
빠르지만, 부적절한 access를 파악하기 어렵다는 단점이 있다.
int main() {
Mat image;
int value, value_B, value_G, value_R, channels;
image = imread("lena.png");
channels = image.channels();
// Data member function
// image.data는 이미지의 원시 데이터에 대한 포인터를 제공
uchar* data = (uchar*)image.data;
// (100, 50) 위치의 픽셀에 대한 B, G, R 색상 값을 조회
value_B = data[(50 * image.cols + 100) * channels + 0];
value_G = data[(50 * image.cols + 100) * channels + 1];
value_R = data[(50 * image.cols + 100) * channels + 2];
cout << "value at (100,50): " << value_B << " "
<< value_G << " " << value_R << endl;
waitKey(0);
}
결과는 다음과 같다. 위와 동일하다.

다음은 MatIterator를 활용하여 pixel access를 하는 방식이다.
MatIterator은 cv::Mat 객체, 즉 이미지나 행렬의 모든 요소를 순회할 때 사용된다.
int main() {
// lena.png를 칼라, 흑백 모드로 읽어서 로드함
Mat image = imread("lena.png");
Mat gray = imread("lena.png", 0);
int value, value_B, value_G, value_R;
// try both image & gray
int channels = image.channels();
MatIterator_ <uchar> it, end;
MatIterator_<Vec3b> it3, end3;
switch (channels) {
case 1:
// 이미지의 처음 위치부터 끝 위치까지 순회하면서 픽셀값을 가져옴
for (it = image.begin<uchar>(), image.end<uchar>(); it != end; ++it) {
value = *it;
cout << "value: " << value << endl;
}
break;
case 3:
// 이미지의 처음 위치부터 끝 위치까지 순회하면서 픽셀값을 가져옴
for (it3 = image.begin<Vec3b>(), end3 = image.end<Vec3b>(); it3 != end3; ++it3) {
value_B = (*it3)[0];
value_G = (*it3)[1];
value_R = (*it3)[2];
cout << "B: " << value_B << ", G: " << value_G << ", R: " << value_R << endl;
}
break;
}
waitKey(0);
}