【Opencv】cvSeq

119 篇文章 12 订阅

简介

轮廓是由一个一个像素组成的,如何在内存中存储一个轮廓,就需要用到序列(CvSeq)结构,另外,在处理任何有序关系的动态的对象时可以用到CvSeq。序列是某种结构的链表,可以将序列想象为许多编程语言中都存在的容器类或容器模板类(如C++中的vector),序列在内存被实现为一个双端队列,因此可以实现快速的随机访问,以及快速删除顶端的元素,但是从中间删除元素值则稍慢些。

opencv的实现原理

参数

函数原型说明
CvSeq* cvCreateSeq(int seq_flags,int header_size,int elem_size,CvMemStorage* storage)

功能:创建一序列 

参数:seq_flags为序列的符号标志。如果序列不会被传递给任何使用特定序列的函数,那么将它设为0,否则从预定义的序列类型中选择一合适的类型。 Header_size为序列头部的大小;必须大于或等于sizeof(CvSeq)。如果制定了类型或它的扩展名,则此类型必须适合基类的头部大小。 Elem_size为元素的大小,以字节计。这个大小必须与序列类型(由seq_flags指定)相一致。例如,对于一个点的序列,元素类型 CV_SEQ_ELTYPE_POINT应当被指定,参数elem_size必须等同于sizeof(CvPoint)。Storage为指向前面定义的 内存存储器

CvSeq* cvCloneSeq(const CvSeq* seq,CvMemStorage* storage=NULL)功能:创建序列的一份拷贝
Void cvSeqInvert(CvSeq* seq)功能:将序列中的元素进行逆序操作
Void cvSeqSort(CvSeq* seq,CvCmpFunc func,void *userdata=NULL)功能:使用特定的比较函数对序列中的元素进行排序
Char* cvSeqSearch(CvSeq* seq,const void* elem,CvCmpFunc func,int is_sorted,int *elem_idx,void *userdata=NULL)功能:查询序列中的元素
Void cvClearSeq(CvSeq* seq);功能:清空序列
Char* cvSeqPush(CvSeq* seq,void* element=NULL)功能:添加元素到序列的尾部
void cvSeqPop(CvSeq* seq,void* element=NULL)功能:删除序列尾部元素
Char* cvSeqPushFront(CvSeq* seq,void* element=NULL)功能:在序列头部添加元素
Void cvSeqPopFront(CvSeq* seq,void* element=NULL)功能:删除在序列的头部的元素
Void cvSeqPushMulti(CvSeq* seq,void* elements,int count,int in_front=0);功能:添加多个元素到序列尾部或头部
Void cvSeqPopMulti(CvSeq* seq,void* elements,int count,int in_front=0)功能:删除多个序列头部或尾部元素
Char* cvSeqInsert(CvSeq* seq,int before_index,void* element=NULL)功能:在序列中的指定位置添加元素
Void cvSeqRemove(CvSeq* seq,int index)功能:删除序列中的指定位置的元素
Char* cvGetSeqElem(const CvSeq* seq,int index)功能:返回索引所指定的元素指针
Int cvSeqElemIdx(const CvSeq* seq,const void* element,CvSeqBlock** block=NULL)功能:返回序列中元素的索引
Void cvStartAppendToSeq(CvSeq* seq,CvSeqWriter* writer)功能:将数据写入序列中,并初始化该过程
Void cvStartWriteSeq(int seq_flags,int header_size,int elem_size,CvMemStorage* storage,CvSeqWriter* writer)功能:创建新序列,并初始化写入部分
CvSeq* cvEndWriteSeq(CvSeqWriter* writer)功能:完成写入操作
Void cvStartReadSeq(const CvSeq* seq,CvSeqReader* reader,int reverse=0)功能:初始化序列中的读取过程

关于flags的参数详解

CvSeq中的flags变量可由3个类值组成,不同类之间的标志可以用或运算来组合。第一类确定序列中的元素类型,如点,Freeman码,点的指针,点索引和二叉树的结点。第二类表示序列本身的性质,如元素的集合,元素所定义的曲线,二叉树,图。第三类表示序列的其他属性,如序列是闭合的,简单的,凸的,嵌套的。

http://blog.csdn.net/yangqiuying/article/details/8500601

在使用CvSeq的时候常常跟CvMemStorage,CvSeqWriter,CvSeqReader一起使用。 这里给出一个包含图形化的理解

其中蓝色高亮部分的结构是一个#define CV_TREE_NODE_FIELDS(node_type) 的结构,可以用它来构造一些更复杂的结构,例如,队列,树,图等。 
代码如下:

#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;

