[컴퓨터 비전] 07 Memory Management / Pixel Access

이찬영·2024년 2월 19일
0

컴퓨터 비전

목록 보기
6/20

[컴퓨터비전 STUDY / KOCW 한동대학교 황성수 교수님 강의 Review]


Memory Management 에는 Shallow copy, Deep copy 방식이 있다.

Shallow copy

Mat(행렬) 데이터 구조는 header와 data 영역으로 구성되어 있다.
Shallow copy의 경우, data의 주소가 복사된다.

-> dest 행렬과 source 행렬이 동일한 유형인 경우 크기, copyTo는 대상 행렬의 주소를 변경하지 않는다.


Deep copy

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 방식이 있다.

at operator

다음은 특정 위치의 픽셀 값을 읽어오는 함수이다.

image.at<DATA_TYPE>(WANT_ROW, WANT_COL)

  • at<DATA_TYPE> : 행렬 요소의 데이터 타입(int, double, uchar 등)을 지정
  • WANT_ROW : Y 좌표에 해당함
  • WANT_COL : X 좌표에 해당함

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);
 }

결과는 다음과 같다.


using pointer

다음은 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

다음은 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

다음은 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);
 }
profile
자율주행, AI, 클라우드

0개의 댓글