[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개의 댓글