nCV之CXCORE篇 (3)



转自 http://blog.csdn.net/wangjie0377/article/details/6629293

 

离散变换

 

 

DFT

执行一维或者二维浮点数组的离散傅立叶正变换或者离散傅立叶逆变换

#define CV_DXT_FORWARD 0

#define CV_DXT_INVERSE 1

#define CV_DXT_SCALE:2

#define CV_DXT_ROWS: 4

#define CV_DXT_INV_SCALE (CV_DXT_SCALE|CV_DXT_INVERSE)

#define CV_DXT_INVERSE_SCALE CV_DXT_INV_SCALE

void cvDFT( const CvArr* src, CvArr* dst, int flags, int nonzero_rows=0);

src

输入数组, 实数或者复数.

dst

输出数组,和输入数组有相同的类型和大小。

flags

变换标志, 下面的值的组合:

CV_DXT_FORWARD - 正向 1D 或者 2D 变换. 结果不被缩放.

CV_DXT_INVERSE - 逆向 1D 或者 2D 变换. 结果不被缩放.当然 CV_DXT_FORWARD 和 CV_DXT_INVERSE 是互斥的.

CV_DXT_SCALE - 对结果进行缩放: 用数组元素数除以它. 通常, 它和 CV_DXT_INVERSE 组合在一起,可以使用缩写 CV_DXT_INV_SCALE.

CV_DXT_ROWS - 输入矩阵的每个独立的行进行整型或者逆向变换。这个标志允许用户同时变换多个向量,减少开销(它往往比处理它自己要快好几倍), 进行 3D 和高维的变换等等。

nonzero_rows

输入矩阵中非0行的个数(在2维的Forward变换中),或者是输出矩阵中感兴趣的行(在反向的2维变换中)。如果这个值是负数,0,或者大于总行数的一个值,它将会被忽略。这个参数可以用来加速2维DFT/IDFT的速度。见下面的例子。


函数 cvDFT 执行一维或者二维浮点数组的离散傅立叶正变换或者离散傅立叶逆变换:

N 元一维向量的正向傅立叶变换:

y = F(N)•x, 这里 F(N)jk=exp(-i•2Pi•j•k/N), i=sqrt(-1)

N 元一维向量的逆向傅立叶变换:

x'= (F(N))-1•y = conj(F(N))•y

x = (1/N)•x

M×N 元二维向量的正向傅立叶变换:

Y = F(M)•X•F(N)

M×N 元二维向量的逆向傅立叶变换:

X'= conj(F(M))•Y•conj(F(N))

X = (1/(M•N))•X'

假设时实数数据 (单通道) ,从 IPL 借鉴过来的压缩格式被用来表现一个正向傅立叶变换的结果或者逆向傅立叶变换的输入:

Re Y0,0: Re Y0,1:Im Y0,1:Re Y0,2: Im Y0,2 ... Re Y0,N/2-1 Im Y0,N/2-1 Re Y0,N/2

Re Y1,0: Re Y1,1:Im Y1,1:Re Y1,2: Im Y1,2 ... Re Y1,N/2-1 Im Y1,N/2-1 Re Y1,N/2

Im Y1,0: Re Y2,1:Im Y2,1:Re Y2,2: Im Y2,2 ... Re Y2,N/2-1 Im Y2,N/2-1 Im Y2,N/2

............................................................................................

Re YM/2-1,0 Re YM-3,1 Im YM-3,1 Re YM-3,2 Im YM-3,2 ... Re YM-3,N/2-1 Im YM-3,N/2-1 Re YM-3,N/2

Im YM/2-1,0 Re YM-2,1 Im YM-2,1 Re YM-2,2 Im YM-2,2 ... Re YM-2,N/2-1 Im YM-2,N/2-1 Im YM-2,N/2

Re YM/2,0:Re YM-1,1 Im YM-1,1 Re YM-1,2 Im YM-1,2 ... Re YM-1,N/2-1 Im YM-1,N/2-1 Im YM-1,N/2

注意:如果 N 时偶数最后一列存在(is present), 如果 M 时偶数最后一行(is present).

如果是一维实数的变换结果就像上面矩阵的第一行的形式。利用DFT求解二维卷积

CvMat* A = cvCreateMat( M1, N1, CV_32F );

