openCV——cvSeq

本文详细解析了OpenCV库中CvSeq的数据结构和常用操作函数,包括创建、复制、排序、搜索、删除元素等方法,并通过实例代码展示了如何正确遍历和操作CvSeq序列,避免常见的边界错误。

openCV中 cvSeq的用法说明

函数原型 说明
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)

功能:初始化序列中的读取过程

 

 

OpenCV CvSeq的内部结构探讨

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

[cpp]  view plain copy
  1. CvSeq *pCurSeq = pInputSeq;  
  2. int index = 0;  
  3. while( pCurSeq=pCurSeq->h_next )  
  4. {  
  5.    if( process(pCurSeq) )  
  6.    {  
  7.       pCurSeq = pCurSeq->h_prev; //这里为了简单不考虑是否为列表头  
  8.       cvSeqRemove(pInputSeq, index);  
  9.       --index;  
  10.    }  
  11.    ++index;  
  12. }  

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

为 什么会有这样的错误呢?看一下CvSeq的源代码就可略见一斑。下面是OpenCV2.0版本有关CvSeq的定义
  1. #define CV_TREE_NODE_FIELDS(node_type)                          /  
  2.     int       flags;         /* micsellaneous flags */          /  
  3.     int       header_size;   /* size of sequence header */      /  
  4.     struct    node_type* h_prev; /* previous sequence */        /  
  5.     struct    node_type* h_next; /* next sequence */            /  
  6.     struct    node_type* v_prev; /* 2nd previous sequence */    /  
  7.     struct    node_type* v_next  /* 2nd next sequence */  
  8. /* 
  9.    Read/Write sequence. 
  10.    Elements can be dynamically inserted to or deleted from the sequence. 
  11. */  
  12. #define CV_SEQUENCE_FIELDS()                                            /  
  13.     CV_TREE_NODE_FIELDS(CvSeq);                                         /  
  14.     int       total;          /* total number of elements */            /  
  15.     int       elem_size;      /* size of sequence element in bytes */   /  
  16.     char*     block_max;      /* maximal bound of the last block */     /  
  17.     char*     ptr;            /* current write pointer */               /  
  18.     int       delta_elems;    /* how many elements allocated when the seq grows */  /  
  19.     CvMemStorage* storage;    /* where the seq is stored */             /  
  20.     CvSeqBlock* free_blocks;  /* free blocks list */                    /  
  21.     CvSeqBlock* first; /* pointer to the first sequence block */  
  22. typedef struct CvSeq  
  23. {  
  24.     CV_SEQUENCE_FIELDS()  
  25. }  
  26. CvSeq;  

原来CvSeq本身就是一个可增长的序列,CvSeq::total是指序列内部有效元素的个数;而h_next和h_prev并不是指向CvSeq内部元 素的指针,它们是指向其它CvSeq的。再回到文章最初的代码,我们可以看到该代码具有逻辑上的错误,首先while语句遍历的是所有的CvSeq,使用 process处理每一个CvSeq,而遇到需要删除的CvSeq时,又使用才cvSeqRemove删除当前CvSeq中的第index个元素。实际上 此时index很可能超出了当前CvSeq中总元素的个数,所以出现了超出边界的错误。正确的做法是直接删除该CvSeq。
  1. CvSeq *pCurSeq = pInputSeq;  
  2. CvSeq *pOldSeq = NULL;  
  3. while( pCurSeq )  
  4. {  
  5.    if( process(pCurSeq) )  
  6.    {  
  7.       pOldSeq = pCurSeq;  
  8.       if(pOldSeq->h_prev)  
  9.       {  
  10.          pCurSeq = pOldSeq->h_prev;  
  11.          pCurSeq->h_next = pOldSeq->h_next;  
  12.          pOldSeq->h_next->h_prev = pCurSeq;  
  13.          pCurSeq=pCurSeq->h_next;  
  14.          cvClearSeq( pOldSeq );  
  15.       }  
  16.       else  
  17.       {  
  18.          pCurSeq = pOldSeq->h_next;  
  19.          pCurSeq->h_prev = NULL;  
  20.          cvClearSeq( pOldSeq );  
  21.       }  
  22.    }  
  23.    else  
  24.    {  
  25.       pCurSeq=pCurSeq->h_next;  
  26.    }  
  27. }  