int main()
{
    CvSeqWriter writer;
    CvMemStorage * storage = cvCreateMemStorage(0);
    cvStartWriteSeq(CV_8UC1, sizeof(CvSeq), sizeof(CvPoint), storage, &writer);
    for (int i = 1; i <= 10; i++)
    {
        CvPoint pt;
        pt.x = 2 * i - 1;
        pt.y = 2 * i;
        CV_WRITE_SEQ_ELEM(pt, writer);
    }
    CvSeq* seq = cvEndWriteSeq(&writer);

    CvPoint temp;
    CvSeqReader reader;
    cvStartReadSeq(seq, &reader, 0);
    for (int i = 0; i<seq->total; i++)
    {
        CV_READ_SEQ_ELEM(temp, reader);
        cout << temp.x << " " << temp.y << endl;
    }
    cout << "\n" << endl;

    cvStartReadSeq(seq, &reader, 1);
    for (int i = 0; i<seq->total; i++)
    {
        CV_READ_SEQ_ELEM(temp, reader);
        cout << temp.x << " " << temp.y << endl;
    }
    return 0;
}

OpenCV CvSeq的内部结构探讨

       一直困惑于CvSeq到底是个什么样的东西,因为曾经拿到别人写的一个函数库,其返回值是一个CvSeq指针,我的任务是遍历所有的Sequence,然 后删除其中不符合要求的Sequence。由于没有文档,我当时并不知道我需要遍历的是Sequence还是Sequence中的Element。于是我 写下了类似如下的代码:

CvSeq *pCurSeq = pInputSeq;
int index = 0;
while( pCurSeq=pCurSeq->h_next )
{
   if( process(pCurSeq) )
   {
      pCurSeq = pCurSeq->h_prev; //这里为了简单不考虑是否为列表头
      cvSeqRemove(pInputSeq, index);
      --index;
   }
   ++index;
}

事实证明这段代码是错误的,而且往往返回的错误信息是
> OpenCV ERROR: One of arguments' values is out of range (Invalid index) 
> in function cvSeqRemove, cxdatastructs.cpp(1587) 

为 什么会有这样的错误呢?看一下CvSeq的源代码就可略见一斑。下面是OpenCV2.0版本有关CvSeq的定义

可以参考https://blog.csdn.net/yangqiuying/article/details/8500601

#define CV_TREE_NODE_FIELDS(node_type)                          /
    int       flags;         /* micsellaneous 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元素的尺寸*/  /
    char*     block_max;      /* maximal bound of the last block上一块的最大 */ /
    char*     ptr;            /* current write pointer当前写指针 */      /
    int       delta_elems;    /* how many elements allocated when the seq grows 序列中快的大小 (序列粒度) */  /
    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;

原来CvSeq本身就是一个可增长的序列,CvSeq::total是指序列内部有效元素的个数;而h_next和h_prev并不是指向CvSeq内部元 素的指针,它们是指向其它CvSeq的。再回到文章最初的代码,我们可以看到该代码具有逻辑上的错误,首先while语句遍历的是所有的CvSeq,使用 process处理每一个CvSeq,而遇到需要删除的CvSeq时,又使用才cvSeqRemove删除当前CvSeq中的第index个元素。实际上 此时index很可能超出了当前CvSeq中总元素的个数,所以出现了超出边界的错误。正确的做法是直接删除该CvSeq。

CvSeq *pCurSeq = pInputSeq;
CvSeq *pOldSeq = NULL;
while( pCurSeq )
{
   if( process(pCurSeq) )
   {
      pOldSeq = pCurSeq;
      if(pOldSeq->h_prev)
      {
         pCurSeq = pOldSeq->h_prev;
         pCurSeq->h_next = pOldSeq->h_next;
         pOldSeq->h_next->h_prev = pCurSeq;
         pCurSeq=pCurSeq->h_next;
         cvClearSeq( pOldSeq );
      }
      else
      {
         pCurSeq = pOldSeq->h_next;
         pCurSeq->h_prev = NULL;
         cvClearSeq( pOldSeq );
      }
   }
   else
   {
      pCurSeq=pCurSeq->h_next;
   }
}

后来在Google Book里查了一下,发现《Learning OpenCV:Computer Vision with the OpenCV Library》中有这么一段话描述的不错:
The sequence structure itself has some important elements that you should be aware of. The first, and one you will use often, is total . This is the total number of points or objects in the sequence. The next four important elements are pointers to other sequence: h_prev , h_next , v_prev and v_next . These four pointers are part of what are called CV_TREE_NODE_FIELDS ; they are used not to indicate elements inside of the sequence but rather to connect different sequences to one another. Other objects in the OpenCV universe also contain these tree node fields.

详细介绍方法和参数

#define CV_TREE_NODE_FIELDS(node_type)                          /
    int       flags;         /* micsellaneous 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元素的尺寸*/  /
    char*     block_max;      /* maximal bound of the last block上一块的最大 */ /
    char*     ptr;            /* current write pointer当前写指针 */      /
    int       delta_elems;    /* how many elements allocated when the seq grows 序列中快的大小 (序列粒度) */  /
    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;