CvMat* B = cvCreateMat( M2, N2, A->type );

 

// it is also possible to have only abs(M2-M1)+1×abs(N2-N1)+1

// part of the full convolution result

CvMat* conv = cvCreateMat( A->rows + B->rows - 1, A->cols + B->cols - 1, A->type );

 

// initialize A and B

...

 

int dft_M = cvGetOptimalDFTSize( A->rows + B->rows - 1 );

int dft_N = cvGetOptimalDFTSize( A->cols + B->cols - 1 );

 

CvMat* dft_A = cvCreateMat( dft_M, dft_N, A->type );

CvMat* dft_B = cvCreateMat( dft_M, dft_N, B->type );

CvMat tmp;

 

// copy A to dft_A and pad dft_A with zeros

cvGetSubRect( dft_A, &tmp, cvRect(0,0,A->cols,A->rows));

cvCopy( A, &tmp );

cvGetSubRect( dft_A, &tmp, cvRect(A->cols,0,dft_A->cols - A->cols,A->rows));

cvZero( &tmp );

// no need to pad bottom part of dft_A with zeros because of

// use nonzero_rows parameter in cvDFT() call below

 

cvDFT( dft_A, dft_A, CV_DXT_FORWARD, A->rows );

 

// repeat the same with the second array

cvGetSubRect( dft_B, &tmp, cvRect(0,0,B->cols,B->rows));

cvCopy( B, &tmp );

cvGetSubRect( dft_B, &tmp, cvRect(B->cols,0,dft_B->cols - B->cols,B->rows));

cvZero( &tmp );

// no need to pad bottom part of dft_B with zeros because of

// use nonzero_rows parameter in cvDFT() call below

 

cvDFT( dft_B, dft_B, CV_DXT_FORWBRD, B->rows );

 

cvMulSpectrums( dft_A, dft_B, dft_A, 0 );

 

cvDFT( dft_A, dft_A, CV_DXT_INV_SCALE, conv->rows ); // calculate only the top part

cvGetSubRect( dft_A, &tmp, cvRect(0,0,conv->cols,conv->rows) );

 

cvCopy( &tmp, conv );

 

GetOptimalDFTSize

对于给定的矢量尺寸返回最优DFT尺寸

int cvGetOptimalDFTSize( int size0 );

size0

矢量长度.

函数 cvGetOptimalDFTSize 返回最小值 N that is greater to equal to size0, such that DFT of a vector of size N can be computed fast. In the current implementation N=2p×3q×5r for some p, q, r.

The function returns a negative number if size0 is too large (very close to INT_MAX)

 

MulSpectrums

两个傅立叶频谱的每个元素的乘法(Performs per-element multiplication of two Fourier spectrums)

void cvMulSpectrums( const CvArr* src1, const CvArr* src2, CvArr* dst, int flags );

src1

第一输入数组

src2

第二输入数组

dst

输出数组,和输入数组有相同的类型和大小。

flags

下面列举的值的组合:

CV_DXT_ROWS - 把数组的每一行视为一个单独的频谱 (参见 cvDFT 的参数讨论).

CV_DXT_MUL_CONJ - 在做乘法之前取第二个输入数组的共轭.

函数 cvMulSpectrums 执行两个 CCS-packed 或者实数或复数傅立叶变换的结果复数矩阵的每个元素的乘法。(performs per-element multiplication of the two CCS-packed or complex matrices that are results of real or complex Fourier transform.)

该函数和 cvDFT 可以用来快速计算两个数组的卷积.

 

DCT

执行一维或者二维浮点数组的离散馀弦变换或者离散反馀弦变换

#define CV_DXT_FORWARD 0

#define CV_DXT_INVERSE 1

#define CV_DXT_ROWS: 4

void cvDCT( const CvArr* src, CvArr* dst, int flags );

src

输入数组, 1D 或者 2D 实数数组.

dst

输出数组,和输入数组有相同的类型和大小。

flags

变换标志符, 下面值的组合:

CV_DXT_FORWARD - 1D 或者 2D 馀弦变换.

CV_DXT_INVERSE - 1D or 2D 反馀弦变换.

CV_DXT_ROWS - 对输入矩阵的每个独立的行进行馀弦或者反馀弦变换. 这个标志允许用户同时进行多个向量的变换,可以用来减少开销(它往往比处理它自己要快好几倍),以及 3D 和高维变换等等。

