[opencv] vector 클래스

글쓴이 Engineer Myoa 날짜

기타 몇개의 큰 클래스들만 진행하면 이제 기본 클래스는 종료된다.

 

#pragma warning(disable: 4819)
#include "opencv.hpp"
using namespace cv;
using namespace std;
int main()
{

	vector<int> V2(3, 0); // length(size), allocator
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	vector<int> V3(arr, arr + sizeof(arr) / sizeof(int)); // 갯수
	cout << (Mat)V3 << endl; //스트림 출력을 위해 (Mat)으로 타입캐스팅

	vector<int> V4(V3.begin(), V3.end());
	// 시작과 끝 정보로도 초기화가 가능
	cout << (Mat)V4 << endl;



	system("cls");

	// vector클래스를 사용하면 초기 데이터의 크기를 지정하지않고 push_back() 멤버함수를 이용해 데이터를 넣고
	// 사용할때는 vector[index] 처럼 배열 처럼 접근이 가능하다.

	// vector 객체에 할당된 크기는 vector::capacity()멤버 함수를 통하 알 수 있다.
	// 할당된 용량보다 크게 삽입이 일어나면 memory reallocation이 일어나며 이러한 재할당은 vector클래스의 성능저하를 초래함.
	// vector::reserve()멤버 함수로 미리 메모리 용량을 확보하고 사용하면 재할당이 빈번히 일어나는것을 방지할 수 있다.
	// C/C++자체의 배열의 불편한 점을 해결할 수 있다.


	vector<float> VV1;
	VV1.push_back(10.0f);
	VV1.push_back(20.0f);
	VV1.push_back(30.0f);
	VV1.push_back(40.0f);

	cout << "VV1.capacity() = " << VV1.capacity() << endl;
	cout << "V1V.size() = " << VV1.size() << endl;

	cout << "for: " << endl;
	for (int i = 0; i < VV1.size(); i++) {
		cout << VV1[i] << " / "; // 다음과 같이 배열처럼 index로 접근이 가능하다.
	}
	cout << endl;

	VV1.erase(VV1.begin() + 2); // 처음으로부터 +2번째 위치에 있는 30.0f가 타겟

	cout << endl << "it1: " << endl;
	// 요소하나를 지우고 난 뒤에 iterator를 이용해 요소목록을 출력
	vector<float>::iterator it1 = VV1.begin();
	for (; it1 != VV1.end(); it1++) {
		cout << *it1 << " / ";
	}
	cout << endl;

	VV1.insert(VV1.begin() + 2, 50.0f); // 지웠던 위치 +2 에 다른 요소를 삽입.
	// 10 / 20 / 50/ 40
	// 리버스하면 40 50 20 10

	vector<float>::reverse_iterator it2; // reverse하게 element를 iterating해주는 it2를 생성;
	for (it2 = VV1.rbegin(); it2 != VV1.rend(); it2++) { // rbegin과 rend를 사용해야함. 안그러면 오류
		cout << *it2 << " / ";
	}
	cout << endl;

	VV1.clear();
	cout << "VV1.capacity() = " << VV1.capacity() << endl;
	cout << "VV1.size() = " << VV1.size() << endl;

	return 0;
}

 

다음으로는, 벡터를 2차원 배열처럼 만들어 사용해보자.

#pragma warning(disable: 4819)
#include "opencv.hpp"
#include <iomanip>