结构CvSeq是所有OpenCV动态数据结构的基础

    通过不同寻常的宏定义,简化了带有附加参数的结构CvSeq的扩展.为了扩展CvSeq,用户可以定义一个新的数据结构,也可以在宏CV_SEQUENCE_FIELDS()所包括的CvSeq域后再放入用户自定义的域.

    序列有两种类型:稠密序列和稀疏序列.稠密序列都派生于CvSeq,用来代表可扩展的一维数组----向量,栈,队列和双端队列等;数据间不存在空隙(即连续存放),如果从序列中删除元素或将新元素插入到序列中(不是两端),则次元素后边的相关元素会被移动.稀疏序列都派生于CvSet,是由节点组成的序列,每个节点或者被占用空间或者是空,由flags标识指定.这些序列作为无序的数据结构而被使用,如点集,图,和哈希表等.

域header_size含有序列头部节点的实际大小,此值大于或等于sizeof(CvSeq)

域h_prev,h_next,v_prev,v_next可用来创建不同序列的层次结构.域h_prev和h_next指向同意层次结构的前一个和后一个序列,域v_prev和v_next指向垂直方向结构上的前一个和后一个序列,即父亲和子孙

域first指向第一个序列

域total包含稠密序列的总元素数和稀疏序列被分配的节点数

域flags的高16位描述(包含)特定的动态结构类型(CV_SEQ_MAGIC_VAL表示稠密序列,CV_SET_MAGIC_VAL表示稀疏序列),flags本身同事还包含形形色色的信息.

flags的最低CV_SEQ_ELTYPE_BITS位包含元素类型的ID(标识符).大多数处理函数并不会用到元素类型,而只会用到存放在elem_size中的元素大小.如果序列中包含CvMat结构数据,则元素的类型就与CvMat中元素的类型相匹配,如CV_32SC2表示由二维空间中的点组成的序列,CV_32FC1表示由浮点数组成的序列等.

通过宏CV_SEG_ELTYPE(seq_header_ptr)来获取序列中元素的类型.处理数字序列的函数判断是elem_size等同于序列元素的大小.除了与CvMat兼容的类型外,还有以下几个在头文件中定义的额外类型.

(1)标准的序列元素类型

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

(2)通用序列类型

#define CV_SEQ_KIND_GENERIC     (0 << CV_SEQ_ELTYPE_BITS)    

(3)稠密数据类型

#define CV_SEQ_KIND_CURVE       (1 << CV_SEQ_ELTYPE_BITS)    
#define CV_SEQ_KIND_BIN_TREE    (2 << CV_SEQ_ELTYPE_BITS)    

(4)稀疏数据类型

#define CV_SEQ_KIND_GRAPH       (1 << CV_SEQ_ELTYPE_BITS)    
#define CV_SEQ_KIND_SUBDIV2D    (2 << CV_SEQ_ELTYPE_BITS)   

CvSeqBlock序列结构如下

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;    

序列块构成一个双向循环列表,因此,指针prev和next永远不为NULL,而总是指向序列中的前一个和后一个序列块.也就是说,最后一个序列块的next指向序列中的第一个块,而第一个块的prev指向最后一个块.也就是说,最后一个序列块的next指向序列中块的位置.例如,一个含有10个元素的序列被分成3块,每块的大小分别为3,5和2,若第一块的参数start_index为2,则该序列的(start_index,count)对的值相应为(2,3),(5,5)和(10,2).第一个块的参数start_index通常为0,除非已有一些元素被插入序列中.

CvSlice对序列分割进行了定义,结构如下

typedef struct CvSlice    
{    
    int  start_index, end_index;    
}    
CvSlice;    
    
CV_INLINE  CvSlice  cvSlice( int start, int end )    
{    
    CvSlice slice;    
    slice.start_index = start;    
    slice.end_index = end;    
    
    return slice;    
}    
    
#define CV_WHOLE_SEQ_END_INDEX 0x3fffffff    
#define CV_WHOLE_SEQ  cvSlice(0, CV_WHOLE_SEQ_END_INDEX)  

下面语句用来计算序列分割长度

int cvSliceLength(CvSlic slice,const CvSeq* seq);  

有关序列的一些操作都将结构CvSlice作为输入参数,默认情况下该参数通常被设置成整个序列(调用CV_WHOLE_SEQ实现).start_index和end_index中的任何一个都可以是负数,或者超过序列长度,start_index是闭界,end_index是开界.如果两者相等,则分割被认为是空分割(即不包含任何元素).由于序列被看作是循环结构,所以分割时可以选择序列中靠后的几个元素,分割后靠前的元素反而跟在它们的后面,如cvSlice(-2,3).函数使用下列方法来规范分割参数.首先,调用cvSliceLength决定分割长度,然后,使用类似于函数cvGetElem的参数index来规范start_index(例如允许使用负数).实际分分割操作起始于规范化了的start_index,中止于start_index + cvSliceLength()(再次假设序列是循环结构).