函数 cvDCT 执行一维或者二维浮点数组的离散馀弦变换或者离散反馀弦变换:

N 元一维向量的馀弦变换:

y = C(N)•x, 这里 C(N)jk=sqrt((j==0?1:2)/N)•cos(Pi•(2k+1)•j/N)

N 元一维向量的反馀弦变换:

x = (C(N))-1•y = (C(N))T•y

M×N 元二维向量的馀弦变换:

Y = (C(M))•X•(C(N))T

M×N 元二维向量的反馀弦变换:

X = (C(M))T•Y•C(N)

 

 

内存存储(memory storage)

CvMemStorage

Growing memory storage

typedef struct CvMemStorage

{

struct CvMemBlock* bottom;

struct CvMemBlock* top;

struct CvMemStorage* parent;

int block_size;

int free_space;

} CvMemStorage;

内存存储器是一个可用来存储诸如序列,轮廓,图形,子划分等动态增长数据结构的底层结构。它是由一系列以同等大小的内存块构成,呈列表型 ---bottom 域指的是列首,top 域指的是当前指向的块但未必是列尾.在bottom和top之间所有的块(包括bottom, 不包括top)被完全占据了空间;在 top和列尾之间所有的块(包括块尾,不包括top)则是空的;而top块本身则被占据了部分空间 -- free_space 指的是top块剩馀的空字节数。

新分配的内存缓冲区(或显式的通过 cvMemStorageAlloc 函数分配,或隐式的通过 cvSeqPush, cvGraphAddEdge等高级函数分配)总是起始于当前块(即top块)的剩馀那部分,如果剩馀那部分能满足要求(够分配的大小)。分配后,free_space 就减少了新分配的那部分内存大小,外加一些用来保存适当列型的附加大小。当top块的剩馀空间无法满足被分配的块(缓冲区)大小时,top块的下一个存储块被置为当前块(新的top块) -- free_space 被置为先前分配的整个块的大小。

如果已经不存在空的存储块(即:top块已是列尾),则必须再分配一个新的块(或从parent那继承,见 cvCreateChildMemStorage)并将该块加到列尾上去。于是,存储器(memory storage)就如同栈(Stack)那样, bottom指向栈底,(top, free_space)对指向栈顶。栈顶可通过 cvSaveMemStoragePos保存,通过 cvRestoreMemStoragePos 恢复指向, 通过 cvClearStorage 重置。

CvMemBlock

内存存储块结构

typedef struct CvMemBlock

{

struct CvMemBlock* prev;

struct CvMemBlock* next;

} CvMemBlock;

CvMemBlock 代表一个单独的内存存储块结构。 内存存储块中的实际数据存储在 header块 之后(即:存在一个头指针 head 指向的块 header ,该块不存储数据),于是,内存块的第 i 个字节可以通过表达式 ((char*)(mem_block_ptr+1))[i] 获得。然而,通常没必要直接去获得存储结构的域。

CvMemStoragePos

内存存储块地址

typedef struct CvMemStoragePos

{

CvMemBlock* top;

int free_space;

} CvMemStoragePos;

该结构(如以下所说)保存栈顶的地址,栈顶可以通过 cvSaveMemStoragePos 保存,也可以通过 cvRestoreMemStoragePos 恢复。

CreateMemStorage

创建内存块

CvMemStorage* cvCreateMemStorage( int block_size=0 );

block_size

存储块的大小以字节表示。如果大小是 0 byte, 则将该块设置成默认值 -- 当前默认大小为64k.

函数 cvCreateMemStorage 创建一内存块并返回指向块首的指针。起初,存储块是空的。头部(即:header)的所有域值都为 0,除了 block_size 外.

CreateChildMemStorage

创建子内存块

CvMemStorage* cvCreateChildMemStorage( CvMemStorage* parent );

parent

父内存块