后来在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.

摘自:http://blog.youkuaiyun.com/wqvbjhc/article/details/5497017

### OpenCV在单片机上的使用与移植 #### 1. 单片机环境下的OpenCV适用性分析 尽管OpenCV是一个功能强大的计算机视觉库,但它通常运行于具有较高计算能力的设备上,如PC或嵌入式Linux平台。对于资源受限的单片机(如Arduino、STM32等),直接部署完整的OpenCV可能并不现实[^4]。 然而,部分轻量化的图像处理算法可以通过裁剪后的OpenCV版本实现。例如,在ARM Cortex-M系列微控制器上,可以尝试通过交叉编译的方式构建精简版的OpenCV库,并将其适配到目标硬件平台上[^1]。 --- #### 2. 跨平台编译流程概述 为了使OpenCV能够在单片机环境中工作,需要完成以下几个核心步骤: - **选择合适的工具链** 对于基于ARM架构的单片机,推荐使用`arm-none-eabi-gcc`作为交叉编译器。该工具链专为裸机环境设计,能够生成适合嵌入式系统的二进制文件[^2]。 - **配置CMake选项** 利用CMake图形化界面(如`cmake-gui`)简化配置过程。需特别注意以下参数设置: - `CMAKE_SYSTEM_NAME`: 设置为目标系统名称(通常是`Generic`或自定义值)。 - `CMAKE_C_COMPILER` 和 `CMAKE_CXX_COMPILER`: 指定交叉编译器路径。 - `BUILD_SHARED_LIBS`: 关闭动态链接支持,改为静态库模式以减少依赖项[^3]。 - **优化代码体积** 在实际应用中,仅保留必要的模块(如core、imgproc)。移除非必需的功能可显著降低内存占用和编译复杂度。 --- #### 3. 实际案例分享 以下是针对特定场景的一个简单示例——将OpenCV移植至OK6410开发板的过程摘要: ```bash # 安装必要软件包 sudo apt-get install cmake build-essential libgtk2.0-dev pkg-config python-numpy # 下载并解压OpenCV源码 wget https://github.com/opencv/opencv/archive/refs/tags/3.4.16.zip unzip opencv-3.4.16.zip # 创建build目录并进入 mkdir -p ~/opencv/build && cd ~/opencv/build # 配置CMake cmake \ -D CMAKE_BUILD_TYPE=Release \ -D BUILD_EXAMPLES=OFF \ -D WITH_QT=OFF \ -D WITH_OPENGL=OFF \ -D INSTALL_PYTHON_EXAMPLES=OFF \ -D OPENCV_EXTRA_MODULES_PATH=/path/to/contrib/modules \ ../opencv-3.4.16/ # 编译 make -j$(nproc) # 安装 sudo make install ``` 上述脚本适用于具备一定存储空间和支持标准GNU/Linux API的目标设备。而对于纯粹RTOS驱动型单片机,则需进一步调整编译策略。 --- #### 4. 技术挑战及解决方案 | 挑战 | 解决方案 | |------|----------| | 存储容量不足 | 压缩OpenCV模块范围;采用外部Flash扩展存储区。 | | 计算性能瓶颈 | 减少高耗能运算;借助DSP协处理器加速关键环节。 | | 接口兼容性差 | 自定义封装函数桥接原生API与HAL层交互逻辑。 | --- ### 示例代码片段 假设我们希望检测一幅灰度图中的边缘特征,可以在单片机端执行如下伪代码: ```c #include "cv.h" #include "highgui.h" int main() { IplImage* img = cvLoadImage("input.jpg", CV_LOAD_IMAGE_GRAYSCALE); if (!img) return -1; CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* contours = NULL; cvFindContours(img, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); // 处理轮廓数据... } ``` 此段程序展示了如何加载图片以及提取其边界信息。需要注意的是,最终效果取决于底层硬件的能力限制。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值