Dãy đối tƣợng liên kết

Một phần của tài liệu Thuật toán phát hiện chuyển động (Trang 62)

Một trong các đối tƣợng có thể đƣợc lƣu trữ trong không gian nhớ là một dãy. Dãy ở đây đƣợc hiểu là một danh sách liên kết với mỗi phần tử là một cấu trúc dữ liệu nào đó. Cấu trúc dãy bản thân nó cũng có các thuộc tính quan trọng mà chúng ta cần đề cập. Đầu tiên, chúng ta thƣờng đọc giá trị total để biết đƣợc số phần tử trong dãy. Thứ hai, đó là các con trỏ h_prev, h_next, v_prev, và v_next. Bốn con trỏ này là một phần của CV_TREE_NODE_FIELDS. Hai con trỏ h_prev, h_next đƣợc sử dụng độc lập để tạo nên một danh sách liên kết đơn

giản. Còn v_prev, and v_next đƣợc sử dụng để tạo nên một cấu trúc phức tạo hơn mà các nút trong dãy có mối quan hệ với nhau. Chính nhờ điều này mà chúng ta có thể biểu diễn mọi đƣờng bao phức tạp nhƣ là cây các đƣờng bao.

typedef struct CvSeq { int flags; int header_size; CvSeq* h_prev; CvSeq* h_next; CvSeq* v_prev; CvSeq* v_next int total; int elem_size; char* block_max; char* ptr; int delta_elems; CvMemStorage* storage; CvSeqBlock* free_blocks; CvSeqBlock* first; }

Giống nhƣ rất nhiều các đối tƣợng khác trong OpenCV thì dãy cũng đƣợc tạo bởi hàm khởi tạo và kết quả trả về là con trỏ trỏ tới cấu trúc dữ liệu dãy. Hàm này đƣợc gọi là cvCreateSeq().

CvSeq* cvCreateSeq( int seq_flags, int header_size, int elem_size,

CvMemStorage* storage );

Hàm này yêu cầu tham số là cờ để xác định chính xác loại dãy đƣợc tạo. Thêm vào đó, nó cũng cần xác nhận kích thƣớc của tiêu đề này, luôn đặt bằng sizeof(CvSeq)* và kích thƣớc của mỗi phần tử trong dãy elem_size. Tham số cuối cùng là không gian nhớ để tạo dãy cũng nhƣ khi tạo thêm các phần tử mới.

Sau khi sử dụng xong, hàm cvClearSeq() có thể đƣợc gọi để xóa tất cả các phần tử trong dãy. Tuy vậy, hàm này không trả lại ô nhớ đã đƣợc cấp phát trong không gian nhớ hay trả lại hệ thống. Nếu muốn thu hồi bộ nhớ vì mục đích nào đó, chúng ta có thể xóa không gian nhớ lƣu trữ dãy bằng lệnh cvClearMemStore().

Để truy cập vào mỗi phần tử dãy một cách trực tiếp, chúng ta có nhiều cách nhƣng cách tốt nhất là sử dụng hàm cvGetSeqElem().

char* cvGetSeqElem( seq, index )

Trong đó, index là phần tử trong dãy seq. Một điểm cần chú ý là do cấu trúc dãy đƣợc xây dựng để lƣu trữ nhiều cấu trúc dữ liệu khác nhau nên khi truy cập mỗi phần tử thì chúng ta cần sử dụng ép kiểu để có đƣợc đúng cấu trúc dữ liệu phần tử mong muốn. Ví dụ dƣới đây mô tả các lấy ra các dãy các điểm ảnh đƣợc lƣu trữ trong dãy:

for( int i=0; i<seq->total; ++i ) {

CvPoint* p = (CvPoint*)cvGetSeqElem ( seq, i ); printf(“(%d,%d)\n”, p->x, p->y );

}

Ta có thể dễ dàng lấy vị trí của một phần tử trong dãy bằng hàm: int cvSeqElemIdx(

const CvSeq* seq, const void* element,

CvSeqBlock** block = NULL );

Nếu tham số thứ 3 khác NULL thì sau khi thực hiện việc tìm kiếm xong, hàm sẽ gán giá trị con trỏ tới giá trị tìm đƣợc.

Có thể tạo một bản sao của một dãy dễ dàng bằng lời gọi: CvSeq* cvCloneSeq(

const CvSeq* seq,

CvMemStorage* storage = NULL )

Nhƣ đã đề cập ở phần trên, một dãy trong OpenCV là một danh sách liên kết. Ta có thể sử dụng dãy nhƣ là một ngăn xếp hay hàng đợi trong nhiều trƣờng hợp cần thiết với việc sử dụng các hàm sau đây:

char* cvSeqPush( CvSeq* seq,

void* element = NULL );

char* cvSeqPushFront( CvSeq* seq,

void* element = NULL );

void cvSeqPop( CvSeq* seq,

void* element = NULL );

void cvSeqPopFront( CvSeq* seq,

void* element = NULL );

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

Chỉ với 4 phƣơng thức truy cập dữ liệu cvSeqPush(), cvSeqPushFront(), cvSeqPop(), và cvSeqPopFront(), chúng ta đã có thể xây dựng một cấu trúc ngăn xếp hoặc hàng đợi đơn giản. Hàm cvSeqPushMulti() và cvSeqPopMulti() hơi khác một chút là cho phép đƣa vào danh sách hoặc lấy ra nhiều phần tử cùng một lúc. Cả hai hàm này đều có một tham số để phân biệt là sẽ lấy phần tử ở đầu hay cuối danh sách là in_front bằng CV_FRONT (1) hay CV_BACK (0).

Với mục đích chỉ muốn sử dụng dãy nhƣ là một danh sách đơn giản hoặc cần có một thuật toán truy cập phức tạp hơn, ta có thể thực hiện thêm hoặc xóa phần tử một cách trực tiếp thông qua lời gọi hàm:

char* cvSeqInsert( CvSeq* seq, int before_index,

void* element = NULL );

void cvSeqRemove( CvSeq* seq,

int index );

Một phần của tài liệu Thuật toán phát hiện chuyển động (Trang 62)