函数 cvCreateChildMemStorage 创建一类似于普通内存块的子内存块,除了内存分配/释放机制不同外。当一个子存储块需要一个新的块加入时,它就试图从parent 那得到这样一个块。如果 parent 中 还未被占据空间的那些块中的第一个块是可获得的,就获取第一个块(依此类推),再将该块从 parent 那里去除。如果不存在这样的块,则 parent 要么分配一个,要么从它自己 parent (即:parent 的 parent) 那借个过来。换句话说,完全有可能形成一个链或更为复杂的结构,其中的内存存储块互为 child/ parent 关系(父子关系)。当子存储结构被释放或清除,它就把所有的块还给各自的 parent. 在其他方面,子存储结构同普通存储结构一样。

子存储结构在下列情况中是非常有用的。想象一下,如果用户需要处理存储在某个块中的动态数据,再将处理的结果存放在该块中。在使用了最简单的方法处理后,临时数据作为输入和输出数据被存放在了同一个存储块中,于是该存储块看上去就类似下面处理后的样子: Dynamic data processing without using child storage.

结果,在存储块中,出现了垃圾(临时数据)。然而,如果在开始处理数据前就先建立一个子存储块,将临时数据写入子存储块中并在最后释放子存储块,那么最终在源/目的存储块(source / destination storage) 中就不会出现垃圾, 于是该存储块看上去应该是如下形式:Dynamic data processing using a child storage.

ReleaseMemStorage

释放内存块

void cvReleaseMemStorage( CvMemStorage** storage );

storage

指向被释放了的存储块的指针

函数 cvReleaseMemStorage 释放所有的存储(内存)块 或者 将它们返回给各自的 parent(如果需要的话)。接下来再释放 header块(即:释放头指针 head 指向的块 = free(head))并清除指向该块的指针(即:head = NULL)。在释放作为 parent 的块之前,先清除各自的 child 块。

ClearMemStorage

清空内存存储块

void cvClearMemStorage( CvMemStorage* storage );

storage

存储存储块

函数 cvClearMemStorage 将存储块的 top 置到存储块的头部(注:清空存储块中的存储内容)。该函数并不释放内存(仅清空内存)。假使该内存块有一个父内存块(即:存在一内存块与其有父子关系),则函数就将所有的块返回给其 parent.

MemStorageAlloc

在存储块中分配一内存缓冲区

void* cvMemStorageAlloc( CvMemStorage* storage, size_t size );

storage

内存块.

size

缓冲区的大小.

函数 cvMemStorageAlloc 在存储块中分配一内存缓冲区。该缓冲区的大小不能超过内存块的大小,否则就会导致运行时错误。缓冲区的地址被调整为CV_STRUCT_ALIGN 字节 (当前为 sizeof(double)).

MemStorageAllocString

在存储块中分配一文本字符串

typedef struct CvString

{

int len;

char* ptr;

}

CvString;

 

CvString cvMemStorageAllocString( CvMemStorage* storage, const char* ptr, int len=-1 );

storage

存储块

ptr

字符串

len

字符串的长度(不计算‘\0’)。如果参数为负数,函数就计算该字符串的长度。

函数 cvMemStorageAlloString 在存储块中创建了一字符串的拷贝。它返回一结构,该结构包含字符串的长度(该长度或通过用户传递,或通过计算得到)和指向被拷贝了的字符串的指针。

 

SaveMemStoragePos

保存内存块的位置(地址)

void cvSaveMemStoragePos( const CvMemStorage* storage, CvMemStoragePos* pos );

storage

内存块.

pos

内存块顶部位置。

函数 cvSaveMemStoragePos 将存储块的当前位置保存到参数 pos 中。 函数 cvRestoreMemStoragePos 可进一步获取该位置(地址)。

RestoreMemStoragePos

恢复内存存储块的位置

void cvRestoreMemStoragePos( CvMemStorage* storage, CvMemStoragePos* pos );

storage

内存块.

pos

新的存储块的位置

函数 cvRestoreMemStoragePos 通过参数 pos 恢复内存块的位置。该函数和函数 cvClearMemStorage 是释放被占用内存块的唯一方法。注意:没有什么方法可去释放存储块中被占用的部分内存。

序列

CvSeq

可动态增长元素序列(OpenCV_1.0已发生改变,详见cxtypes.h) Growable sequence of elements

#define CV_SEQUENCE_FIELDS() \

int flags; \

int header_size; \

struct CvSeq* h_prev; \

struct CvSeq* h_next; \

struct CvSeq* v_prev; \

struct CvSeq* v_next; \