using namespace cv;
using namespace std;
int main()
{

	vector<int> V2(3, 0); // length(size), allocator
	int arr[] = { 1,2,3,4,5,6,7,8,9 };
	vector<int> V3(arr, arr + sizeof(arr) / sizeof(int)); // 갯수
	cout << (Mat)V3 << endl; //스트림 출력을 위해 (Mat)으로 타입캐스팅

	vector<int> V4(V3.begin(), V3.end());
	// 시작과 끝 정보로도 초기화가 가능
	cout << (Mat)V4 << endl;



	system("cls");

	// vector클래스를 사용하면 초기 데이터의 크기를 지정하지않고 push_back() 멤버함수를 이용해 데이터를 넣고
	// 사용할때는 vector[index] 처럼 배열 처럼 접근이 가능하다.

	// vector 객체에 할당된 크기는 vector::capacity()멤버 함수를 통하 알 수 있다.
	// 할당된 용량보다 크게 삽입이 일어나면 memory reallocation이 일어나며 이러한 재할당은 vector클래스의 성능저하를 초래함.
	// vector::reserve()멤버 함수로 미리 메모리 용량을 확보하고 사용하면 재할당이 빈번히 일어나는것을 방지할 수 있다.
	// C/C++자체의 배열의 불편한 점을 해결할 수 있다.


	vector<float> VV1;
	VV1.push_back(10.0f);
	VV1.push_back(20.0f);
	VV1.push_back(30.0f);
	VV1.push_back(40.0f);

	cout << "VV1.capacity() = " << VV1.capacity() << endl;
	cout << "V1V.size() = " << VV1.size() << endl;

	cout << "for: " << endl;
	for (int i = 0; i < VV1.size(); i++) {
		cout << VV1[i] << " / "; // 다음과 같이 배열처럼 index로 접근이 가능하다.
	}
	cout << endl;

	VV1.erase(VV1.begin() + 2); // 처음으로부터 +2번째 위치에 있는 30.0f가 타겟

	cout << endl << "it1: " << endl;
	// 요소하나를 지우고 난 뒤에 iterator를 이용해 요소목록을 출력
	vector<float>::iterator it1 = VV1.begin();
	for (; it1 != VV1.end(); it1++) {
		cout << *it1 << " / ";
	}
	cout << endl;

	VV1.insert(VV1.begin() + 2, 50.0f); // 지웠던 위치 +2 에 다른 요소를 삽입.
	// 10 / 20 / 50/ 40
	// 리버스하면 40 50 20 10

	vector<float>::reverse_iterator it2; // reverse하게 element를 iterating해주는 it2를 생성;
	for (it2 = VV1.rbegin(); it2 != VV1.rend(); it2++) { // rbegin과 rend를 사용해야함. 안그러면 오류
		cout << *it2 << " / ";
	}
	cout << endl;

	VV1.clear();
	cout << "VV1.capacity() = " << VV1.capacity() << endl;
	cout << "VV1.size() = " << VV1.size() << endl;


	system("cls");

	vector< vector<int>> M1(2, vector<int>(3, 0)); //2차원 배열 vector 2*3
	M1[0][0] = 10; M1[0][1] = 20; M1[0][2] = 30;
	M1[1][0] = 40; M1[1][1] = 50; M1[1][2] = 60;

	cout << "size, capacity infomation" << endl;
	cout << M1.size() << " " << M1.capacity() << endl;
	//cout << Mat(M1) << endl;


	cout << "access use 'for':" << endl;
	for (int i = 0; i < M1.size(); i++) {
		for (int j = 0; j < M1[i].size(); j++) { // M1[i]가 가지고 있는 즉 2차원째의 요소 갯수를 max index로 지정
			cout << setw(4) << M1[i][j] << " ";
		}
		cout << endl;
	}

	// 또한 배열적 접근외 iterator로 순차접근하여 출력이 가능하다.
	vector< vector<int>>::iterator M1_it1;
	vector<int>::iterator M1_it2;

	cout << endl <<  "access use 'iterator':" << endl;
	for (M1_it1 = M1.begin(); M1_it1 != M1.end(); M1_it1++) {
		//M1_it2는 계속 reassign
		for (M1_it2 = M1_it1->begin(); M1_it2 != M1_it1->end(); M1_it2++) {
			cout << setw(4) << *M1_it2 << " ";
		}
		cout << endl;
	}


	return 0;
}

 

1차원 벡터와 마찬가지로 iterator와 array적 접근이 가능하다.

 

Vector는 여러 자료형으로 형변환이 가능하다.

1) Vector <-> Mat

#pragma warning(disable: 4819)
#include "opencv.hpp"
#include <iomanip>

