< STL vector를 이용해서 C++에서 2차원 배열 생성하는 방법 >
( 배열의 크기를 상수가 아닌 변수로 주어야 할 경우 해결 법 )
header : #include <vector>
using namespace std;
넣어주고 vector 사용
2차원 배열을 만들때
int Amat[4][3] 이런거 말고
int Amat[image->rows][image->cols] 이렇게 만들라고 했더니 C++에서는 안되니 다른 방법을 써보시게나~
라고 C++이 친절하게? 거부한다...
그래서 STL의 vector를 이용하게 되었다.
빠른 이해를 위해서.
vector는 우리가 아는 vector라고 생각하면 된다. 흔히 수학에서 나오는 vector
v1 = (4, 5, 6, 12 ...) 이러한 건데 STL의 vector도 그렇게 생겼다. 자세한 내용은 구글링해서 찾는걸로 패쓰하고 2차원 배열을 어찌 사용하냐면 vector 안에 vector를 만든다.
즉, col vector를 만들고 각각의 요소는 row vector를 갖는다.
Amat = ( c1, c2, c3, ...) 이렇게 생겼고 여기서 c1 = ( r1, r2,...).
보기 쉽게 정리하면
Amat = c1 (r1, r2, ..., rn)
c2 (r1, r2, ..., rn)
c3 (r1, r2, ..., rn)
...
이렇게 생겻음.
CvMat에 담긴 영상에서 RGB를 받아 온다면, pimgMat에서 사이즈 가져와서 쓰고 vector를 생성해 준다.
vector< vector<int> > vt_img_R(pimgMat->rows, vector<int>(pimgMat->cols));
// int vt_img[rows][cols] 를 생성한다는 의미
vector< vector<int> > vt_img_G(pimgMat->rows, vector<int>(pimgMat->cols));
vector< vector<int> > vt_img_B(pimgMat->rows, vector<int>(pimgMat->cols));
for (int i=0;i<pimgMat->rows;i++)
{
for (int j=0;j<pimgMat->cols;j++)
{
vt_img_B[i][j] = pimgMat->data.ptr[i*pimgMat->step+j*3 +0]; // Blue
vt_img_G[i][j] = pimgMat->data.ptr[i*pimgMat->step+j*3 +1]; // Green
vt_img_R[i][j] = pimgMat->data.ptr[i*pimgMat->step+j*3 +2]; // Red
printf("%d ",vt_img_R[i][j]);
}
printf("\n");
}
해석 하자면, vector를 만들껀데, vector 안에 vector를 넣을꺼다, 안에 들어가는 vector는 col 에 관한 vector들이고 그걸 담고 있는 상위 vector는 row(col의 집합을 가진)를 담고 있다.
이 vector들에 들어갈 원소들은 int 형이다.
좀더 정확히 설명하자면,
vector f(n) : 벡터 f는 초기화된 n개의 원소를 가진다는 의미.
vector f(n,x) : 벡터 f는 x값으로 초기화된 원소 n개를 가진다는 의미.
따라서, vector <vector <int> > F(n, vector<int>(m)) : m개의 원소를 가지는 int형 벡터 n개를 원소로 가지는 벡터 F 라는 뜻.
실제로 46x46 짜리 이미지에 대해서 실행한 뒤 값이 들어간걸 보면,
vt_img_B 가 위에서 만들어진 vector이며 크기가 46인 vector이다, 그리고 vector의 각 요소는 [0], [1],
처럼 총 46가지 원소가 있는데 각각의 원소를 보니 원소가 vector형태들이다.
[4] 번 요소를 클릭해 보니 [4]요소 또한 vector안의 vector이며 46의 크기를 갖는다. (이 크기는 col의 크기) 그리고 [4]번 vector의 내부 요소들을 보니 값을 가지고 있는데 이것이 바로 RGB 값임.
< 다차원 배열 만들기 (3차원 이상) >
visual studio 2013 기준으로 3차원 vector를 만들고자 할때, vector의 크기를 추후에 원소를 넣으면서 만들어도 되지만 초기에 크기를 할당해서 구조를 만들어 넣으면 이후에 다루기 편하므로 선언과 동시에 크기를 만들려고 했다.
vector<vector<vector<int> > > aa[3][4][5]; 라고 만들면 aa 행렬이 만들어진다.( >> 입력시 오류 방지를 위해 겹치지 않게 space하나씩 준다.)
그러나 상수가 아닌 변수를 aa에 넣어서 배열을 만들면 '상수를 넣어야된다' 라는 말과 함께 오류가 난다.
변수로 배열크기 만들려고 vector쓰는건데 뭔소리지 싶어서 찾아서 공부한 뒤 정리한다.
vector<vector<vector<int> > > aa(num_cam, vector<vector<int>>(num_cam, vector<int>(num_rank,-1)));
여기서 num_cam은 int형의 상수이고 num_rank도 int형의 상수이다.
vector의 경우에는 vector<int> aa(3,4); 로 만들면 size가 3이고 원소가 모두 4로 초기화 된 상태의 배열을 만들게 된다.
따라서, 위의 3차원 배열은 모든 원소가 -1인 aa[num_cam][num_cam][num_rank]; 인 셈이다.
이방법은 복잡해 보이나 확실하게 나타낼 수 있으며 확장이 용이하다.
즉, 위의 선언은 vector<vector<int> > aa(num_cam,vector<int>(num_cam,-1));을 한차원 더 늘린 셈이다.
vector<타입> 이름(크기, 원소값);을 정확히 보고 vector안의 vector의 개념으로 늘려 나가면 된다.
<추가 정보>
vector 요놈이 신기한게 정해진 타입이 없어서 대박인듯하다.
만약에 영상을 담고 있는 CvMat을 vector에 담아서 image들이 담긴 Box 개념을 만들고 싶다면,
(즉, v = ( 영상1, 영상2, 영상3, ...., 영상n), 각 영상은 CvMat 형태)
이럴때는
========================================================================
vector<CvMat> SetOfimg; // STL 자체가 지정 형식 없이 컴퓨터가 잡아 주다보니 structure 형식도 이용 가능하다.
...
CvMat *classImg = cvCreateMat(imageMat.rows,imageMat.cols,CV_8UC1);
...
SetOfimg.push_back(*classImg);
이게 가능하다.
======================================================================
만약 CvMat이 Mat에서 넘어와서 pointer 형태가 아니고 걍 CvMat 타입이면,
CvMat haha = i_mat; 여기서 imat은 Mat 타입
이라면
vector<CvMat> SetOfimg; // 이거는 위에랑 똑같고
CvMat haha = i_mat;
SetOfimg.push_back(haha ); // * 별표를 지워준다.
<Vector를 사용해야 하는 이유>
참조 : http://thinkpiece.tistory.com/21
동적 할당 배열보다는 Vector를 쓰자. 속도는 같고 더 안전하다.
뭐든지 직접 만들어쓰는 버릇이 있어서 기본적인 data structure도 만들어서 써오곤 했는데, 배열도 마찬가지였다. 되도록이면 new/delete를 통해서 직접 만들어쓰곤 했는데, 아무래도 vector를 이용하면 간단히 해결되는 것도 손이 훨씬 많이 간다. 코드가 복잡해지다보면 한번씩은 메모리 누수를 직접 확인해봐야하기도 하고...
그러다가 어느날, 과연 내가 직접 new/delete를 통해서 하나하나 컨트롤하는 것이 c++에서 제공되는 std::vector를 사용하는 것보다 과연 더 빠를까싶은 생각이 들었다. 사실 처음부터 당연히 더 무겁겠지...생각하고 써오지 않았다. 찾아보니 StackOverflow(http://stackoverflow.com)에는 이미 해당 관련 쓰레드가 있고 실험 결과도 있다. Dynamically allocated arrays or std::vector (http://stackoverflow.com/questions/1071674/dynamically-allocated-arrays-or-stdvector)
어찌되었든 배열을 위해서는 되도록이면 vector 혹은 boost::array를 사용하는 것을 추천
< Function에서 vector 값을 Return 받아야 할때 (vector pointer) >
하다보면 vector값을 return 받아야 할때가 있다. 물론 함수를 void가 아닌 vector로 해서 만들면 되지만, void로 만들고 리턴 받을 내용을 pointer로 input 주는게 편하다. 그 과정을 보면
main()
{
...
vector<int> K(5);
vector<int>* aa = &K;
...
haha(aa);
}
void haha(vector<int>* aa)
{
...
(*aa).push_back(값);
...
}
이와 같이 해주면 haha 함수 안에서 변경된 값들을 main함수의 K가 받을수 있다.
< Vector에서 요소를 point로 사용할때 clear는 직접 접근해서 해야 한다 >
참조 : http://www.soen.kr/lecture/ccpp/cpp4/40-1-4.htm
벡터의 타입이 vector<Time *>로 변경되었으며 벡터에 요소를 추가할 때 Time 객체가 아니라 new 연산자로 동적 생성한 Time 객체의 포인터를 저장했다. dump 함수의 OutTime 호출문도 -> 연산자로 호출하도록 변경해야 한다. vt 객체는 메모리에 다음과 같이 생성될 것이다.
값을 저장하는 벡터는 Time 객체를 직접 가지지만 포인터를 저장하는 벡터는 동적 생성된 Time 객체의 포인터만을 가진다. 포인터를 가지는 벡터를 파괴할 때는 각 포인터가 가리키는 객체를 직접 파괴해야 한다. 그렇지 않으면 동적으로 생성한 객체가 파괴되지 않으므로 메모리 누수가 발생한다. 벡터는 요소를 관리할 뿐이지 요소가 가리키는 객체까지는 관리하지 못한다.
벡터에 임의의 타입을 저장할 수 있지만 그렇다고 정말 아무 타입이나 저장할 수 있는 것은 아니며 일정한 조건을 만족하는 타입만 저장할 수 있다. 다음 예제는 내부에서 동적 할당을 하는 객체를 요소로 가지는 벡터를 만든다. 동적 할당을 하는 클래스는 생성자, 가상 파괴자, 복사 생성자, 대입 연산자를 적절히 정의해야 한다.