int total; \

int elem_size; \

char* block_max; \

char* ptr; \

int delta_elems; \

CvMemStorage* storage; \

CvSeqBlock* free_blocks; \

CvSeqBlock* first;

 

 

typedef struct CvSeq

{

CV_SEQUENCE_FIELDS()

} CvSeq;

结构CvSeq是所有OpenCV动态数据结构的基础。在1.0版本中,将前六个成员剥离出来定义成一个宏. 通过不同寻常的宏定义简化了带有附加参数的结构 CvSeq 的扩展。为了扩展 CvSeq, 用户可以定义一新的数据结构或在通过宏CV_SEQUENCE_FIELDS()所包括的 CvSeq 的域后在放入用户自定义的域。

有两种类型的序列 -- 稠密序列和稀疏序列。稠密序列都派生自 CvSeq, 它们用来代表可扩展的一维数组 -- 向量,栈,队列,双端队列。数据间不存在空隙(即:连续存放)-- 如果元素从序列中间被删除或插入新的元素到序列中(不是两端),那么此元素后边的相关元素会被移动。稀疏序列都派生自 CvSet,后面会有详细的讨论。它们都是由节点所组成的序列,每一个节点要么被占用空间要么是空,由 flag 标志指定。这些序列作为无序的数据结构而被使用,如点集,图,哈希表等。

域 header_size(结构的大小) 含有序列头部节点的实际大小,此大小大于或等于 sizeof(CvSeq).当这个宏用在序列中时,应该等于 sizeof(CvSeq),若这个宏用在其他结构中,如CvContour,结构的大小应该大于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 表示稀疏序列),同时包含形形色色的信息。

低 CV_SEQ_ELTYPE_BITS 位包含元素类型的 ID(标示符)。大多数处理函数并不会用到元素类型,而会用到存放在 elem_size 中的元素大小 。如果序列中包含 CvMat 中的数据,那么元素的类型就与 CvMat 中的类型相匹配,如:CV_32SC2 可以被使用为由二维空间中的点序列, CV_32FC1用为由浮点数组成的序列等。通过宏 CV_SEQ_ELTYPE(seq_header_ptr) 来获取序列中元素的类型。处理数字序列的函数判断: elem.size 等同于序列元素的大小。除了与 CvMat 相兼容的类型外,还有几个在头 cvtypes.h 中定义的额外的类型。

Standard Types of Sequence Elements

 

#define CV_SEQ_ELTYPE_POINT CV_32SC2

#define CV_SEQ_ELTYPE_CODE CV_8UC1

#define CV_SEQ_ELTYPE_GENERIC 0

#define CV_SEQ_ELTYPE_PTR CV_USRTYPE1

#define CV_SEQ_ELTYPE_PPOINT CV_SEQ_ELTYPE_PTR

#define CV_SEQ_ELTYPE_INDEX CV_32SC1

#define CV_SEQ_ELTYPE_GRAPH_EDGE CV_SEQ_ELTYPE_GENERIC

#define CV_SEQ_ELTYPE_GRAPH_VERTEX CV_SEQ_ELTYPE_GENERIC

#define CV_SEQ_ELTYPE_TRIAN_ATR CV_SEQ_ELTYPE_GENERIC

#define CV_SEQ_ELTYPE_CONNECTED_COMP CV_SEQ_ELTYPE_GENERIC

#define CV_SEQ_ELTYPE_POINT3D CV_32FC3

后面的 CV_SEQ_KIND_BITS 字节表示序列的类型:

Standard Kinds 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)

 

 

#define CV_SEQ_KIND_GRAPH (3 << CV_SEQ_ELTYPE_BITS)

#define CV_SEQ_KIND_SUBDIV2D (4 << CV_SEQ_ELTYPE_BITS)

 

CvSeqBlock

连续序列块

typedef struct CvSeqBlock

{

struct CvSeqBlock* prev;

struct CvSeqBlock* next;

int start_index;

int count;

char* data;

} CvSeqBlock;

序列块构成一个双向的循环列表,因此指针 prev 和 next 永远不会为 null, 而总是指向序列中的前一个和后一个序列块。也就是说:最后一个序列块的 next 指向的就是序列中的第一个块,而第一个块的 prev 指向最后一个块。域 start_index 和 count 有助于跟踪序列中块的位置。 例如,一个含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;