如果次函数不接受分割参数,但仍想要处理序列的某一部分,则可使用函数cvSeqSlice来获取子序列,

(1)CreatSeq

功能:

函数cvCreatSeq创建一序列并返回指向该序列的指针.

格式

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

参数:

seq_flags 序列的符号标志.如果序列不被传递给任何需使用特定序列的函数,则将他设为0,否则从预定义的序列类型中选择一合适类型.

header_size  序列头部的大小,必须大于或等于sizeof(CvSeq).如果指定了序列类型或序列扩展名,则次类型必须适合基类的头部大小.

elem_size 元素的大小,以字节为单位.此大小必须与序列类型一致.例如,对于一个店序列,应当指定元素类型为CV_SEQ_ELTYPE_POINT,参数elem_size必须等于sizeof(CvPoint)

storage 序列的位置.

说明

函数在存储块中分配序列的头部作为一个连续块,并将结构CvSeq的flags,elem_size,header_size和storage各域的值设置为从此函数传递过来的值,设置delta_elems为默认(可通过函数cvSetSeqBlockSize对其重新赋值),清空其他的头部域,包括前sizeof(CvSeq)字节空间.

(2)SetSeqBlockSize

功能:

函数cvSetSeqBlockSize设置序列块的大小

格式

void cvSetSeqBlockSize(CvSeq* seq,int delta_emems);

参数

seq  序列

delta_elems  满足元素所需块的大小

说明

函数cvSetSeqBlockSize将对内存分配的粒度产生影响,当序列缓冲区中的空间消耗完时,函数为delta_elems个序列元素分配空间.如果新分配的空间与之前分配的空间相邻,则两块合并;否则,创建一个新的序列块.因此,参数值delta_elems越大,序列中出现碎片的可能性就越小;不过内存中更多的空间被浪费.当序列被创建后,参数delta_elems的大小被设置为默认大小(1K).之后,可随时调用此函数,并影响内存的分配.次函数可修改传递过来的参数值,以满足内存块的大小限制.

(3)SeqPush

功能

函数cvSeqPush添加元素到序列的尾部

格式

char* cvSeqPush(CvSeq* seq,void* element = NULL);

参数

seq  块

element 添加的元素

说明:

函数cvSeqPush在序列块的尾部添加一元素并返回指向该元素的指针.如果输入参数为NULL,则函数仅分配一空间,留给下一个元素使用.下列代码说明了如何使用该函数去分配一个空间.

CvMemStorage* storage = cvCreateMemStorage(0);    
CvSeq* seq = cvCreateSeq(CV_32SC1,sizeof(CvSeq),sizeof(int),storage);    
int i;    
for (int i=0;i<100;i++)    
{    
    int* added = (int*)cvSeqPush(seq,&i);    
    printf("%d is added",*added);    
}    
cvReleaseMemStorage(&storage);    

函数cvSeqPush的时间复杂度为O(1).如果需要分配并使用的空间较大,则可使用分配较快的函数(见cvStartWriterSeq和相关函数)

(4)SeqPop

功能:

函数cvSeqPop删除序列的尾部元素

格式

void cvSeqPop(CvSeq* seq,void* element =NULL);

参数:

seq  序列

element  可选参数.如果改指针不为空,则赋值被删除元素到指针所指位置.

说明:

函数cvSeqPop从序列中删除一元素.如果序列已经为空,则报告错误.函数的事件复杂度为O(1).

(5)SeqPushFront

功能

函数cvSeqPushFront在序列头部添加元素

格式

char* cvSeqPushfront(CvSeq* seq,void* element = NULL);

参数:

seq  序列

element 添加的元素.

说明

函数cvSeqPushFront类似于cvSeqPush,不过是在序列头部添加元素.其时间复杂度为O(1).

(6)SeqPopFront

功能:

函数cvSeqPopFront删除序列的头部元素

格式

void cvSeqPop(CvSeq* seq,void* element =NULL);

参数:

seq  序列

element  可选参数.如果改指针不为空,则赋值被删除元素到指针所指位置.

说明:

函数cvSeqPop从序列中删除一元素.如果序列已经为空,则报告错误.函数的事件复杂度为O(1).

(7)SeqPushMulti

功能

函数cvSeqPushMulti在序列头部或尾部添加多个元素

格式

void cvSeqPushMulti(CvSeq* seq,void* elements,int count , int in_font = 0);

参数

seq  序列

elements 待添加的元素

count  添加的元素个数

in_front  标识在头部haishi在尾部添加元素.为下列值之一:

           CV_BACK(=0)  在序列的尾部添加元素

           CV_FRONT(≠ 0) 在序列的头部添加元素

