31. OpenCV шаг за шагом. Типы данных OpenCV — хранилище памяти, последовательность



Оглавление
1. OpenCV шаг за шагом. Введение.
2. Установка.
3. Hello World.
4. Загрузка картинки.

28. Преобразование Хафа
29. Интегральное изображение
30. Трансформация изображения — аффинные преобразования, гомография
31. Типы данных OpenCV — хранилище памяти, последовательность

На шаге про преобразование Хафа, вы, возможно, обратили внимание, на непонятные типы данных CvMemStorage и CvSeq, которые использовались в примерах использования функций cvHoughLines2() и cvHoughCircles()

OpenCV использует сущность хранилища памяти (memory storage — CvMemStorage) в своих методах, для которых требуется хранить динамические объекты. Фактически, хранилище памяти — это связанный список блоков памяти.

Типы CvMemStorage и CvSeq объявлены в cxtypes.h (2.1) или modules\core\include\opencv2\core\types_c.h (2.2 и далее)

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

#define CV_STORAGE_MAGIC_VAL    0x42890000

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;

#define CV_IS_STORAGE(storage)  \
    ((storage) != NULL &&       \
    (((CvMemStorage*)(storage))->signature & CV_MAGIC_MASK) == CV_STORAGE_MAGIC_VAL)


typedef struct CvMemStoragePos
{
    CvMemBlock* top;
    int free_space;
}
CvMemStoragePos;


/*********************************** Sequence *******************************************/

typedef struct CvSeqBlock
{
    struct CvSeqBlock*  prev; /* Previous sequence block.                   */
    struct CvSeqBlock*  next; /* Next sequence block.                       */
  int    start_index;         /* Index of the first element in the block +  */
                              /* sequence->first->start_index.              */
    int    count;             /* Number of elements in the block.           */
    schar* data;              /* Pointer to the first element of the block. */
}
CvSeqBlock;


#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. */

typedef struct CvSeq
{
    CV_SEQUENCE_FIELDS()
}
CvSeq;

Методы для работы с хранилищем памяти:

CVAPI(CvMemStorage*)  cvCreateMemStorage( int block_size CV_DEFAULT(0));

— создание хранилища памяти
block_size — размер блока хранилища в байтах (если 0 — то используется значение по-умолчанию: 64Kb)

функция создаёт хранилище памяти и возвращает указатель на него. В исходном положении все поля заголовка хранилища, кроме block_size, равны 0.

CVAPI(void)  cvReleaseMemStorage( CvMemStorage** storage );

— освобождает хранилище памяти
storage — двойной указатель на хранилище памяти

CVAPI(void)  cvClearMemStorage( CvMemStorage* storage );

— очищает хранилище памяти
storage — указатель на хранилище памяти

функция не освобождает память.

Один тип объектов, которые могут храниться в хранилище памяти — это последовательность (sequence).
Сами по себе, последовательности являются связанными списками других структур.
Фактически, последовательность — это двусторонняя очередь (deque)

Функции OpenCV для работы с последовательностью:

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

— создание последовательности
seq_flags — флаг создаваемой последовательности (можно комбинировать используя оператор побитового ИЛИ):

#define CV_SEQ_ELTYPE_POINT          CV_32SC2  /* (x,y) */
#define CV_SEQ_ELTYPE_CODE           CV_8UC1   /* freeman code: 0..7 */
#define CV_SEQ_ELTYPE_GENERIC        0
#define CV_SEQ_ELTYPE_PTR            CV_USRTYPE1
#define CV_SEQ_ELTYPE_PPOINT         CV_SEQ_ELTYPE_PTR  /* &(x,y) */
#define CV_SEQ_ELTYPE_INDEX          CV_32SC1  /* #(x,y) */
#define CV_SEQ_ELTYPE_GRAPH_EDGE     0  /* &next_o, &next_d, &vtx_o, &vtx_d */
#define CV_SEQ_ELTYPE_GRAPH_VERTEX   0  /* first_edge, &(x,y) */
#define CV_SEQ_ELTYPE_TRIAN_ATR      0  /* vertex of the binary tree   */
#define CV_SEQ_ELTYPE_CONNECTED_COMP 0  /* connected component  */
#define CV_SEQ_ELTYPE_POINT3D        CV_32FC3  /* (x,y,z)  */