int end_index;

} CvSlice;

 

inline CvSlice cvSlice( int start, int end );

#define CV_WHOLE_SEQ_END_INDEX 0x3fffffff

#define CV_WHOLE_SEQ cvSlice(0, CV_WHOLE_SEQ_END_INDEX)

 

 

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

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

如果函数并不接受分割参数,但你还是想要处理序列的一部分,那么可以使用函数 cvSeqSlice 获取子序列。 

CreateSeq

创建一序列

CvSeq* cvCreateSeq( 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).

函数 cvCreateSeq 创建一序列并且返回指向该序列的指针。函数在存储块中分配序列的头部作为一个连续躯体,并且设置结构的 flags域, elem_size域, header_size域 和 storage域 的值为被传递过来的值,设置 delta_elems 为默认值(可通过函数 cvSetSeqBlockSize 重新对其赋值),清空其他的头 部域,包括前sizeof(CvSeq) 个字节的空间。

SetSeqBlockSize

设置序列块的大小

void cvSetSeqBlockSize( CvSeq* seq, int delta_elems );

seq

序列

delta_elems

满足元素所需的块的大小

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

SeqPush

添加元素到序列的尾部

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

seq

element

添加的元素

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

The following code demonstrates how to create a new sequence using this function:

 

CvMemStorage* storage = cvCreateMemStorage(0);

CvSeq* seq = cvCreateSeq( CV_32SC1,

sizeof(CvSeq),

sizeof(int),

storage );

int i;

for( i = 0; i < 100; i++ )

{

int* added = (int*)cvSeqPush( seq, &i );

printf( "%d is added\n", *added );

}

 

...

 

cvReleaseMemStorage( &storage );

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

SeqPop

删除序列尾部元素

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

seq

序列

element

可选参数。如果该指针不为空,就拷贝被删元素到指针指向的位置

函数 cvSeqPop 从序列中删除一元素。如果序列已经为空,就报告一错误。函数时间复杂度为 O(1).

SeqPushFront

在序列头部添加元素

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

seq

序列

element

添加的元素

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

SeqPopFront

删除序列的头部元素

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

seq

序列

element

可选参数。如果该指针不为空,就拷贝被珊元素到指针指向的位置。

函数 cvSeqPopFront 删除序列的头部元素。如果序列已经为空,就报告一错误。函数时间复杂度为 O(1).

SeqPushMulti

添加多个元素到序列尾部或头部。

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

seq

序列

elements

待添加的元素

count

添加的元素个数

in_front

标示在头部还是尾部添加元素

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

CV_FRONT( != 0) -- 在序列头部添加元素。

函数 cvSeqPushMulti 在序列头部或尾部添加多个元素。元素按输入数组中的顺序被添加到序列中,不过它们可以添加到不同的序列中

SeqPopMulti

删除多个序列头部或尾部的元素

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

seq

序列

elements

待删除的元素

count

删除的元素个数

in_front

标示在头部还是尾部删除元素

CV_BACK ( = 0) -- 删除序列尾部元素。

CV_FRONT( != 0) -- 删除序列头部元素。

函数 cvSeqPopMulti 删除多个序列头部或尾部的元素。如果待删除的元素个数超过了序列中的元素总数,则函数删除尽可能多的元素 。

SeqInsert

在序列中添加元素

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

seq

序列

before_index

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

element

待插入的元素

函数 cvSeqInsert 移动从被插入的位置到序列尾部元素所在的位置的所有元素,如果指针 element 不位 null, 则拷贝 element 中的元素到指定位置。函数返回指向被插入元素的指针。

SeqRemove

从序列中删除指定的元素。

void cvSeqRemove( CvSeq* seq, int index );

seq

目标序列

index

被删除元素的索引或位置。

函数 cvSeqRemove 删除seq中指定索引(位置)的元素。如果这个索引超出序列的元素个数,会报告出错。企图从空序列中删除元素,函数也将报告错误。函数通过移动序列中的元素来删除被索引的元素。

ClearSeq

清空序列

void cvClearSeq( CvSeq* seq );

seq

Sequence.

seq

序列

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

GetSeqElem

