2017년 2월 15일 수요일

OpenCV CvMat

OpenCV CvMat행렬의 구조와 데이터 접근 방법에 대해 정리한다.

CvMat 행렬의 구조는 다음과 같다.
 
typedef struct CvMat {
    int type;
    int step;
    int* refcount;     // for internal use only
    union {
         uchar* ptr;
         short* s;
         int*    i;
         float* fl;
         double* db;
    } data;
    union {
         int rows;
         int height;
    };
    union {
         int cols;
         int width;
    };
} CvMat;

매트릭스의 행렬의 생성
 
CvMat* cvCreateMat( int rows, int cols, int type );

// Create only matrix header without allocating data
//
CvMat* cvCreateMatHeader( int rows, int cols, int type );

// Initialize header on existing CvMat structure
//
CvMat* cvInitMatHeader(
   CvMat* mat,
   int   rows,
   int   cols,
   int   type,
   void* data = NULL,
   int   step = CV_AUTOSTEP
);

// Like cvInitMatHeader() but allocates CvMat as well.
//
CvMat cvMat(
   int   rows,
   int   cols,
   int   type,
   void* data = NULL
);

// Allocate a new matrix just like the matrix 'mat'.
//
CvMat* cvCloneMat( const cvMat* mat );

// Free the matrix 'mat', both header and data.
//
void cvReleaseMat( CvMat** mat );



행렬 데이터에 접근하기

- 간편한 방법

CV_MAT_ELEM() 매크로를 사용하여 행렬의 원소값 받아오기
  CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );
  float element_3_2 = CV_MAT_ELEM( *mat, float, 3, 2 );
  printf("Exercise 3_4, matrix created and accessed [3,2]=%f\n",element_3_2);

CV_MAT_ELEM_PTR() 매크로를 사용하여 행렬의 원소값 설정하기
  CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );
  float element_3_2 = 7.7;
  *( (float*)CV_MAT_ELEM_PTR( *mat, 3, 2 ) ) = element_3_2;

  // below from example ch3_ex3_8.txt
  cvmSet( mat, 2, 2, 0.5000 );
  cvSetReal2D( mat, 3, 3, 0.3300 );

  printf("Exercise 3_5, matrix created and accessed [3,2]=%f, [2,2]=%f, [3,3]=%f\n",CV_MAT_ELEM( *mat, float, 3, 2 ),CV_MAT_ELEM( *mat, float, 2, 2 ),CV_MAT_ELEM( *mat, float, 3, 3 ));

CV_MAT_ELEM() 매크로와 CV_MAT_ELEM_PTR() 매크로는 호출할 때마다 주소값 계산을 반복한다.
매번 행렬의 데이터 영역 시작 주소를 찾고, 알고싶은 원소의 주소까지 오프셋을 계산한 후 시작 주소에 더해서 해당 원소에 접근하는 작업을 반복한다.



- 엄격한 방법

Example 3-6. Pointer access to matrix structures 
  uchar* cvPtr1D(
      const CvArr* arr,
      int          idx0,
      int*         type = NULL
   );
   uchar* cvPtr2D(
      const CvArr* arr,
      int          idx0,
      int          idx1,
      int*         type = NULL
   );
   uchar* cvPtr3D(
      const CvArr* arr,
      int          idx0,
        int          idx1,
        int          idx2,
        int*         type = NULL
    );
    uchar* cvPtrND(
        const CvArr* arr,
        int*         idx,
        int*         type            = NULL,
        int          create_node     = 1,
        unsigned*    precalc_hashval = NULL
    );

Example 3-7. CvMat and IplImage element functions
double cvGetReal1D( const  CvArr* arr, int idx0 );
double cvGetReal2D( const  CvArr* arr, int idx0, int idx1 );
double cvGetReal3D( const  CvArr* arr, int idx0, int idx1, int idx2 );
double cvGetRealND( const  CvArr* arr, int* idx );
CvScalar cvGet1D( const CvArr*  arr, int idx0 );
CvScalar cvGet2D( const CvArr*  arr, int idx0, int idx1 );
CvScalar cvGet3D( const CvArr*  arr, int idx0, int idx1, int idx2 );
CvScalar cvGetND( const  CvArr* arr, int* idx );

Example 3-8. Set element functions for CvMat or IplImage.  
// Also see usage example in ch3_ex5.cpp
void cvSetReal1D( CvArr* arr, int idx0, double value );
void cvSetReal2D( CvArr* arr, int idx0, int idx1, double value );
void cvSetReal3D(
   CvArr* arr,
   int idx0,
   int idx1,
   int idx2,
   double value
);
void cvSetRealND( CvArr* arr, int* idx, double value );
void cvSet1D( CvArr* arr, int idx0, CvScalar value );
void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );
void cvSet3D(
   CvArr* arr,
   int idx0,
   int idx1,
   int idx2,
   CvScalar value
);
void cvSetND( CvArr* arr, int* idx, CvScalar value );

- 적절한 방법
float sum( CvMat* mat ) {
  float s = 0.0f;
  for( int row = 0; row < mat->height; row++ )
  {
    float* ptr = mat->data.fl + row * mat->step / 4; // Float형 포인터인 경우
    // float* ptr = (float*)(mat->data.ptr + row * mat->step); // 그냥 포인터인 경우
    for( int col = 0; col < mat->width; col++ )
    {
      s += *ptr++;
    }
  }
  return( s );
};
OpenCV에서 step은 행렬의 행 크기를 바이트 단위로 나타낸다.
그러므로 다음행으로 넘어가기 위해서는 해당 포인터에 step을 더해줘야한다.




댓글 없음:

댓글 쓰기