using namespace cv;
using namespace std;
int main()
{
	// Vector <-> Mat 변환
	vector<Point3f> V1;
	V1.push_back(Point3f(255, 0, 0));
	V1.push_back(Point3f(0, 255, 0));
	V1.push_back(Point3f(0, 0, 255));
	V1.push_back(Point3f(0, 255, 255));

	Mat A(V1); // vector to mat;
	// equal to
	// Mat A(4,1, CV_32FC3);  memcpy(A.data, V1.data(), V1.size()*sizeof(Point3f) );

	cout << "A.channels()= " << A.channels() << endl;
	cout << "A.rows, A.cols = " << A.rows << ", " << A.cols << endl << endl;
	cout << "A = " << A << endl << endl;

	vector<Point3f> V2;
	A.copyTo (V2); // mat to vector
	cout << "V2 = " << endl;
	for (int i = 0; i < V2.size(); i++) {
		// cout << (Point3f)V2[i] << ", ";
		cout << V2[i] << ", ";  // vector이므로 array처럼 접근 가능
	}

	Mat B = A.reshape(1); // 1차원으로 변경
	Mat C = A.reshape(1).t(); // 1차원 전치행렬로 (cols to rows)
	cout << endl << endl;
	cout << "B.channels()= " << B.channels() << endl;
	cout << "B.rows, B.cols = " << B.rows << ", " << B.cols << endl;
	cout << "B = " << B << endl << endl;

	cout << "C.channels()= " << C.channels() << endl;
	cout << "C.rows, C.cols = " << C.rows << ", " << C.cols << endl;
	cout << "C = " << C << endl << endl;


	return 0;
}

 

2) vector <-> 1차원 Point배열

#include <opencv2\opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


template <class T>
T* vec2Arr(vector<T> v1) {
	T* v2 = new T[v1.size()] ;
	for (int i = 0; i < v1.size() ; i++){
		v2[i] = v1[i];
	}
	return v2;
}

template<class T>
void deleteArr(T* arr) {
	delete[] arr;
}

int main(void) {

	Mat destImage(512, 512, CV_8UC3, Scalar(255, 255, 255));

	vector<Point> contour;
	contour.push_back(Point(100, 100));
	contour.push_back(Point(200, 100));
	contour.push_back(Point(200, 200));
	contour.push_back(Point(100, 200));

	cout << contour.size() << endl;

	int nPoints[] = { contour.size() };


	Point *P1 = &contour[0];
	for (int i = 0; i < contour.size(); i++) {
		cout << "P1 [ " << i << "] = " << P1[i] << endl;
	}

	cout << endl << endl;

	Point *P2 = (Point*)Mat(contour).data;
	for (int i = 0; i < contour.size(); i++) {
		cout << "P2 [ " << i << "] = " << P2[i] << endl;

	}

	cout << endl << endl;

	Point P3[4];
	copy(contour.begin(), contour.end(), P3);

	for (int i = 0; i < contour.size(); i++) {
		cout << "P3 [ " << i << "] = " << P3[i] << endl;

	}

	cout << endl << endl;

	Point *P4 = vec2Arr<Point>(contour);
	for (int i = 0; i < contour.size(); i++) {
		cout << "P4 [ " << i << "] = " << P4[i] << endl;
	}
	polylines(destImage, (const Point**)&P4, nPoints, 1, true, Scalar(255, 0, 0));

	imshow("destImage", destImage);
	waitKey(0);

	return 0;


}

vector를 point로 변환하여 복사하는 4가지 기본 기법이 있다.

이 중에서

Point *P1 = &contour[0];

Point P3[4];
copy(contour.begin(), contour.end(), P3);

이 2개가 가장 직관적이고 외우기도 편한게 느껴진다.

 

또한 2차원 Point배열로도 변환이 가능하다.

#include <opencv2\opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


template<class T>
T** vecvec2Arr(vector<vector<T>> v1) {
	T** v2 = new T*[v1.size()];
	for (int i = 0; i < v1.size(); i++) {
		v2[i] = new T[v1[i].size()];
		for (int j = 0; j < v1[i].size(); j++) {
			v2[i][j] = v1[i][j]; // copy
		}
	}

	return v2;

}

template <class T>
void delArrArr(T** arr, int size) {
	for (int i = 0; i < size; i++) {
		delete[] arr[i];
	}

	delete[] arr;
}