返回索引所指定的元素指针

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 代表所需元素的索引。该宏首先核查所需的元素是否属于第一个块,如果是,则返回该元素,否则,该宏就调用主函数 GetSeqElem. 如果索引为负数的话,则总是调用函数 cvGetSeqElem。函数的时间复杂度为 O(1), 假设块的大小要比元素的数量要小。

SeqElemIdx

返回序列中元素的索引

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

seq

序列

element

指向序列中元素的指针

block

可选参数,如果不为空(NULL),则存放包含该元素的块的地址

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

cvSeqToArray

拷贝序列中的元素到一个连续的内存块中

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

seq

序列

elemenets

指向目的(存放拷贝元素的)数组的指针,指针指向的空间必须足够大。

slice

拷贝到序列中的序列部分。

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

MakeSeqHeaderForArray

构建序列

CvSeq* 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

序列中元素的总数。参数值必须等于数据的大小

seq

指向被使用作为序列头部的局部变量

block

指向局部变量的指针

函数 cvMakeSeqHeaderForArray 初始化序列的头部。序列块由用户分配(例如:在栈上)。该函数不拷贝数据。创建的序列只包含一个块,和一个 NULL指针,因此可以读取指针,但试图将元素添加到序列中则多数会引发错误。

SeqSlice

为各个序列碎片建立头

CvSeq* cvSeqSlice( const CvSeq* seq, CvSlice slice,

CvMemStorage* storage=NULL, int copy_data=0 );

seq

序列

slice

部分序列块

storage

存放新的序列和拷贝数据(如果需要)的目的存储空间。如果为NULL, 则函数使用包含该输入数据的存储空间。

copy_data

标示是否要拷贝元素,如果 copy_data != 0, 则需要拷贝;如果 copy_data == 0, 则不需拷贝。

函数 cvSeqSlice 创建一序列,该序列表示输入序列中特定的一部分(slice),。新序列要么与原序列共享元素要么拥有自己的一份拷贝。因此,如果有人需要去处理 该部分序列,但函数却没有 slice 参数, 则使用该函数去获取该序列。.

CloneSeq

创建序列的一份拷贝

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

seq

序列

storage

存放新序列的 header部分和拷贝数据(如果需要)的目的存储块。如果为 NULL, 则函数使用包含输入序列的存储块 。

函数 cvCloneSeq 创建输入序列的一份完全拷贝。调用函数 cvCloneSeq (seq, storage) 等同于调用 cvSeqSlice(seq, CV_WHOLE_SEQ, storage, 1).

SeqRemoveSlice

删除序列的 slice部分

void cvSeqRemoveSlice( CvSeq* seq, CvSlice slice );

seq

序列

slice

序列中被移动的那部分

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

SeqInsertSlice

在序列中插入一数组

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

seq

序列

slice

序列中被移动的那部分

from_arr

从中获取元素的数组

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

SeqInvert

将序列中的元素进行逆序操作

void cvSeqInvert( CvSeq* seq );

seq

序列

函数 cvSeqInvert 对序列进行逆序操作 -- 即:使第一个元素成为最后一个,最后一个元素为第一个。

SeqSort

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

 

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

 

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

seq

待排序的序列

func

比较函数,按照元素间的大小关系返回负数,零,正数(见:上面的声明和下面的例子) --相关函数为 C 运行时库中的 qsort, 后者(qsort)不使用参数userdata.

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( 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*)cvSeqElem( seq, i );

printf( "(%d,%d)\n", pt->x, pt->y );

}

 

cvReleaseMemStorage( &storage );

SeqSearch

查询序列中的元素

 

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

输出参数;(已查找到)元素的索引值

user_data

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

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

 

StartAppendToSeq

将数据写入序列中,并初始化该过程

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

seq

指向序列的指针

writer

writer 的状态;由该函数初始化

函数 cvStartAppendToSeq 初始化将数据写入序列这个过程。通过宏 CV_WRITE_SEQ_ELEM( written_elem, writer ),写入的元素被添加到序列尾部。注意:在写入期间,序列的其他操作可能会产生的错误的结果,甚至破怀该序列(见 cvFlushSeqWriter 的相关描述,有助于避免这些错误)

StartWriteSeq

创建新序列,并初始化写入部分(writer)

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

写入部分 writer 的状态;由该函数初始化