说明

带添加元素按输入数组中的顺序被添加到序列中,而且他们可以添加到不同的序列中.

(8)SeqPopMulti

功能

函数cvSeqPopMulti删除多个序列头部或尾部的元素.

格式

void cvSeqPopMulti(CvSeq* seq,void* elements,int count , int in_font = 0);

参数

seq  序列

elements 待删除的元素

count  待删除的元素个数

in_front  标识在头部haishi在尾部添加元素.为下列值之一:

           CV_BACK(=0)  在序列的尾部添加元素

           CV_FRONT(≠ 0) 在序列的头部添加元素

说明

如果待删除元素的个数超过序列中的元素总数,则函数将删除所有元素.

(9)SeqInsert

功能

函数cvSeqInsert在序列中插入元素

格式

char* cvSeqInsert(CvSeq* seq, int before_index, void*  element = NULL);

参数

seq 序列

before_index  元素插入的位置(即索引).如果插入的位置在0(允许的参数最小值)之前,则该函数等同于函数cvSeqPushFront;如果是在seq_total(允许的参数最大值)之后,则函数等同于cvSeqPush

element  待插入的元素

说明

函数cvSeqIndsert移动从被插入位置到序列尾部的所有元素,如果指针element不为NULL,则将element中的元素复制到指定的位置.函数返回指向被插入元素的指针.

(10)SeqRemove

功能

函数cvSeqRemove删除索引指定的元素

格式

void cvSeqRemove(CvSeq* seq,int  index);

参数

seq 序列

index 被删除的元素索引

说明

如果索引超出了序列的范围,或函数企图从空序列中删除元素,则报告错误.函数通过移动序列中的元素来删除指定的元素

(11)ClearSeq

功能

函数cvClearSeq删除序列中的所有元素

格式

void cvClearSeq(CvSeq* seq);

参数

seq 序列

说明

函数不会将不在使用的内存返回到存储器中,当新元素添加到序列中时,可重新使用该内存.函数的时间复杂度为O(1).

(12)GetSeqElem

功能

函数cvGetSeqElem返回索引指定的元素指针

格式

char* cvGetSeqElem(const CvSeq* seq,int index);

#define CV_GET_SEQ_ELEM(TYPE,seq,index)\

            (TYPE* )cvGetSeqElem((CvSeq*)(Seq),(index))

参数

seq  序列

index 索引

说明

函数cvGetSeqElem查找序列中由索引指定的元素,并返回指向该元素的指针.如果元素不存在则返回0.函数索引支持负数,即-1代表序列的最后一个元素,-2代表倒数第二个元素,等等.如果序列只包含一个块,或者所需元素在第一个块中,那么应当使用宏CV_GET_SEQ_ELEM(elemType,seq,index),宏中参数elemType是序列中元素的类型(如CvPoint),参数seq表示序列,参数index代表所需元素的索引.该宏首先核查所需元素是否属于第一个块,若是则返回该元素;否则,该宏调用主函数cvGetSeqElem.如果索引为负数,则总调用函数cvGetSeqElem.函数的时间复杂度为O(1),并假设块的大小比元素的数量小.

(13)SeqElemIdx

功能

函数cvSeqElemIdx返回序列中元素的索引

格式

int cvSeqElemIdx(const CvSeq* seq,const void* element, CvSeqBlock** blodk=NULL);

参数

seq序列

element 指向序列中元素的指针

block 可选参数,如果不为空,则存放包含所指元素的块的地址

说明

函数cvSeqElemIdx放回元素的索引,如果该元素不在此序列中,则返回一个负数

(14)CvtSeqToArray

功能

函数cvCvtSeqToArray复制序列中的元素到一个连续的内存块中

格式

void* cvCvtSeqToArray(const CvSeq* seq, void* elements, CvSlice slice= CV_WHILE_SEQ);

参数

seq 序列

elements 指向目的的数组(存放已复制的元素)的指针,指针指向的空间必须足够大.

slice 复制序列部分到数组中.

说明

函数cvCvtSeqToArray复制整个或部分序列到指定的缓冲区中,并返回指向该缓冲区的指针.

下列说明如何将序列中的点集提取出来

CvMemStorage* stor;    
CvSeq* cont;    
CvPoint* PointArray;    
CvPoint2D32f* PointArray2D32f;    
float temp;    
    