#define CV_SEQ_KIND_BITS        3
#define CV_SEQ_KIND_MASK        (((1 << CV_SEQ_KIND_BITS) - 1)<<CV_SEQ_ELTYPE_BITS)

/* types of sequences */
#define CV_SEQ_KIND_GENERIC     (0 << CV_SEQ_ELTYPE_BITS)
#define CV_SEQ_KIND_CURVE       (1 << CV_SEQ_ELTYPE_BITS)
#define CV_SEQ_KIND_BIN_TREE    (2 << CV_SEQ_ELTYPE_BITS)

/* types of sparse sequences (sets) */
#define CV_SEQ_KIND_GRAPH       (3 << CV_SEQ_ELTYPE_BITS)
#define CV_SEQ_KIND_SUBDIV2D    (4 << CV_SEQ_ELTYPE_BITS)

header_size — размер заголовка последовательности (должен быть больше или равен sizeof(CvSeq))
elem_size — размер элемента последовательности в байтах (размер должен соответствовать типу последовательности. Например, для последовательности точек: тип элемента CV_SEQ_ELTYPE_POINT , то параметр elem_size должен быть sizeof(CvPoint) )
storage — указатель на хранилище памяти последовательности

функция создаёт последовательность и возвращает указатель на неё.

CVAPI(void)  cvClearSeq( CvSeq* seq );

— очищает последовательность
seq — указатель на последовательность

функция удаляет все элементы из последовательности (не возвращает память, занимаемую блоками, т.о. память снова может быть использована последовательностью ).

Методы для работы с последовательностью:

CVAPI(schar*)  cvSeqPush( CvSeq* seq, const void* element CV_DEFAULT(NULL));

— добавление нового элемента в последовательность
seq — указатель на последовательность
element — указатель на элемент для добавления

функция добавляет элемент в конец последовательности и возвращает указатель на этот элемент. Если функции передаётся нулевой указатель (NULL), то функция просто выделяет место для еще одного элемента.

CVAPI(schar*)  cvSeqPushFront( CvSeq* seq, const void* element CV_DEFAULT(NULL));

— добавление нового элемента в начало последовательности
seq — указатель на последовательность
element — указатель на элемент для добавления

По действию, функция аналогична функции cvSeqPush(), но добавляет новый элемент не в конец, а в начало последовательности.

CVAPI(void)  cvSeqPop( CvSeq* seq, void* element CV_DEFAULT(NULL));

— удаляет последний элемент последовательности

CVAPI(void)  cvSeqPopFront( CvSeq* seq, void* element CV_DEFAULT(NULL));

— удаляет первый элемент последовательности

#define CV_FRONT 1
#define CV_BACK 0
CVAPI(void)  cvSeqPushMulti( CvSeq* seq, const void* elements,
                             int count, int in_front CV_DEFAULT(0) );

— помещает несколько новых элементов в конец/начало последовательности

seq — последовательность
elements — добавляемые элементы
count — число элементов для добавления
in_front — флаг, определяющий куда будут добавлены элементы:
CV_BACK — в конец последовательности
CV_FRONT — в начало последовательности

CVAPI(void)  cvSeqPopMulti( CvSeq* seq, void* elements,
                            int count, int in_front CV_DEFAULT(0) );

— удаляет несколько элементов с конца или начала последовательности
seq — последовательность
elements — удаляемые элементы
count — число элементов для удаления
in_front — флаг, определяющий откуда будут удалены элементы

CVAPI(schar*)  cvSeqInsert( CvSeq* seq, int before_index,
                            const void* element CV_DEFAULT(NULL));

— добавление элементов в середину последовательности

// cvSeqInsert(seq,0,elem) == cvSeqPushFront(seq,elem)
CVAPI(void)  cvSeqRemove( CvSeq* seq, int index );

— удаляет элемент из середины последовательности

Непосредственный доступ к элементам последовательности:

CVAPI(schar*)  cvGetSeqElem( const CvSeq* seq, int index );

— возвращает указатель на элемент последовательности в соответствии с его номером(индексом).
seq — последовательность
index — номер элемента

Пример обхода всех элементов последовательности точек и отображение их координат:

