본문 바로가기
[Public] 신호처리/OpenCV 다루기

[OpenCV] #3 시퀀스 활용하기 CvMemStorage CvSeq

by 차출발 2010. 3. 26.
반응형

예를 들어 코너점을 추출한다고 하자 그럼 코너점을 저장해야할 공간이 필요할 것이다.

STL을 이용해도 되지만  OpenCV 에서는 시퀀스라는 좋은 자료구조를 제공해준다.


먼저 Seq의 구조체를 보자

#define CV_TREE_NODE_FIELDS(node_type)                               
    int       flags;                                   /* Miscellaneous flags.         다양한 용도의 플래그*/      
    int       header_size;                        /* Size of sequence header.  시퀀스 헤더의 크기*/      
    struct    node_type* h_prev;             /* Previous sequence.           이전 시퀀스*/      
    struct    node_type* h_next;             /* Next sequence.                 다음 시퀀스*/      
    struct    node_type* v_prev;             /* 2nd previous sequence.      이전의 이전 시퀀스*/      
    struct    node_type* v_next              /* 2nd next sequence.            다음의 다음 시퀀스*/
/*
   Read/Write sequence.
   Elements can be dynamically inserted to or deleted from the sequence.
*/
#define CV_SEQUENCE_FIELDS()                                              
    CV_TREE_NODE_FIELDS(CvSeq);                                           
    int       total;                                    /* Total number of elements.              원소의 전체 개수*/  
    int       elem_size;                           /* Size of sequence element in bytes. 시퀀스 크기 (바이트)  */  
    schar*    block_max;                       /* Maximal bound of the last block.     마지막 블록의 최대 크기*/  
    schar*    ptr;                                   /* Current write pointer.                      현재 저장 포인터*/  
    int       delta_elems;                         /* Grow seq this many at a time.        시퀀스 증가에 따른 원소할당*/  
    CvMemStorage* storage;                 /* Where the seq is stored.                 시퀀스가 저장(실질 메모리)*/  
    CvSeqBlock* free_blocks;                /* Free blocks list.                              비어있는 블록 목록*/  
    CvSeqBlock* first;                           /* Pointer to the first sequence block.   첫 시퀀스 블록 가리키는 포인트*/


여기서 CvMemStorage* storage 여기가 실질 메모리가 저장되는 곳이다.

즉 여기서 자신이 원하는 데이터 타입으로 저장을 하는 것이다.

그럼 CvMemStorage 구조체에 대하여 알아보자 

별거 없다.

typedef struct CvMemStorage
{
    int signature;
    CvMemBlock* bottom;              /* First allocated block.                   */
    CvMemBlock* top;                   /* Current memory block - top of the stack. */
    struct  CvMemStorage* parent; /* We get new blocks from parent as needed. */
    int block_size;                        /* Block size.                              */
    int free_space;                       /* Remaining free space in current block.   */
}
CvMemStorage;


typedef struct CvMemBlock
{
    struct CvMemBlock*  prev;
    struct CvMemBlock*  next;
}
CvMemBlock;

보면 연결리스트와 같은 구조로 되어 있음을 알수 있다.

그럼 이걸 어떻게 활용하느냐 

먼저 시퀀스를 활용하기 전에 메모리 부터 할당해야하니 메모리 스토리지 할당하는 법부터 보자

메모리 스토리지 함수는 많이 있지만 자주 사용하는 4개 함수만 설명하겠다. 
(이외 것이 궁금하다면 직접 찾아 보아라 활용가능한 것이 많을 것이다. )
(어떻게 구현되어 있는지는 안봐진다. 젠장)

CVAPI(CvMemStorage*)  cvCreateMemStorage( int block_size CV_DEFAULT(0));   // 새로운 메모리를 만든다.

CVAPI(void)  cvReleaseMemStorage( CvMemStorage** storage );                        // 메모리 포인터 반환한다.

CVAPI(void)  cvClearMemStorage( CvMemStorage* storage );                            // 메모리 스토리지 재사용 가능

CVAPI(void*) cvMemStorageAlloc( CvMemStorage* storage, size_t size );          // 메모리 버퍼를 새로 할당


간단하게 메모리를 만들었으면 이제 시퀀스를 만들자

시퀀스의 생성은

CVAPI(CvSeq*)  cvCreateSeq( int seq_flags, int header_size, int elem_size, CvMemStorage* storage );

마지막에 우리가 만든 메모리가 들어 감을 알 수 있다.

자 이제 함수만 설명하면 이해하기 힘드니 바로 소스를 보면서 설명하자

아래 설명과 번호를 따르면 이해하기 쉬울 것이다.


#include "cv.h"
#include "highgui.h"
#include <stdio.h>

// 1번
typedef struct featureData
{
int nX;
int nY;
}FData;