函数 cvStartWriteSeq 是 函数 cvCreateSeq 和函数 cvStartAppendToSeq 的组合。 指向被创建的序列的指针存放在 writer->seq 中, 通过函数cvEndWriteSeq 返回(因当在最后调用)

 

EndWriteSeq

完成写入操作

CvSeq* cvEndWriteSeq( CvSeqWriter* writer );

writer

写入部分 writer 的状态

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

FlushSeqWriter

根据写入状态,刷新序列头部

void cvFlushSeqWriter( CvSeqWriter* writer );

writer

写入部分的状态

函数 cvFlushSeqWriter 用来使用户在写入过程中每当需要时读取序列元素,比如说,核查制定的条件。函数更新序列的头部,从而使读取序列中的数据成为可能。不过,写入并没有被关闭,为的是随时都可以将数据写入序列。在有些算法中,经常需要刷新,考虑使用 cvSeqPush 代替该函数。

StartReadSeq

初始化序列中的读取过程

void cvStartReadSeq( const CvSeq* seq, CvSeqReader* reader, int reverse=0 );

seq

序列

reader

读取部分的状态;由该函数初始化

reverse

决定遍历序列的方向。如果 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()0;

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

}

...

 

cvReleaseStorage( &storage );

GetSeqReaderPos

返回当前的读取器的位置

int cvGetSeqReaderPos( CvSeqReader* reader );

reader

读取器的状态.

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

 

SetSeqReaderPos

移动读取器到指定的位置。

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

reader

reader 的状态

index

目的位置。如果使用了 positioning mode, 则实际位置为 index % reader->seq->total.

is_relative

如果不位 0, 那么索引(index) 就相对于当前的位置

函数 cvSetSeqReaderPos 将 read 的位置移动到绝对位置,或相对于当前的位置(相对位置)


原文地址:用cvSplit和cvMerge实现图像只显示单通道 作者:liuhongbin2007


 

用cvSplit和cvMerge实现图像只显示单通道

帖子 Venus » 2009-07-25 10:29

//彩色图片信道分割
int XinDaoFenGe()
{
IplImage *pImageChannel[4] = { 0, 0, 0, 0 };
IplImage *pImageColor[4] = { 0, 0, 0, 0 };
IplImage *pSrcImage = cvLoadImage( "D:\lena.jpg", 1 ) ;  
printf("nChannels %d",pSrcImage->nChannels);
IplImage *pImage = cvCreateImage(cvGetSize(pSrcImage), pSrcImage->depth, pSrcImage->nChannels);
if( pSrcImage )
{
for( int i = 0; i < pSrcImage->nChannels; i++ )
{
pImageChannel[i] = cvCreateImage( cvGetSize(pSrcImage), pSrcImage->depth, 1 );

pImageColor[i] = cvCreateImage( cvGetSize(pSrcImage), pSrcImage->depth, 3 );
}

cvSplit( pSrcImage, pImageChannel[0], pImageChannel[1],pImageChannel[2], pImageChannel[3] );

cvMerge(pImageChannel[0],NULL, NULL,NULL, pImageColor[0] );
cvMerge( NULL, pImageChannel[1],NULL,NULL, pImageColor[1] );
cvMerge( NULL, NULL,pImageChannel[2],NULL, pImageColor[2] );


}

cvNamedWindow("b",CV_WINDOW_AUTOSIZE);
cvShowImage("b",pImageColor[0]);

cvNamedWindow("g",CV_WINDOW_AUTOSIZE);
cvShowImage("g",pImageColor[1]);

cvNamedWindow("r",CV_WINDOW_AUTOSIZE);
cvShowImage("r",pImageColor[2]);

cvWaitKey(0);

cvReleaseImage(&pImageChannel[0]);
cvReleaseImage(&pImageChannel[1]);
cvReleaseImage(&pImageChannel[2]);
cvReleaseImage(&pSrcImage);

cvDestroyWindow("r");
cvDestroyWindow("g");
cvDestroyWindow("b");
return 0;
}
Venus
OpenCV初中生
 
帖子:  22
注册:  2009-07-23 16:40
页首

Re: RGB图像如何只显示单通道

帖子 a1z26 » 2010-05-26 17:59


你可能感兴趣的:(nCV之CXCORE篇 (3))