//创建动态<strong>结构</strong>和<strong>序列</strong>    
stor = cvCreateMemStorage(0);    
cont = cvCreateSeq(CV_SEQ_ELTYPE_POINT,sizeof(CvSeq),sizeof(CvPoint),stor);    
//寻找所有轮廓并放置在<strong>序列</strong>中    
cvFindContours(src,stor,&cont,sizeof(CvContour),CV_RETR_LIST,CV_CHAIN_APPROX_NONE,cvPoint(0,0));    
for (;cont;cont=cont->h_next)    
{    
    int i;    
    int count = cont->total;    
    CvPoint center;    
    CvSize size;    
    
    PointArray = (CvPoint*) malloc(count* sizeof(CvPoint));    
    PointArray2D32f = (CvPoint2D32f*)malloc(count* sizeof(CvPoint2D32f));    
    //获取点内容    
    cvCvtSeqToArray(cont,PointArray,CV_WHOLE_SEQ);    
    //把点放置到集合中    
    for (i=0;i<count ;i++)    
    {    
        PointArray2D32f[i].x =(float)PointArray[i].x;    
        PointArray2D32f[i].y = (float)PointArray[i].y;    
    }    
    //释放内存    
    free(PointArray);    
    free(PointArray2D32f);    
    //free(box);    
    
}   

(15)MakeSeqHeadForArray

功能

函数cvMakeSeqHeaderForArray构建序列

格式

cvMakeSeqHeaderForArray( int seq_type, int header_size,

                                       int elem_size, void* elements, int total,

                                       CvSeq* seq, CvSeqBlock* block );

参数

seq_type 序列的类型

header_size 序列的头部大小.该值必须大于等于数组的大小

elem_size  元素的大小

elements 星辰该序列的元素

total 序列中元素的总数.此值必须等于elements中元素的个数

seq 指向被用做序列头部分局部变量

block 指向局部变量的指针

说明

函数cvMakeSeqHeaderForArray初始化序列的头部.序列头和序列块由用户分配.该函数不复制数据.创建的序列只包含一个块及一个NULL指针,因此用户可以读取指针.但若试图将元素添加到序列中,则多数会引发错误.

(16)SeqSlice

功能

函数cvSeqSlice为序列碎皮建立独立的头

格式

CvSeq* cvSeqSlice(const CvSeq* seq,CvSlice slice, CvMemStorage* storage = NULL, int copy_data = 0);

参数

seq 序列

slice 部分序列

storage 存放新序列和复制数据(如果需要)的目的的存储空间.如果为NULL,则此函数使用存储输入序列的存储空间

copy_data 标志是否要复制元素,如果不为0,则需要复制;否则,则不需要复制

说明

函数cvSeqSlice创建一序列,该序列为输入序列中特定的一部分(slice).新序列或者与原序列共享元素或者拥有自己的一个备份.因此,如果需要处理某部分序列,而函数却没有slice参数,则可使用次函数获取该序列

(17)cloneSeq

功能

函数cvCloneSeq创建序列的一个备份

格式

CvSeq* cvCloneSeq(const CvSeq* seq,CvMemStorage* storage= NULL);

参数

seq 序列

storage 存放新序列头部分和复制数据(如果需要)的目的存储空间.如果为NULL,则此函数使用存储输入序列的存储空间

说明

函数cvCloneSeq创建输入序列的一个完全备份.调用函数cvCloneSeq(seq,storage)等同于调用函数cvSeqSlice(seq,CV_WHolE_SEQ,storage,1);

(18)SeqRemoveSlice

功能

函数cvSeqRemoveSlice删除序列中的slice部分

格式

void cvSeqRemoveSlice(CvSeq* seq,CvSlice slice);

参数

seq  序列

slice 序列中被删除的部分

(19)SeqInsertSlice

功能

函数cvSeqInsertSlice在序列中插入一个数组

格式

void cvSeqInsertSlice(CvSeq* seq,int before_index,const CvArr* from_arr);

seq 序列

before_index 插入位置

from_arr  获取元素的数组

说明

函数cvSeqInsertSlice在指定位置插入来自数组from_arr中的所有元素.数组from_arr可以是一个矩阵,也可以是另外一个序列

(20)SeqInvert

功能

函数cvSeqInvert对序列中的元素进行逆序操作.

格式

void cvSeqInvert(CvSeq* seq);

参数

seq 序列

说明

函数cvSeqInvert对序列进行逆序操作,也就是说,使第一个元素成为最后一个,最后一个元素成为第一个

(21)SeqSort

功能

函数cvSeqSort使用特定的比较函数对序列中的元素进行排序

在使用特定的比较函数应先进行如下声明

/*要实现的比较运算是: a<b?-1;a>b?1:0*/

typedef int (CV_CDECL* CvCmpFunc)(const void* a, const void*b,void* userdata);

格式

void cvSeqSort(CvSeq* seq,CvCmpFunc,void* userdata = NULL);

参数

seq 待排序的序列

func 比较函数,按照元素间的大小关系返回负数,零,或者正数

userdata 传递给比较函数的用户参数,在某些情况下,可避免全局变量的使用

说明

函数cvSeqSort使用特定的标准对序列进行排序.下面是一个函数实例

