[opencv] vector 클래스
기타 몇개의 큰 클래스들만 진행하면 이제 기본 클래스는 종료된다.
#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개의 댓글