Trước khi theo vết các chuyển động, hệ thống cần phải trích xuất ra đối tượng. Kỹ thuật phổ biển để nhận được đối tượng từảnh 2D là trừ nền. Lý do để sử dụng việc trừ nền đó là chúng hoạt động tốt trong trường hợp ảnh nền là tĩnh trong một thời gian dài. Mà trong thực tế, khi ta lắp camera tại vị trí cố định để giám sát giao thông thì ảnh nền thu nhận được là tĩnh trong một thời gian dài.
OpenCV cung cấp nhiều kỹ thuật khác nhau để loại bỏ nền tĩnh từ khung ảnh thu nhận được. Một trong các kỹ thuật đó là mô hình nền MoG. Hàm MoG cung cấp trong thư viện OpenCV đã được cập nhật và sửa đổi một vài lần. Trong phiên bản trước của OpenCV (phiên bản 2.1, cập nhật tháng 4/2010), hàm MoG được viết lại để tăng độc chính xác và hiệu năng. Hàm MoG của thư viện OpenCV được dựa trên một phiên bản cải tiến của thuật toán MoG (KaewTraKulPong and Bowden, 2001). Mặc dù từ các phiên bản 2.2 trở đi của OpenCV, việc phát hiện bóng được đề xuất trong bài báo này không được thực thi nữa, nhưng nó không ảnh hưởng đến kết quả nhiều vì bóng cũng có thể được xem xét như là một phần của đối tượng.
Để thực hiện hàm này, trong thư viện OpenCV ta gọi hàm
cvCreateGaussianBGModel. Hàm cvCreateGaussianBGModel yêu cầu tối thiểu một tham số, đó là frame thu nhận được tại thời điểm hiện tại từ camera hoặc từ chuỗi hình ảnh (video).
cvCreateGaussianBGModel ( IplImage* frame,
41
);
(a) (b)
Hình 3.1: Ảnh tách nền riêng (a) và đối tượng chuyển động riêng (b)
3.1.2. Tạo chu tuyến và đánh dấu đối tượng
Sau khi đã trừ nền thành công để trích ra ảnh nền, ta sẽ vẽ chúng dưới dạng ảnh nhị phân. Việc tiếp theo cần làm là đánh dấu các đối tượng. Tạo chu tuyến là việc xửlý đểđánh dấu đường viền các cạnh của đối tượng, làm cho chúng dễ được nhận biết hơn. Trong lĩnh vực thị giác máy tính, biên của các đối tượng thường được định nghĩa là vùng các điểm ảnh mà tương phản với vùng lân cận hoặc vùng chuyển động của nó.
Hàm để tìm và tạo chu tuyến trong OpenCV rất hữu dụng, nó cung cấp các thông tin bổ sung của đối tượng như kích thước, điểm trọng tâm, các điểm có thể được sử dụng để vẽ một hình hộp bao quanh đối tượng. Điểm trọng tâm của đối tượng rất hữu ích cho bước xử lý tiếp theo đó là theo vết đối tượng, nó hoạt động như một điểm đặc trưng tốt (good feature point) cho đối tượng.
Hàm tạo chu tuyến trong OpenCV sử dụng cấu trúc dữ liệu tương tự như các danh sách liên kết lưu trữ các nút chu tuyến, trong đó các nút chu tuyến này thường là góc hoặc chỗ lượn của đối tượng. Trước khi các chu tuyến được vẽ, hàm cvFindContours cần được thực hiện để lấy được danh sách các nút chu tuyến:
int cvFindContours( IplImage* img,
CvMemStorage* storage, CvSeq** firstContour,
42
int headerSize = sizeof(CvContour),
CvContourRetrievalMode mode, = CV_RETR_LIST,
CvChainApproxMethod method = CV_CHAIN_APPROX_SIMPLE );
Sau khi lấy được danh sách các nút chu tuyến, ta tiến hành vẽ chu tuyến với việc sử dụng hàm: void cvDrawContours ( CvArr *img, CvSeq* contour, CvScalar external_color, CvScalar hole_color, int max_level,
int thickness CV_DEFAULT(1), int line_type CV_DEFAULT(8),
CvPoint offset CV_DEFAULT(cvPoint(0,0)) );
Xác định một hình hộp bao quanh đối tượng sử dụng hàm: CvRect cvBoundingRect(
CvArr* points,
int update CV_DEFAULT(0) );
Sau đó, ta vẽ một hình chữ nhật bao quanh đểđánh dấu đối tượng với hàm: void cvRectangle(
CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color,
int thickness CV_DEFAULT(1), int line_type CV_DEFAULT(8),
43
int shift CV_DEFAULT(0) );
Kết quả khi chạy chương trình:
Hình 3.2: Kết quả tạo chu tuyến và đánh dấu đối tượng
3.2. Theo vết chuyển động
3.2.1. Sử dụng thuật toán Lucas - Kanade (LK)
Việc trừ nền bản thân nó không thực hiện bất cứ phép toán theo vết nào trên đối tượng được trích ra. Các thuật toán luồng quang học được sử dụng để theo vết đối tượng như đã trình bày ởcác chương trước.
LK là phương pháp luồng quang học thưa, yêu cầu một tập các đặc trưng đầu vào trước khi thực hiện bất kỳ phép toán nào. Thuật toán này không thực hiện bất kỳ việc phát hiện đặc trưng nào. OpenCV cung cấp hàm bổ sung
cvGoodFeaturesToTrack để giúp người dùng tựđộng phát hiện đặc trưng thay bằng việc họ phải xây dựng chúng bằng tay. Tuy nhiên, các đặc trưng được tính toán từ cvGoodFeaturesToTrack có thể không biểu diễn đối tượng mà nó chỉ lấy các điểm ảnh có đặc điểm tốt cho việc xác định và theo vết đối tượng.
void cvGoodFeaturesToTrack( const CvArr* image, CvArr* eig_image, CvArr* temp_image, CvPoint2D32f* corners,
44
double min_distance,
const CvArr* mask CV_DEFAULT(NULL), int block_size CV_DEFAULT(3),
int use_harris CV_DEFAULT(0), double k CV_DEFAULT(0.04) );
Kết quảkhi tìm các đặc trưng sử dụng cvGoodFeaturesToTrack:
Hình 3.3: Các đặc trưng tìm được sử dụng hàm cvGoodFeaturesToTrack
OpenCV cung cấp hai hàm LK optical flow đó là cvCalcOpticalFlowLK và cvCalcOpticalFlowPyrLK. Sự khác nhau giữa chúng đó là hàm sau sử dụng thêm tháp hình ảnh để cải thiện chất lượng kết quả(đây cũng là hàm được sử dụng để cài đặt chương trình mô phỏng trong luận văn). Tháp hình ảnh là tập hợp các hình ảnh mà các ảnh bên trong tháp được tạo ra từ một ảnh gốc đơn:
45
void cvCalcOpticalFlowPyrLK( const CvArr* imgA, const CvArr* imgB, CvArr* pyrA, CvArr* pyrA,
const CvPoint2D32f* featuresA, CvPoint2D32f* featuresA, int count, CvSize win_size, int level, char* status, float* track_error, CvTermCriteria criteria, int flags ); trong đó:
- imgA và imgB là khung hình ở thời điển hiện tại (framen) và khung hình trước đó (framen-1).
- pyrA và pyrB là tháp hình ảnh tương ứng của imgA và imgB. Chúng có thể nhận giá trị NULL và hệ thống sẽ tựđộng xác định chúng, tuy nhiên điều này có thể gây rủi ro và hiệu năng không cao.
- featuresA là một mảng dữ liệu kiểu CvPoint2D32f, là cấu trúc sử dụng để lưu trữ điểm ảnh trong OpenCV. featuresA là một mảng điểm được tìm thấy trong imgA và sẽ cố gắng để tìm và theo vết chúng trong imgB, tương ứng với featuresB.
- count là sốcác đặc trưng được lưu trữ trong featuresA.
- winSize là kích thước cửa sổ cục bộ có thể thay đổi nếu người dùng muốn. Nếu winSize lớn hơn thì chuyển động lớn hơn có thểđược theo vết.
- Level: Số mức pyramid lớn nhất. Nếu bằng 0 , các pyramid không được dùng (single level), nếu bằng 1 , có 2 mức được sử dụng...
- Status: có cấu trúc mảng chứa tất cả các phần tử của mảng được đặt là 1 nếu như việc thực hiện optical flow cho các điểm là được tìm thấy, 0 với trường hợp còn lại.
46
- Criteria: Chỉ định khi quá trình xử lý lặp đi lặp lại của việc tìm kiếm flow cho mỗi điểm trong mỗi mức pyramid được dừng lại.
Kết quả đạt được khi vẽcác véc tơ chuyển động tìm kiếm được từ thuật toán trên:
Hình 3.5: Kết quả tìm véc tơ chuyển động theocvCalcOpticalFlowPyrLK
3.2.2. Sử dụng thuật toán Horn và Schunck
OpenCV cũng cung cấp một hàm cvCalcOpticalFlowHS để thực hiện tính các trường chuyển động theo thuật toán Horn and Schunck như sau:
void cvCalcOpticalFlowHS( const CvArr* prev, const CvArr* curr,
int use_previous, CvArr* velx, CvArr* vely, doublelambda, CvTermCriteriacriteria );
trong đó:
- prev lưu khung ảnh trước, curr lưu khung ảnh hiện tại, dưới định dạng 8-bit và single channel.
- use_previous: cờ để xác định xem có sử dụng vận tốc đầu vào như là khởi tạo xấp xỉ hay không.
- velx: thành phần luồng quang optical flow theo phương nằm ngang với kích thước bằng kích thước các ảnh đầu vào, kiểu 32 bit floating-point và single channel.
47
- vely: thành phần luồng quang optical flow theo phương thẳng đứng với kích thước bằng kích thước các ảnh đầu vào, kiểu 32 bit floating-point và single channel.
- lambda: trọng số làm trơn. Giá trị này lớn thể hiện luồng quang nhận được trơn hơn.
- criteria: tiêu chí kết thúc việc tính vận tốc.
Ta thấy rằng không giống như hàm cvCalcOpticalFlowPyrLK có tham số số lượng đặc trưng cần tính, hàm cvCalcOpticalFlowHS sẽ tính luồng cho mọi điểm của ảnh đầu vào, do đó thuật toán sẽ xửlý lâu hơn và hiện tại không còn được sử dụng trong thực tế. Để theo vết tất cả các điểm ảnh, người ta sử dụng thuật toán Gunna Farneback với hàm OpenCV tương ứng dưới đây.
3.2.3. Sử dụng thuật toán Gunna Farneback
OpenCV cung cấp hàm cvCalcOpticalFlowFarneback để tính luồng quang cho tất cảcác điểm ảnh trong ảnh theo thuật toán Gunnar Farneback với độ hiệu quả cao hơn hẳn so với cvCalcOpticalFlowHS ở trên.
voidcvCalcOpticalFlowFarneback( const CvArr* prev, const CvArr* next,
CvArr* flow, double pyr_scale, int levels,
int winsize, int iterations, int poly_n, double poly_sigma, int flags
);
trong đó:
- prev: ảnh đầu vào của khung hình trước với cấu hình 8-bit, single channel. - next: ảnh khung hình hiện tại với cấu hình 8-bit, single channel.
- flow: ảnh luồng tính toán được có kích thước bằng kích thước của các ảnh prev và next, có kiểu là CV_32FC2.
- pyr_scale: tham số để xây dựng các tháp cho mỗi ảnh; pyr_scale = 0.5 là tháp cổđiển, trong đó mỗi tầng kế tiếp là nhỏhơn hai lần so với tầng trước đó.
- levels: số tầng của tháp bao gồm cảảnh khởi tạo; levels = 1 tức là không tạo thêm các tầng mà chỉ các ảnh gốc được sử dụng.
48
mạnh của thuật toán tới nhiễu ảnh và tăng cơ hội tìm được các chuyển động nhanh nhưng làm nhòe trường chuyển động hơn.
- iterations: số lần lặp của thuật toán tại mỗi mức tháp.
- poly_n: kích thước vùng điểm ảnh lân cận được sử dụng để tìm đa thức mở rộng trong mỗi điểm ảnh. Giá trị này lớn tức là ảnh sẽ được xấp xỉ với các bề mặt trơn hơn, do đó thuật toán hiệu quả hơn và làm nhòe trường chuyển động hơn. Thông thường poly_n nhận các giá trị 5 hoặc 7.
- poly_sigma: độ lệch chuẩn của Gaussian được sử dụng để làm trơn các đạo hàm sử dụng như cơ sở cho việc mở rộng đa thức; với poly_n = 5, ta có thể thiết lập poly_sigma = 1.1; với poly_n = 7 ta có thể thiết lập poly_sigma = 1.5 là tốt nhất.
- flags: toán tử cờ, có thể nhận các giá trị OPTFLOW_USE_INITIAL_FLOW hoặc OPTFLOW_FARNEBACK_GAUSSIAN.
3.2.4. Sử dụng thuật toán so khớp vùng
OpenCV cung cấp hàm cvCalcOpticalFlowBM sử dụng thuật toán so khớp vùng để tính luồng quang giữa hai ảnh như sau:
void cvCalcOpticalFlowBM( const CvArr* prev, const CvArr* curr, CvSize block_size, CvSize shift_size,
CvSize max_range, int use_previous, CvArr* velx, CvArr* vely
);
trong đó:
- prev: ảnh đầu tiên, 8-bit và single channel. - curr: ảnh thứ hai, 8-bit và single channel.
- block_size: kích thước của các khối (vùng) được so sánh. - shift_size: mức độtăng tọa độ khối.
- max_range: kích thước vùng lân cận được quét trong các điểm ảnh xung quanh khối.
- use_previous: cờ báo hiệu việc có sử dụng vận tốc đầu vào làm khởi tạo xấp xỉ hay không.
49
- velx: thành phần theo phương nằm ngang của luồng quang với kích thước:
�𝑝𝑝𝑝𝑝𝑝𝑝𝑢𝑢→𝑤𝑤𝑢𝑢𝑤𝑤𝑡𝑡ℎ−𝑏𝑏𝑏𝑏𝑏𝑏𝑐𝑐𝑘𝑘𝑠𝑠ℎ𝑢𝑢𝑖𝑖𝑡𝑡_𝑠𝑠𝑢𝑢𝑠𝑠𝑝𝑝.𝑤𝑤𝑢𝑢𝑤𝑤𝑡𝑡ℎ_𝑠𝑠𝑢𝑢𝑠𝑠𝑝𝑝.𝑤𝑤𝑢𝑢𝑤𝑤𝑡𝑡ℎ�× [𝑝𝑝𝑝𝑝𝑝𝑝𝑢𝑢→ℎ𝑝𝑝𝑢𝑢𝑛𝑛ℎ𝑡𝑡−𝑏𝑏𝑏𝑏𝑏𝑏𝑐𝑐𝑘𝑘𝑠𝑠ℎ𝑢𝑢𝑖𝑖𝑡𝑡_𝑠𝑠𝑢𝑢𝑠𝑠𝑝𝑝.ℎ𝑝𝑝𝑢𝑢𝑛𝑛ℎ𝑡𝑡_𝑠𝑠𝑢𝑢𝑠𝑠𝑝𝑝.ℎ𝑝𝑝𝑢𝑢𝑛𝑛ℎ𝑡𝑡] , 32bit floating point, single channel.
- vely: thành phần theo phương thẳng đứng của luồng quang với cùng kích thước với velx, 32-bit floating point, single channel.
Hàm này tính luồng quang của các block chồng lấn kích thước
𝑏𝑏𝑏𝑏𝑏𝑏𝑐𝑐𝑘𝑘_𝑠𝑠𝑖𝑖𝑧𝑧𝑒𝑒.𝑤𝑤𝑖𝑖𝑑𝑑𝑡𝑡ℎ×𝑏𝑏𝑏𝑏𝑏𝑏𝑐𝑐𝑘𝑘_𝑠𝑠𝑖𝑖𝑧𝑧𝑒𝑒.ℎ𝑒𝑒𝑖𝑖𝑔𝑔ℎ𝑡𝑡, bởi vậy các trường vận tốc sẽ bị nhỏhơn so với các ảnh gốc. Với mỗi block trong ảnh prev, các hàm cố gắng tìm một block tương tự trong ảnh curr trong số các vùng lân cận của block gốc hoặc các block dịch lên (velx(x0,y0), vely(x0,y0)).
3.3. Xác định vận tốc chuyển động của phương tiện
Khi áp dụng các hàm tính luồng quang ở trên, ta sẽthu được luồng quang của các đặc trưng đã chọn hoặc toàn bộ các điểm ảnh (tùy thuộc vào từng thuật toán được sử dụng). Luồng quang này sẽ lưu các véc tơ chuyển động cho các điểm ảnh. Dựa trên công thức đã thiết lập ở mục 2.5 (chương 2) và mục 1.2 (chương 1), ta sẽ tính được vận tốc của các phương tiện được theo vết.
Đối với từng video thu nhận được, để kết quả xác định vận tốc được chính xác, ta cần xác định được chính xác độ rộng D1, D2 của quang cảnh thực tế mà camera có thể quan sát được. Ngoài ra, góc nghiêng của camera, vị trí lắp camera cũng có ảnh hưởng rất lớn đến độ chính xác của việc ước lượng vận tốc. Khi phương tiện chuyển động với cùng vận tốc nhưng tại vị trí ở xa camera hơn thì độ lớn của véc tơ vận tốc cũng nhỏ hơn (do khoảng cách di chuyển theo điểm ảnh nhỏ hơn - điều này cũng thể hiện ở chỗ khi ở xa camera thì vật thể nhỏ hơn là khi tiến lại gần camera).
Dưới đây là một số kết quả chạy chương trình mô phỏng với 05 bộ video khác nhau sưu tầm trên Youtube.com:
50
Hình 3.6: Kết quả tính vận tốc của các phương tiện với video 1
51
Hình 3.8: Kết quả tính vận tốc với video 3
52
Hình 3.10: Kết quả tính vận tốc với video 5 (VOV giao thông)
Hình 3.11: Kết quả xác định vận tốc (không vẽ chu tuyến phương tiện)
3.4. Thực nghiệm và đánh giá
3.4.1. Kết quả thực nghiệm
53
trên youtube.com với 04 thuật toán đã nêu trong chương 3 ở trên. Trong các bộ video, có 01 bộcó độ phân giải 480x360 và 05 bộcó độ phân giải là 1280x720.
Phần mềm sử dụng trong thực nghiệm:
- Ngôn ngữ lập trình Visual C++, bộ thư viện xử lý ảnh mã nguồn mở OpenCV, phiên bản 2.4.6.
Cấu hình thực nghiệm cho các thuật toán: - Thuật toán Lucas-Kanade: chọn 400 đặc trưng. - Thuật toán Horn-Schunck: lambda = 0.1.
- Thuật toán Farneback: pyr_scale=0.5, levels = 3, winSize = 15, iterations = 3, poly_n = 5, poly_sigma = 1.2, flags = 0;
- Thuật toán Block Matching: block = (16,16), shift = (1,1), range = (3,2); Bảng kết quả thời gian tính toán của từng thuật toán trên 02 khung hình liên tiếp được ghi nhận lại (tính bằng giây) như sau:
Bảng 3.1: Kết quả thực nghiệm trên 06 bộ test
STT Tên file test Độ phân giải
(pixel x pixel) Thuật toán Thời gian (giây) 1 test01.mp4 480 x 360 Lucas – Kanade 0.002 Horn – Schunck 0.817 Farneback 0.148 Block Matching 0.051 2 test02.mp4 1280 x 720 Lucas – Kanade 0.006 Horn – Schunck 13.464 Farneback 0.377 Block Matching 0.86 3 test03.mp4 1280 x 720 Lucas – Kanade 0.006 Horn – Schunck 10.415 Farneback 0.384 Block Matching 1.985 4 test04.mp4 1280 x 720 Lucas – Kanade 0.007
54 Horn – Schunck 10.395 Farneback 0.387 Block Matching 1.493 5 test05.mp4 1280 x 720 Lucas – Kanade 0.008 Horn – Schunck 10.401 Farneback 0.386 Block Matching 4.817 6 test06.mp4 1280 x 720 Lucas – Kanade 0.008 Horn – Schunck 10.241 Farneback 0.389 Block Matching 4.825
Hình 3.12: Biểu đồ so sánh thời gian chạy giữa các thuật toán
test01 (480x360) test02 (1280x720) test03 (1280x720) test04 (1280x720) test05 (1280x720) test06 (1280x720) Lucas-Kanade 0.002 0.006 0.006 0.007 0.008 0.008 Horn-Schunck 0.817 13.464 10.415 10.395 10.401 10.241 Farneback 0.148 0.377 0.384 0.387 0.386 0.389 BlockMatching 0.051 0.86 1.985 1.493 4.817 4.825 0 2 4 6 8 10 12 14 16 Th ời gian tín h toán (giây)
55
3.4.2. Đánh giá kết quả
Qua kết quả thực nghiệm có được từ 06 bộ test, ta rút ra một số nhận xét sau: - Thời gian thực thi của thuật toán Lucas-Kanade là nhanh nhất, sau đó đến thuật toán Farneback, Block Matching và cuối cùng là thuật toán Horn-Schunck.
- Thuật toán Lucas-Kanade tính toán cho luồng thưa, với số đặc trưng nhất định. Còn 3 thuật toán còn lại đều tính cho luồng dày, với tất cảcác điểm ảnh trong ảnh đầu vào, do đó thời gian tính toán lâu hơn so với Lucas-Kanade.
- Tốc độ tính toán của các thuật toán đều tỷ lệ nghịch với kích thước khung hình video thu nhận được, tức là nếu độ phân giải video lớn thì thời gian thực hiện thuật toán chậm đi. Cụ thể, khi độ phân giải thay đổi từ 480x360 lên 1280x720,