static int cmp_func(const void* _a,const void* _b,void* userdata)    
{    
    CvPoint* a = (CvPoint*)_a;    
    CvPoint* b = (CvPoint*)_b;    
    int y_diff = a->y - b->y;    
    int x_diff = a->x - b->x;    
    return  y_diff ? y_diff : x_diff;    
}    
CvMemStorage* storage = cvCreateMemStorage(0);    
CvSeq* seq = cvCreateSeq(CV_32SC2,sizeof(CvSeq),sizeof(CvPoint),storage);    
int i;    
for (int i=0;i<10;i++)    
{    
    CvPoint pt;    
    pt.x = rand() % 1000;    
    pt.y = rand() % 1000;    
    cvSeqPush(seq,&pt);    
}    
cvSeqSort(seq,cmp_func,0);    
  
  
//输出结果    
for (i=0;i < seq->total;i++)    
{    
    CvPoint* pt = (CvPoint*) cvGetSeqElem(seq,i);    
    printf("(%d,%d)",pt->x,pt->y);    
}    
  
  
cvReleaseMemStorage(&storage);   

(22)SeqSearch

功能

函数cvSeqSearch查询序列中的元素

在使用比较函数之前应先进行如下声明

/*要实现的比较运算是: a < b ? -1 :a > b ?1 : 0*/

typedef int (Cv_CDECL * CvCmpFunc)(Const void* a,const void * b, void * userdata);

格式

char* cvSeqSearch(CvSeq* seq,const  void*  elem, CvCmpFunc func , int is_sorted , int * elem_idx, void * userdata = NULL);

参数

seq 序列

elem 待查询的元素

func 比较函数,按照元素间的大小关系放回负数,零或正数(见函数cvSeqSort中的解释)

is_sorted  标志序列是否已经排序

elem_idx 输出参数,已查找到的元素索引值

Userdata  传递到比较函数的用户参数,在某些情况下,有助于避免使用全局变量.

说明

函数cvSeqSearch查找序列中的元素.如果序列已被排序,则使用二分查找(事件复杂度为O[log(N)]);否则使用简单线性查找.若查找元素不存在,则函数返回NULL指针,而将索引值设置为序列中的元素数(当使用线性查找时)或者是能够满足表达式seq(i) > elem的最小i值

(23)StartAppendToSeq

功能

函数cvStartAppendToSeq将数据写如序列中,并初始化该过程

格式

void cvStartAppendToSeq(CvSeq* seq,CvSeqWriter* writer);

参数

seq 指向序列的指针

writer 写入部分的状态,由此函数初始化

说明

函数cvStartAppendToSeq对数据写入序列的过程初始化.通过宏CV_WRITE_SEQ_ELEM(wtitten_elem,writer)可以将要写入的元素添加到序列的尾部.

注意 在写入期间,对序列的其他操作可能会产生错误的结果,甚至破坏该序列(具体说明参见函数cvFlishSeqWriter的相关描述,有助于避免这些错误).

(24)StartWriterSeq

功能

函数cvStartWriteSeq创建新序列,并初始化写入状态

格式

void cvStartWriteSeq(int  seq_flags, int header_size, int  elem_size,CvMemStorage* storage, CvSeqWriter*  writer);

参数

seq_flags  标志被创建的序列.如果序列还未传递给任何可处理特定序列类型的函数,则序列值等于0;否则,必须从之前定义的序列类型中选择一个合适的类型.

header_size 头部的大小.此参数值不小于sizeof(CvSeq).如果定义了某一类型或其扩展,则次参数必须与所定义类型的头大小相同

elem_size  元素的大小(以字节计),必须与序列类型一致.例如,如果创建了包含指针的序列(元素类型为CV_SEQ_ELTYPE_POINT),那么elem_size必须等于sizeof(CvPoint).

storage 序列(在内存的位置)

writer 写入部分的状态,由此函数初始化

说明

函数cvStartWriteSeq是函数cvCreateSeq与函数cvStartAppendToSeq的组合.指向被创建的序列指针存放在writer->seq中,该指针通过函数cvEndWriterSeq放回(应当在最后调用).

(25)EndWriteSeq

功能

函数cvEndWriteSeq

格式

CvSeq* cvEndWriteSeq(CvSeqWriter* writer);

参数

writer 写入部分的状态

说明

函数cvEndWriteSeq完成写入操作并返回指向被写入元素的序列的地址,同事,函数将截取最后那个不完整的序列块.块的剩余部分返回到内存之后,序列即可以被安全地读和写.

(26)FlushSeqWriter

功能

函数cvFlushSeqWriter根据写入状态刷新序列头部

格式

void cvFlushSeqWriter(CvSeqWriter* writer);

参数

writer 写入部分的状态

说明

函数cvFlishSeqWriter用来在写入过程中使用户在需要时读取序列中的元素,例如核查指定的条件.函数更新序列的头部,从而使读取序列中的数据成为可能.不过,写如操作并没有被关闭,因此随时都可以将数据写入序列.在某些需要经常刷新的算法中,应考虑使用cvSeqOush来代替此函数.

