Phƣơng pháp sử dụng các mẫu trong theo dõi chuyển động đã đƣợc phát triển trong phòng thí nghiệm MIT Media bởi Bobick và Davis. Đây là một phƣơng pháp hiệu quả để theo dõi các chuyển động thông thƣờng và thƣờng đƣợc sử dụng trong các ứng dụng nhận dạng chuyển động, cử chỉ. Phƣơng pháp này yêu cầu một một vệt ảnh tạo ra bởi đối tƣợng để lấy thông tin về chuyển động của nó. Vệt ảnh này có thể thực hiện bằng một số cách nhƣ sau:
Sử dụng cách máy quay tĩnh đặc ở vị trí thích hợp sau đó lấy sai khác giữa các khung hình. Cách này sẽ đem lại cho chúng ta cạnh của các đối tƣợng chuyển động.
Sử dụng đặc tính sắc độ. Ví dụ: nếu chúng ta biết màu nền là xanh lá cây sáng thì việc lấy vật thể chỉ đơn giản là tất cả các phần khác màu nền đó. Sử dụng các kĩ thuật tạo vệt tích cực. Ví dụ tạo ra một bức tƣờng đƣợc
chiếu ánh sáng hồng ngoại. Khi đó mọi vật thể chuyển động qua đều là vệt.
Sử dụng mô hình nền: Đây cũng chính là phƣơng pháp đã đƣợc đề cập ở phần 1 và đƣợc ứng dụng trong xây dựng ứng dụng thuật toán liên quan ở phần 3.
Giả sử sau quá trình tách nền chúng ta đã có một đối tƣợng cần theo dõi đƣợc biểu diễn bằng khối hình chữ nhật trắng. Chúng ta sẽ sử dụng màu trắng để mô tả tất cả các điểm ảnh nhận giá trị thực của đối tƣợng tại hiện tại. Khi hình chữ nhật chuyển động, một vệt mới đƣợc ghi nhận tại vị trí mới. Hình chữ nhật cũ sẽ đƣợc biểu diễn bằng hình chữ nhật tối hơn. Nhƣ vậy sau một thời gian quan sát ta sẽ có một chuỗi các vệt ghi lại chuyển động của vật thể đƣợc hiểu nhƣ là ảnh lịch sử chuyển động của nó.
Hình 12. Quá trình cập nhật vệt theo thời gian.
Mỗi một vệt sẽ có một nhãn thời gian tƣơng ứng là chính là thời điểm mà nó xuất hiện. Các vệt có nhãn thời gian cũ hơn duration giá trị so với nhãn hiện tại sẽ bị loại bỏ, thiết lập bằng 0. Việc tạo ra mẫu chuyển động đƣợc gọi bằng hàm cvUpdateMotionHistory():
void cvUpdateMotionHistory( const CvArr* silhouette, CvArr* mhi,
double timestamp, double duration );
Hình 13. Các vệt mẫu chuyển động của 2 đối tƣợng.
Trong cvUpdateMotionHistory(), tất cả mảng ảnh điều là các ảnh đơn kênh – một màu. Ảnh vệt là ảnh kiểu byte trong đó các giá trị khác 0 biểu diễn giá trị gần nhất của vệt đƣợc tách của đối tƣợng. Ảnh mhi là một ảnh có giá trị thực biểu diễn mẫu chuyển động hay ảnh lịch sử của chuyển động. Giá trị
timestamp là thời gian hệ thống hiện tại – thƣờng đếm theo mili-giây. duration
nhƣ đã nói ở phần trên nhận giá trị là khoảng thời gian mà các điểm ảnh đƣợc giữ lại trong mhi.
Mỗi mẫu chuyển động có một tập các vệt của đối tƣợng chồng lên nhau theo thời gian. Chúng ta có thể dễ dàng nhận thấy chuyển động bằng cách tạo ra độ chuyển màu ảnh mhi theo giá trị nhãn thời gian đã nói ở phần trƣớc. Để làm việc này thƣờng sử dụng hàm tạo độ chuyển Scharr và Sobel. Sẽ có thể có một số thành phần sẽ lớn và không hợp lệ. Các thành phần có độ chuyển không hợp lệ khi phần cũ hơn hoặc không tích cực của ảnh mhi đƣợc thiết lập bằng 0, sẽ tạo ra độ chuyển lớn giả tạo quanh viền ngoài của các vệt. Bởi vì chúng ta biết khoảng thời gian tồn tại duration nên có thể xác định đƣợc độ lớn của độ chuyển cần thiết – đặc trƣng bởi giá trị vi phân dx, dy của bƣớc chuyển. Chính vì vậy, chúng ta có thể sử dụng biên độ chuyển để loại bỏ các thành phần quá lớp.
Ta có thể thực hiện việc xác định độ chuyển của chuyển động bằng cách gọi hàm:
void cvCalcMotionGradient( const CvArr* mhi, CvArr* mask, CvArr* orientation, double delta1, double delta2, int aperture_size=3 );
Hình 14. Tính độ chuyển vết của chuyển động.
Trong hàm này, tất cả các mảng ảnh đều là đơn kênh. Tham số mhi là ảnh lịch sử chuyển động mang các giá trị thực. Các biến delta1, delta2 là biên độ cực tiểu, cực đại của độ chuyển cho phép. Thƣờng thì giá trị delta1 đƣợc thiết lập bằng 0,5 và 1,5 lần giá trị trung bình xác định trong cvUpdateMotionHistory(). Giá trị aperture_size mang giá trị kích thƣớc chiều rộng, chiều cao của toán tử tạo độ chuyển. Các giá trị này có thể là:
aperture_size = -1: CV_SCHARR kích thƣớc 3x3 aperture_size = 3: ứng với bộ lọc Sobel 3x3 aperture_size = 5: ứng với bộ lọc Sobel 5x5 aperture_size = 7: ứng với bộ lọc Sobel 7x7
Hàm trả về: mask là ảnh 8 bit đơn kênh với các giá trị khác 0 là các độ chuyển hợp lệ đƣợc tìm thấy, orientation là ảnh với các giá trị thực chứa góc của hƣớng chuyển tại mỗi điểm ảnh.
Hàm cvCalcGlobalOrientation() tìm hƣớng toàn cục của chuyển động nhƣ là véc-tơ tổng của tất cả các hƣớng độ chuyển hợp lệ.
double cvCalcGlobalOrientation( const CvArr* orientation, const CvArr* mask, const CvArr* mhi, double timestamp, double duration );
Khi sử dụng cvCalcGlobalOrientation(), chúng ta truyền vào ảnh
orientation và mask đƣợc tính ở hàm cvCalcMotionGradient() cùng với
timestamp, duration và ảnh mhi xác định từ lời gọi hàm
cvUpdateMotionHistory(). Giá trị nhãn thời gian timestamp cùng với duration
cho phép hàm xác định đƣợc giá trị chuyển động từ mhi và orientation.
Chúng ta cũng có thể tác các vùng trong ảnh mẫu chuyển động mhi và xác định chuyển động cục bộ trong các vùng của ảnh. Trong hình dƣới đây, ảnh mhi
đƣợc quét từ vùng của vệt hiện tại. Khi một vùng đƣợc đánh dấu với nhãn thời gian gần nhất đƣợc tìm thấy thì sẽ xác định đƣợc chu vi của vùng đó để tính chuyển động trong đó.
Hình 15. Tách các chuyển động cục bộ trong ảnh.
Hàm cho phép tác và tính toán các chuyển động cục bộ là cvSegmentMotion():
CvSeq* cvSegmentMotion( const CvArr* mhi, CvArr* seg_mask,
CvMemStorage* storage, double timestamp,
double seg_thresh );
Trong hàm cvSegmentMotion(), mhi vẫn là ảnh mhi đƣợc xác định từ các bƣớc trƣớc. Chủ ta cũng truyền vào storage là cấu trúc CvMemoryStorage đã đƣợc nói ở phần quản lý bộ nhớ trong OpenCV. Tham số timestamp là nhãn thời gian của vệt hiện tại trong mhi mà chúng ta muốn tác chuyển động cục bộ. Cuối cùng là seg_thresh là số bƣớc lùi cực đại để chấp nhận trong chuyển động cần tách. Thông thƣờng seg_thresh bằng 1,5 lần sai khác trung bình nhãn thời của vệt. Hàm trả lại cấu trúc CvSeq của CvConnectedComp biểu diễn mỗi chuyển động tìm đƣợc. Nó cũng trả về seg_mask là ảnh với giá trị thực đơn kênh với các vùng đƣợc đánh dấu là các số khác không duy nhất. Để tính các chuyển động
cho vùng hiện tại thì mỗi lần chúng ta gọi cvCalcGlobalOrientation() sử dụng vùng mặt nạ thích hợp từ CvConnectedComp thích hợp hoặc từ các giá trị đơn trong seg_mask.
Chƣơng 3. CÀI ĐẶT MÔ HÌNH NỀN SỬ DỤNG TỪ ĐIỂN HAI LỚP 3.1 Cấu trúc dữ liệu
Trƣớc khi đi vào việc xây dựng chƣơng trình thực hiện thuật toán mô hình hóa nền sử dụng từ điển thì ta định nghĩa các cấu trúc dữ liệu cho từ mã và từ điển. Căn cứ vào cách thức truy nhập và sử dụng của từ điển mà chúng ta đề xuất hình thức lƣu trữ từ điển nhƣ sau:
Hình 16. Tổ chức dữ liệu lƣu trữ từ điển.
Nhƣ đã mô tả ở phần trƣớc thì mỗi một điểm ảnh sẽ có một từ điển đặc trƣng, mô hình hóa cho nó. Để cho đơn giản chúng ta sẽ sử dụng một ma trận các con trỏ có kích thƣớc bằng chính kích thƣớc của ảnh – mỗi một điểm ảnh tƣơng ứng sẽ có một từ điển. Mỗi phần tử của ma trận là con trỏ trỏ tới danh sách liên kết – chính là từ điển - với mỗi phần tử từ mã.
Trƣớc khi đi vào khai báo cụ thể của từng cấu trúc dữ liệu thì chúng ta có một số quy định khi đặt giá trị tên biến để cho tiện theo dõi nhƣ sau:
Một hoặc hai kí tự đầu tiên viết chữ thƣờng mô tả kiểu dữ liệu trong ngôn ngữ lập trình hoặc do ngƣời dùng tự định nghĩa.
Tiếp đó là thông tin dữ liệu liên quan tới khái niệm theo thời gian dài (LT) hoặc thời gian ngắn(ST) – nếu có. Vì trong suốt quá trình làm việc chúng ta luôn đề cập song song tới hai mô hình xét trong thời gian ngắn H và mô hình xét trong thời gian dài M với rất nhiều các thuộc tính giống nhau nên sẽ sử dụng kí hiệu này để phân biệt.
Tên sau dấu gạch dƣới sẽ là tên mô tả hoạt động về mặt logic thuật toán của trƣờng đó.
Từ đó chúng ta sẽ đi quy định cấu trúc dữ liệu của mô hình nhƣ là tổng hợp của cả mô hình H và M đồng thời nhƣ sau:
typedef struct BGCodeBookModel { CvSize size; int iLT_T; int i_Tadd; CvMemStorage* stLT_storage; CvMemStorage* stST_storage; int iLT_maxMNRL; BGCodeBookElem** cbLT_map; BGCodeBookElem* cwLT_list; int iST_maxMNRL; BGCodeBookElem** cbST_map; BGCodeBookElem* cwST_list; }BGCodeBookModel;
Trong đó, size là kích thƣớc của mô hình, giá trị này bằng chính kích thƣớc của ảnh cần mô hình hóa – với lý do đã trình bày ở phần trên. Trƣờng
iLT_T lƣu nhãn thời gian hiện tại của mô hình. Trƣờng i_Tadd là khoảng thời gian Tadd – khoảng thời gian tồn tại của từ mã trong mô hình H sẽ đƣợc chuyển sang mô hình M. Trƣờng stLT_storage là không gian bộ nhớ cấp phát bởi OpenCV để lƣu trữ cấu trúc và thông tin từ điển của mô hình M – sử dụng cấp phát chung cho toàn bộ các từ điển của các điểm ảnh. Trƣờng iLT_maxMNRL
lƣu giá trị max. Trƣờng cbLT_map là con trỏ trỏ tới từ mã đầu tiên của từ điển ứng với điểm ảnh xét còn cwLT_list là con trỏ trỏ tới từ mã tiếp theo chƣa đƣợc sử dụng có thể đƣợc sử dụng để tạo ra từ mã mới. Tƣơng tự nhƣ vậy với
stST_storage, iST_maxMNRL, cbST_map, cwST_list.
Chúng ta định nghĩa tƣờng minh cấu trúc dữ liệu từ mã của từ điển nhƣ sau:
typedef struct BGCodeBookElem{
struct BGCodeBookElem* next;
float fBriMax; int iFreq; int iMNRL; int iFirstAccess; int iLastAccess; }BGCodeBookElem;
Vì lý do tổ chức lƣu trữ từ điển dƣới dạng danh sách liên kết nhƣ trình bày ở phần trên nên ngoài các trƣờng cần thiết cho từ mã, chúng ta còn có thêm trƣờng con trỏ next trỏ tới phần tử tiếp theo – từ mã tiếp theo trong từ điển. Nếu là từ mã cuối cùng trong từ điển thì con trỏ này sẽ mang giá trị NULL.
3.2 Giải thuật thực hiện mô hình hóa nền sử dụng từ điển hai lớp thích nghi nghi
Nhƣ đã đề cập trong chƣơng 1 khi giới thiệu về mô hình nền sử dụng từ điển hai lớp thích nghi thì chúng ta sẽ chia công việc mô hình hóa nền thành 3 pha thực hiện khác nhau:
Pha khởi tạo mô hình nền M sử dụng chuỗi ảnh huấn luyện (đào tạo). Pha tối ƣu mô hình nền M đã xây dựng: thực hiện sau khi đã hoàn thành
xong việc học chuỗi ảnh huấn luyện.
Pha tách nền, cập nhật mô hình nền H và M đồng thời.
Các pha này đƣợc định nghĩa thông qua nguyên mẫu hàm nhƣ sau: void BGCodeBookUpdate(
BGCodeBookModel* model, const CvArr* image);
void BGCodeBookUpdateFinish( BGCodeBookModel* model );
void BGCodeBookDiff(
BGCodeBookModel* model,
const CvArr* image, CvArr* fgmask );
Đầu tiên, với mỗi ảnh trong chuỗi ảnh huấn luyện ta sẽ tiến hành cập nhật mô hình M theo thuật toán đã đƣợc trình bày. Có một lƣu ý nhỏ là ảnh trong trong thuật toán này là ảnh mức xám.
Tiếp đó, lời gọi hàm BGCodeBookUpdateFinish() sẽ thực hiện loại bỏ các thành phần ứng với thông tin vật thể, những thông tin không đƣợc mô hình hóa trong mô hình nền cần hƣớng tới thông qua giá trị max hay iLT_maxMNRL nhƣ trong khai báo của mô hình M. Giá trị iLT_maxMNRL thƣờng đƣợc chọn bằng một nửa chiều dài chuỗi huấn luyện theo đề xuất của Kim. Sau bƣớc này, chúng ta sẽ thu đƣợc mô hình M là mô hình nền cần tìm ứng với chuỗi huấn luyện đã sử dụng. Mô hình này đƣợc đề cập tới nhƣ là mô hình nền vĩnh cửu.
Cuối cùng, hàm BGCodeBookDiff() mô tả công việc chính sau toàn bộ các bƣớc chuẩn bị. Đó là từ ảnh hiện tại, chúng ta tiến hành tách nền để phát hiện ra vật thể chuyển động trong nó. Để mô hình nền đã có có thể thích nghi với các thay đổi về độ sáng, về thành phần của nền thì chúng ta thiết lập mô hình nền tạm H và liên tục tiến hành cập nhật lại giá trị cho cả 2 mô hình đã có – mô hình H và mô hình M – thông qua việc tạo mới, di chuyển và xóa các từ mã phù hợp theo từng yêu cầu nhất định.
3.2.1 Pha khởi tạo mô hình nền M
Thuật toán của pha này nhƣ sau:
1. Kiểm tra mô hình M đã đƣợc khởi tạo chƣa? a. Đã đƣợc khởi tạo thì chuyển sang bƣớc 2. b. Nếu chƣa đƣợc khơi tại thì tiến hành khởi tạo
i. Khởi tạo không gian nhớ stLT_storage và stLT_storage. Kích thƣớc của không gian nhớ phụ thuộc vào đặc tính của nguồn cần mã hóa.
[!] Theo gợi ý của Kim thì số từ mã trung bình trong mỗi từ điển - ứng với mỗi điểm ảnh cỡ 6,5 từ mã. Từ đó ta có thể tính ra đƣợc không gian nhớ cần cấp phát cho hợp lý. ii. Tạo cấu trúc danh sách liên kết cwLT_list và cwST_list từ
không gian nhớ tƣơng ứng vừa cấp phát. Việc này giúp hạn chế các tính toán trong việc tạo ra danh sách liên kết sau này.
iii. Thiết lập các tham số của mô hình nhƣ TH, Tdelete và Tadd
tƣơng ứng với các biến iST_maxMNRL, iLT_maxMNRL
và i_Tadd.
2. Với mỗi điểm ảnh ta tiến hành: Ta đi tìm từ mã trong mô hình M tƣơng ứng?
a. Nếu tìm thấy thì cập nhật giá trị cho từ mã đó.
b. Nếu không thì tạo mới từ mã bằng cách thêm vào đầu từ điển với từ mã có giá trị tƣơng ứng với giá trị điểm ảnh đang xét. Việc này thực hiện nhờ các điều chỉnh giá trị cwlLT_list và
cbLT_map.
3.2.2 Pha tối ƣu mô hình nền M đã xây dựng
Thuật toán của pha này khá đơn giản đƣợc mô tả nhƣ sau:
1. Cập nhật lại giá trị của toàn bộ các từ mã trong mô hình M theo công thức:
i = max{i , N-qi+pi-1}
Nhƣ đã đề cập ở phần trƣớc, N là số ảnh trong chuỗi huấn luyện. q và q là thời điểm lần đầu tiên và cuối cùng truy nhập vào từ mã.
2. Loại bỏ các từ mã có lớn hơn N/2.
3.2.3 Pha tách nền, cập nhât mô hình M và H đồng thời
Với mỗi điểm ảnh ta tiến hành các bƣớc nhƣ sau: 1. Tìm từ mã trong mô hình M?
a. Nếu thấy thì cập nhật từ mã tƣơng ứng và điểm ảnh đó là một điểm ảnh thuộc nền vĩnh cửu.
b. Nếu không thì đi tìm trong mô hình H?
i. Nếu thấy từ mã tƣơng ứng trong mô hình H thì đây là nền tạm thời. Tiến hành cập nhật giá trị cho từ mã tìm thấy. ii. Nếu không thấy từ mã thì tạo mới trong mô hình H.
2. Tối ƣu mô hình nền tạm H.
3. Di chuyển các từ mã thích hợp từ mô hình H sang mô hình M. 4. Tối ƣu mô hình nền M.
3.3 Phân tích thông tin chuyển động
Việc phân tích chuyển động về cơ bản dựa trên những tích lũy về lịch sử các chuyển động trƣớc đó của đối tƣợng. Ảnh sau khi đƣợc thực hiện tách nền ở phần trƣớc đƣợc xử lý tiếp ở phần này là ảnh nhị phân với giá trị khác 0 đại diện cho các điểm ảnh của vật thể và ngƣợc lại là của nền. Các bƣớc xử lý cụ thể đƣợc mô tả nhƣ sau:
1. Cập nhật chuyển động của vật thể theo nhãn thời gian hiện tại.
2. Tính giá trị độ chuyển để tạo ra vệt lƣu vết của chuyển động từ thời