int main(int argc, char **argv)
{
// 2번
CvMemStorage *Storage; 
Storage = cvCreateMemStorage(0);

// 3번
int i;
CvSeq *Seq;
Seq = cvCreateSeq(0, sizeof(CvSeq), sizeof(FData), Storage);
featureData data1 = {0, 0};
featureData data2 = {1, 1};
featureData data3 = {2, 2};
//4번
cvSeqPush(Seq,  &data1);
cvSeqPush(Seq,  &data2);
cvSeqPush(Seq,  &data3);

printf("\n====새로운 시퀀스=======\n");
for(i=0 ; i<Seq->total ; ++i)
{
featureData* CurrentData = (featureData*)cvGetSeqElem(Seq, i);
printf("[%d, %d] -> ", CurrentData->nX, CurrentData->nY);
}

//5번 
CvSeq *CloneSeq = cvCloneSeq(Seq, 0);

printf("\n====복사된 시퀀스======\n");
for(i=0 ; i<CloneSeq->total ; ++i)
{
featureData* CloneCurrentData = (featureData*)cvGetSeqElem(CloneSeq, i);
printf("[%d, %d] -> ", CloneCurrentData->nX, CloneCurrentData->nY);
}

// 6번
featureData* ChangeData = (featureData*)cvGetSeqElem(CloneSeq, 1);
ChangeData->nX = 4;

printf("\n=====같은 스토리지 사용확인 새 시퀀스========\n");
for(i=0 ; i<CloneSeq->total ; ++i)
{
featureData* ChangeCloneCurrentData = (featureData*)cvGetSeqElem(CloneSeq, i);
printf("[%d, %d] -> ", ChangeCloneCurrentData->nX, ChangeCloneCurrentData->nY);
}
printf("\n===같은 스토리지 사용확인 복사 시퀀스====\n");
for(i=0 ; i<Seq->total ; ++i)
{
featureData* ChangeCurrentData = (featureData*)cvGetSeqElem(Seq, i);
printf("[%d, %d] -> ", ChangeCurrentData->nX, ChangeCurrentData->nY);
}
// 7번
cvClearSeq(Seq);
cvClearSeq(CloneSeq);
cvReleaseMemStorage(&Storage);
}

1. 나는 메모리 공간을 사용자 정의로 설정했다. 간단하게 X, Y 좌표 를 가진것으로
   간단하게 구조체를 선언 했다.

2. 메모리 스토리지 공간을 할당하였다.
 
3. 시퀀스를 생성하였다. 
    Seq = cvCreateSeq(0, sizeof(CvSeq), sizeof(FData), Storage); 
    내가 생성한 X, Y 좌표인 FData 로 구성됨을 알 수 있다. 

4. 간단하게 3개 좌표 (1,1) (2,2) (3,3) 를 내가 생성한 시퀀스에 저장할려고 한다.
   cvSeqPush(Seq,  &data1); 함수를 사용하였다. 

   여기서 제공되는 함수는 다양하다
   cvSeqPush
   cvSeqPushFront
   cvSeqPop
   cvSeqPopFront
   cvSeqPushMulti
   cvSeqPopMulti
   
그리고 제대로 되는지 출력하려고 데이터에 접근하기 위해 cvGetSeqElem를 이용한다.
(featureData*)cvGetSeqElem(Seq, i);

출력 하면 원하는 결과가 나온다. 

5. 자 Seq를 통채로 복사해보자   cvCloneSeq함수를 이용하자
   CvSeq *CloneSeq = cvCloneSeq(Seq, 0);
  여기서 0은 메모리 스토리지를 뜻한다. (NULL)
  출력해보자 이전 시퀀스와 같은 결과를 출력함을 알 수 있다. 

  여기서 의문점이 생길것이다. 같은 스토리지를 쓰는가 ? 아님 다른 스토리지를 사용하는가 하는 문제점이다.
  
6. 그럼 Clone 내용을 바꿔보자 올래 데이터가 변하는지

featureData* ChangeData = (featureData*)cvGetSeqElem(CloneSeq, 1);
ChangeData->nX = 4;
2번째 데이터 공간에 X 좌표만 4 로 변형햇다. 

 올래 데이터는 앗 변하지 않는다. 서로 다른 메모리를 사용함을 알 수가 있다. 

7. 이제 실험이 끝났으니 메모리를 해제하자. 
  

결과는 
 
 
이 실험을 통하여 간단하게 Seq의 사용은 간단하게 사용할 수 있다. 

하지만 이건 연결리스트라 느리다는 단점이 있다.

좀더 공부를 원한다면 Seq 안에 슬라이드 개념 등등  다양한 방법이 나와 있다. 

한번 탐험 해보시고 시퀀스에 대하여 다시는 책을 볼일이 없기를 바란다.