(27)StartReadSeq

功能

函数cvStartReadSeq初始化序列中的读取过程

格式

void cvStartReadSeq(const CvSeq* seq ,CvSeqRead* redaer,int reverse=0);

参数

seq 序列

reader  读取部分的状态,有此函数初始化

reverse  决定遍历序列的方向.如果为0,则读取顺序为从序列头部的元素开始;否则,从序列尾部开始.

说明

函数cvStartReadSeq初始化读取部分的状态.顺序读取可通过调用宏CV_READ_SEQ_ELEM(read_elem,reader)实现.逆序读取可通过调用宏CV_REV_READ_SEQ_ELEM(read_elem,reader)实现.这两个宏都将序列元素读入read_elem中,并将指针移到下一个元素.下列代码显示如果使用reader和writer

CvMemStorage* storage = cvCreateMemStorage(0);    
    CvSeq* seq = cvCreateSeq(CV_32SC1,sizeof(CvSeq),sizeof(int),storage);    
    CvSeqWriter writer;    
    CvSeqReader reader;    
    int i;    
    cvStartAppendToSeq(seq,&writer);    
    for (i=0;i<10;i++)    
    {    
        int val = rand() % 100;    
        CV_WRITE_SEQ_ELEM(val,writer);    
        printf("%d is written\n",val);    
    }    
    cvEndWriteSeq(&writer);    
    
    cvStartReadSeq(seq,&reader,0);    
    for (i=0;i<seq->total;i++)    
    {    
        int val;    
#if 1    
        CV_READ_SEQ_ELEM(val,reader);    
        printf("%d is read\n",val);    
#else     
        printf("%d is read\n",*(int*)reader.ptr);    
        CV_NEXT_SEQ_ELEM(seq->elem_size,reader);    
#endif    
    
    }    
    
    cvReleaseMemStorage(&storage);  

(28)GetSeqReaderPos

功能

函数cvGetSeqReaderPos返回当前读取器的位置

格式

int cvGetSeqReaderPos(CvSeqReader*  reader);

参数

reader  读取器的状态

说明

函数cvGetSeqReaderPos放回当前reader的位置在(0到reader->seq->total-1之间)

(29)SetSeqReaderPos

功能

函数cvSetSeqReaderPos移动读取器到指定的位置

格式

void cvSetSeqReaderPos(CvSeqReader* reader,int  index, int is_relative = 0);

参数

reader 读取器的状态

index 索引的位置.如果使用绝对位置,则实际位置为index % reader->seq->total.

is_relative 如果不为0,则索引(index)值为相对位置

说明

函数cvSetSeqReaderPos将读取器的位置移动到绝对位置,或相对于当前位置的相对位置上.

 

 

 

 

 

参考:http://hi.baidu.com/ltbing/blog/item/490d88948af27c14d21b7008.html

      http://jackchongsun.blog.163.com/blog/static/4071607201019101312221/

https://blog.csdn.net/yuzuweicool/article/details/27583427

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
EMCV是一个可以在TI C6000系列DSP上运行的计算机视觉库,它提供了与OpenCV完全一致的函数接口,使得你可以轻松地将你的OpenCV算法移植到DSP上,而无需修改任何代码。EMCV目前已经支持了IplImage、CvMat、CvSeq等基本数据结构,可以使用cvCreateImage等函数来创建和释放图像,以及进行轮廓检测等操作。 在使用EMCV时,你需要在CCS环境下编写测试程序进行测试。测试程序可以完成一些简单的任务,比如创建图像、添加矩形框、释放图像等。这些测试程序包含了一些OpenCV的基本数据结构,比如CvPoint、CvScalar、CvSize和IplImage,以及一些基本的函数,比如cvCreateImage、cvRectangle和cvReleaseImage。如果这些代码能够成功运行,那么就表明EMCV已经成功移植到DSP上了。 以下是一个示例程序的代码: ```c #include <stdio.h> #include <stdlib.h> #include <emcv.h> int main() { // 创建图像 IplImage* image = cvCreateImage(cvSize(640, 480), IPL_DEPTH_8U, 3); // 添加矩形框 CvPoint pt1 = cvPoint(100, 100); CvPoint pt2 = cvPoint(200, 200); CvScalar color = cvScalar(0, 255, 0); cvRectangle(image, pt1, pt2, color, 2); // 释放图像 cvReleaseImage(&image); return 0; } ``` 这段代码演示了如何在DSP上使用EMCV进行图像处理。首先,我们创建了一个大小为640x480的RGB图像。然后,我们在图像上添加了一个矩形框,矩形框的左上角坐标为(100, 100),右下角坐标为(200, 200),颜色为绿色。最后,我们释放了图像的内存。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值