for( int i=0; itotal; ++i ) {
	CvPoint* p = (CvPoint*)cvGetSeqElem ( seq, i );
	printf("(%d,%d)\n", p->x, p->y );
}

И ещё два способа получения координат точек последовательности:

// получение координат точек
for( int i=0; itotal; ++i ) {
	CvPoint* p = CV_GET_SEQ_ELEM( CvPoint, seq, i );
	printf(" (%d,%d)\n", p->x, p->y );
}
// объявление считывалки точек
CvSeqReader reader;
// инициализация считывалки точек
cvStartReadSeq( result, &reader, 0 );
// две точки
CvPoint pt[2];

// достаем точки из последовательности точек и рисуем линии
for (int i=0;itotal-1;i++) {
	CV_READ_SEQ_ELEM( pt[0], reader );
	CV_READ_SEQ_ELEM( pt[1], reader );
	cvLine(img, pt[0], pt[1], CV_RGB(255,0,0));
}

Указатели на хранилище памяти и последовательности используются многими функциями OpenCV, для сохранения в них результатов своей работы (найденных контуров, полученных ключевых точек и т.п.)

Пример использования последовательности можно посмотреть на шаге про преобразование Хафа, в примерах использования функций cvHoughLines2() или cvHoughCircles()

total хранит общее число элементов последовательности и поэтому, перебор и отображение всех кругов, найденных методом cvHoughCircles() производится циклом:

// пробегаемся по всем элементам последовательности
for( int i = 0; i < results->total; i++ ) {
	// получение i-го элемента последовательности
	float* p = (float*) cvGetSeqElem( results, i );
	CvPoint pt = cvPoint( cvRound( p[0] ), cvRound( p[1] ) );
	// рисуем круг
	cvCircle( src, pt, cvRound( p[2] ), CV_RGB(0xff,0,0) );
}

Далее: 32. Нахождение контуров и операции с ними


0 комментариев на «“31. OpenCV шаг за шагом. Типы данных OpenCV — хранилище памяти, последовательность”»

  1. Добрый день.помогите пожалуйста разобраться.задача такая — из элементов последовательности контуров, найденных функцией и удовлетворяющих определенным критериям, сформировать новую последовательность.пытаюсь сделать это с помощью функции CvSeqPush, но пока не разобрался с аргументами при создании новой последовательности, видимо поэтому не работает.вот кусок кода.
    [code]
    //создаем новую последовательность и выделяем под нее память
    CvMemStorage* storage_1 = cvCreateMemStorage(0);
    CvSeq* contours_1=cvCreateSeq( CV_SEQ_KIND_CURVE,sizeof(CvSeq),sizeof(CvContour),storage_1);

    if(contours!=0){
    // поиск совпадения контуров по их моментам и отсеивание по периметру
    for(CvSeq* seq0 = contours;seq0!=0;seq0 = seq0->h_next){

    double match0 = cvMatchShapes(seq0, seqT, CV_CONTOURS_MATCH_I3);

    if(match0<0.5&&seq0->total>=200)
    {cvSeqPush( contours_1,seq0);}
    printf("[i] %d match: %.2f\n", ++counter, match0);
    }
    }
    [/code]

Добавить комментарий

Arduino

Что такое Arduino?
Зачем мне Arduino?
Начало работы с Arduino
Для начинающих ардуинщиков
Радиодетали (точка входа для начинающих ардуинщиков)
Первые шаги с Arduino

Разделы

  1. Преимуществ нет, за исключением читабельности: тип bool обычно имеет размер 1 байт, как и uint8_t. Думаю, компилятор в обоих случаях…

  2. Добрый день! Я недавно начал изучать программирование под STM32 и ваши уроки просто бесценны! Хотел узнать зачем использовать переменную типа…

3D-печать AI Arduino Bluetooth CraftDuino DIY Google IDE iRobot Kinect LEGO OpenCV Open Source Python Raspberry Pi RoboCraft ROS swarm ИК автоматизация андроид балансировать бионика версия видео военный датчик дрон интерфейс камера кибервесна манипулятор машинное обучение наше нейронная сеть подводный пылесос работа распознавание робот робототехника светодиод сервомашинка собака управление ходить шаг за шагом шаговый двигатель шилд юмор

OpenCV
Робототехника
Будущее за бионическими роботами?
Нейронная сеть - введение