int main(void) {

	Mat destImage(512, 512, CV_8UC3, Scalar(255, 255, 255));

	vector<vector <Point> > contour(2, vector<Point>());

	contour[0].push_back(Point(100, 100));
	contour[0].push_back(Point(200, 100));
	contour[0].push_back(Point(200, 200));
	contour[0].push_back(Point(100, 200));

	contour[1].push_back(Point(300, 200));
	contour[1].push_back(Point(400, 100));
	contour[1].push_back(Point(400, 200));

	int nPoints[] = { contour[0].size(), contour[1].size() };

	Point *point1, *point2;

	point1 = &contour[0][0];
	point2 = &contour[1][0];

	/* Solution 1
	Point *P1[] = { point1, point2 };
	for (int i = 0; i < contour.size(); i++) {
		for (int j = 0; j < contour[i].size(); j++) {
			cout << "P1[" << i << "] [" << j << "] = " << P1[i][j] << endl;
			polylines(destImage, (const Point**)&P1, nPoints, contour.size(), true, Scalar(0, 0, 255));
		}
	}

	imshow("test", destImage);
	waitKey(0);
	*/

	/* Solution 2
	point1 = (Point*)Mat(contour[0]).data; // Mapping
	point2 = (Point*)Mat(contour[1]).data;
	Point* P2[] = { point1, point2 };
	for (int i = 0; i < contour.size(); i++) {
		for (int j = 0; j < contour[i].size(); j++) {
			cout << "P2[" << i << "] [" << j << "] = " << P1[i][j] << endl;
			polylines(destImage, (const Point**)&P2, nPoints, contour.size(), true, Scalar(0, 0, 255));

		}
	}
	*/

	/* Solution 3
	Point P3[2][4];
	copy(contour[0].begin(), contour[0].end(), P3[0]);
	copy(contour[1].begin(), contour[1].end(), P3[1]);
	Point *P3Pointer[] = { P3[0], P3[1] };
	for (int i = 0; i < contour.size(); i++) {
		for (int j = 0; j < contour[i].size(); j++) {
			cout << "P3[" << i << "] [" << j << "] = " << P3[i][j] << endl;
			polylines(destImage, (const Point**)&P3Pointer, nPoints, contour.size(), true, Scalar(0, 0, 255));
		}
	}
	imshow("test", destImage);
	waitKey(0);
	*/
	/* Solution 4 */
	Point **P4 = vecvec2Arr<Point>(contour);
	for (int i = 0; i < contour.size(); i++) {
		for (int j = 0; j < contour[i].size(); j++) {
			cout << "P4[" << i << "] [" << j << "] = " << P4[i][j] << endl;
			polylines(destImage, P4, nPoints, contour.size(), true, Scalar(0, 0, 255));

		}
	}
	delArrArr<Point>(P4, contour.size()); //size만큼 for문으로 반복해서 각각의 요소들의 동적할당을 개방해줘야하기 때문.

	imshow("test", destImage);
	waitKey(0);


	return 0;


}

 

1차원 포인터 배열과 동일하게 4가지 기법을 예로든다.

마찬가지로,

copy(contour[0].begin(), contour[0].end(), P3[0]);
copy(contour[1].begin(), contour[1].end(), P3[1]);
Point *P3Pointer[] = { P3[0], P3[1] };

처럼 seek정보(?)를 바탕으로 끌어오는게 좋아보인다…만

아쉽게도 쉽게쓰기에는 P3을 정적(2차 배열) 선언 해야한다..

물론 동적할당을 사용하면 되지만 comfortable한 장점이 싸~악 사라진다.

필요에 의해 vector를 2차원 포인터로 변환하는 것이기 때문에 비효율적인 연산을 할 필요가 없다. (물론 Solution4는 해당방법을 포함하고 있다 ^.^)

vector에는 이미 충분히 고급기능들을 포함하고 있으니..

 

결국 Solution1정도의 적절한 complexity와 comportable한 정도가 적당하려나..

 

 

vector의 Mat클래스 템플릿을 활용하면 이미지들의 리스트처럼 저장할 수 있다.

#include <opencv2\opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


int main(void) {

	vector<Mat> imageListVector;
	imageListVector.push_back((Mat) imread("someResource")); // Mat이라도 vector의 템플릿이기 때문에 결국 push_back을 사용하면 해결된다.
	imageListVector.push_back((Mat)imread("someResource"));
	imageListVector.push_back((Mat)imread("someResource"));
	imageListVector.push_back((Mat)imread("someResource"));

	for (int i = 0; i < imageListVector.size(); i++) {
		imshow("View ImageListVector Element" + i, imageListVector[i]);
		waitKey(0);
	}
	return 0;
}

 

imread는 return형이 Mat이기 때문에 굳이 형변환을 안해줘도 무관하다. 위는 가식성을 위해 명시적으로 작성했을 뿐이다.

또한 imread는 외부 리소스를 읽어오는 메소드이기 때문에 someResource대신 실제 경로를 활용하여 파일을 불러올 수 있도록 한다.

 

카테고리: 개발노트

3개의 댓글

답글 남기기

Avatar placeholder

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다