Trong đề tài “ỨNG DỤNG XỬ LÝ ẢNH BẰNG THUẬT TOÁN SVM” với nhiệm vụ nghiên cứu về mặt kĩ thuật của bài toán nhận dạng biển số xe, viết chương trình mô phỏng bằng ngôn ngữ C++ với sự giúp đỡ của thư viện OpenCV, một thư viện mã nguồn mở được đánh giá là mạnh mẽ về tốc độ xử lý đáp ứng được các ứng dụng trong thời gian thực, sau đó sử dụng thuật toán máy học SVM (Surport Vector Machine) để nhận dạng ký tự biển số xe.
Trang 1MỤC LỤC DANH MỤC CÁC TỪ VIẾT TẮT
MỞ ĐẦU
CHƯƠNG 1 : LÀM QUEN VỚI THƯ VIỆN OPENCV
1.1 Giới thiệu chương
1.2 So sánh phiên bản OpenCV 1 và OpenCV 2
1.3 Hướng dẫn sử dụng thư viện Open CV trên Window
1.4 Kết luận chương
CHƯƠNG 2 : CÁC PHÉP XỬ LÝ ẢNH ĐƠN GIẢN TRONG OPENCV
2.1 Giới thiệu chương
2.2 Chương trình đầu tiên
2.3 Ảnh nhị phân, nhị phân hóa với ngưỡng động
2.4 Các phép toán hình thái học trong ảnh
2.4.1 Phép toán giãn nở (dialation)
2.4.2 Phép toán co (erosion)
2.4.3 Phép toán đóng và mở (closing & opening)
2.5 Kết luận chương
CHƯƠNG 3 : LẬP TRÌNH XỬ LÝ ẢNH VỚI GIAO DIỆN MFC
3.1 Giới thiệu chương
3.2 Khởi tạo project MFC
3.3 Làm việc với các điều khiển (Control) của MFC
3.3.1 Đặt tên biến cho các control
3.3.2 Lấy giá trị nhập vào từ một Edit Control
3.3.3 Hiển thị các test lên các control
3.3.4 Hiển thị ảnh lên một control
3.3.5 Enable, disnable một control
3.3.6 Lấy giá trị từ thanh trược (Slider control)
3.3.7 Lấy giá trị lựa chọn từ Combo Box
3.3.8 Dialog mở file và lưu file
Trang 23.3.9 Xử lý sự kiện khi click chuột vào button
3.3.10 Xử lý sự kiện khi thay đổi lựa chọn Combo box
3.3.11 Thêm menu vào chương trình, xử lý sự kiện khi click vào menu3.4 Chuyển đổi các kiểu dữ liệu trong MFC
3.5 Chương trình tải ảnh và hiển thị ảnh trên giao diện MFC
3.6 Kết luận chương
CHƯƠNG 4: NHẬN DẠNG BIỂN SỐ XE BẰNG THUẬT TOÁN SVM
4.1 Giới thiệu chương
4.2 Phát hiện vùng chứa biển số xe và cách ly kí tự
4.3 Nhận dạng kí tự
4.3
4.3.2 Tính toán đặt trưng trong ảnh
4.4 Cài đặt chương trình nhận dạng biển số xe ô tô
4.4.1 Huấn luyện mô hình SVM
4.5 Phát hiện và nhận dạng biển số xe
4.6 Giao diện chương trình chính
4.7 Kết luận chương
KẾT LUẬN – HƯỚNG PHÁT TRIỂN
TÀI LIỆU THAM KHẢO
PHỤ LỤC
Trang 3DANH MỤC CÁC TỪ VIẾT TẮT
API Application Programming Interface
BSD Berkeley Software Distribution
IEEE Institute of Electrical and Electronics Engineers
LPR License Plate Recognition
MFC Microsoft Foundation Classes
OpenCV Open Source Computer Vision
RFID Radio Frequency Identification
SVM Surport Vector Machine
Trang 4mẽ trong tương lai nếu như được quan tâm một cách nghiêm túc
Trong đề tài “ỨNG DỤNG XỬ LÝ ẢNH BẰNG THUẬT TOÁN SVM” với
nhiệm vụ nghiên cứu về mặt kĩ thuật của bài toán nhận dạng biển số xe, viết chươngtrình mô phỏng bằng ngôn ngữ C++ với sự giúp đỡ của thư viện OpenCV, một thưviện mã nguồn mở được đánh giá là mạnh mẽ về tốc độ xử lý đáp ứng được các ứngdụng trong thời gian thực, sau đó sử dụng thuật toán máy học SVM (Surport VectorMachine) để nhận dạng ký tự biển số xe
Nội dung đồ án bao gồm:
Chương 1 : Làm quen với thư viện OpenCV
Chương 2 : Các phép xử lý ảnh đơn giản trong OpenCV
Chương 3 : Lập trình xử lý ảnh với giao diện MFC
Chương 4 : Nhận dạng biển số xe bằng thuật toán SVM
Tuy em đã cố gắng tìm tòi và nỗ lực hết sức để thực hiện đồ án nhưng hẳn sẽ cókhông ít sai sót, vì vậy kính mong các thầy cô góp ý và chỉ bảo thêm, giúp em nắmvững hơn vốn kiến thức của mình
Em xin chân thành cảm ơn
Trang 5CHƯƠNG 1 : LÀM QUEN VỚI THƯ VIỆN OPENCV 1.1 Giới thiệu chương
Chương này giới thiệu về OpenCV (Open Source Computer Vision), một thưviện mã nguồn mở về thị giác máy với hơn 500 hàm và hơn 2500 các thuật toán đãđược tối ưu về xử lý ảnh, và các vấn đề liên quan tới thị giác máy OpenCV đượcthiết kế một cách tối ưu, sử dụng tối đa sức mạnh của các dòng chip đa lõi… đểthực hiện các phép tính toán trong thời gian thực, nghĩa là tốc độ đáp ứng của nó cóthể đủ nhanh cho các ứng dụng thông thường
OpenCV là thư viện được thiết kế để chạy trên nhiều nền tảng khác nhau(cross-patform), nghĩa là nó có thể chạy trên hệ điều hành Window, Linux, Mac,iOS …Việc sử dụng thư viện OpenCV tuân theo các quy định về sử dụng phầnmềm mã nguồn mở BSD do đó có thể sử dụng thư viện này một cách miễn phí cho
cả mục đích phi thương mại lẫn thương mại Dự án về OpenCV được khởi động từnhững năm 1999, đến năm 2000 nó được giới thiệu trong một hội nghị của IEEE vềcác vấn đề trong thị giác máy và nhận dạng, tuy nhiên bản OpenCV 1.0 mãi tới tậnnăm 2006 mới chính thức được công bố và năm 2008 bản 1.1 (pre-release) mớiđược ra đời Tháng 10 năm 2009, bản OpenCV thế hệ thứ hai ra đời (thường gọi làphiên bản 2.x), phiên bản này có giao diện của C++ (khác với phiên bản trước cógiao diện của C) và có khá nhiều điểm khác biệt so với phiên bản thứ nhất
Thư viện OpenCV ban đầu được sự hỗ trợ từ Intel, sau đó được hỗ trợ bởWillow Garage, một phòng thí nghiệm chuyên nghiên cứu về công nghệ robot Chođến nay, OpenCV vẫn là thư viện mở, được phát triển bởi nguồn quỹ không lợinhuận (none -profit foundation) và được sự hưởng ứng rất lớn của cộng đồng
1.2 So sánh phiên bản OpenCV 1 và OpenCV 2
Cho tới nay, trải qua hơn 6 năm từ lúc phiên bản OpenCV đầu tiên được công
bố, đã có lần lượt nhiều phiên bản OpenCV ra đời, tuy nhiên có thể chia thư việnnày thành hai bản chính dựa trên những đặc điểm khác biệt lớn nhất của chúng:
Trang 6phiên bản OpenCV thế hệ thứ nhất (hay còn gọi là phiên bản OpenCV 1.x) và phiênbản OpenCV thứ hai (hay còn gọi là phiên bản OpenCV 2.x) Sau đây là một sốđiểm khác biệt cơ bản giữa hai phiên bản này.
OpenCV 1.x (bao gồm bản 1.0 và bản pre-release 1.1) dựa trên giao diện C, cấutrúc của một ảnh số dựa trên cấu trúc của IplImage, trong khi thư OpenCV 2.x dựatrên giao diện C++, cấu trúc của ảnh số, ma trận dựa trên cấu trúc của cv::Mat.Trong OpenCV 1.x, người sử dụng phải hoàn toàn quản lý bộ nhớ của các đốitượng, nghĩa là khi một đối tượng mới được tạo ra, chúng ta phải luôn chú ý để giảiphóng nó khi không còn sử dụng nữa (trong nhiều trường hợp có thể sẽ bị tràn bộnhớ nếu không chú ý đều này), trong khi thư viện OpenCV 2.x việc quản lý bộ nhớtrở nên dễ dàng hơn nhờ các hàm hủy các các lớp đối tượng trong OpenCV 2.x đãthực hiện điều này khi một đối tượng không còn được sử dụng nữa
Việc viết các dòng lệnh để thực hiện cùng một chức năng trong OpenCV 2.x là
dễ dàng hơn nhiều so với OpenCV 1.x, một phần là là giao diện C++ có phần dễhiểu hơn so với C, một phần là các hàm trong OpenCV 2.x đã được tối ưu hóa nhiềubước trung gian không cần thiết về mặt giao diện người sử dụng Chẳng hạn khi xét
ví dụ về việc phát hiện đường tròn trong ảnh màu dựa vào thuật toán Hough, cácbước để thực hiện là load một ảnh màu, chuyển sang ảnh nhị phân, tìm biên dựatrên bộ lọc canny và phát hiện đường tròn dựa trên thuật toán Hough OpenCV 1.xthực hiện như sau:
// Phát hiện đường tròn trong ảnh OpenCV 1.x
IplImage* src = cvLoadImage(“image.jpg”);
IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);
cvCvtColor(src, gray, CV_BGR2GRAY);
cvCanny(gray, gray, 10, 30, 3);
CvMemStorage* storage = cvCreateMemStorage(0);
CvSeq*circles=cvHoughCircles(gray,storage,CV_HOUGH_GRADIENT, 1,50,100,50);
Trang 7Trong khi đó, OpenCV 2.x thực hiện như sau:
// Phát hiện đường tròn trong ảnh OpenCV 2.x
HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 50, 100, 50);
Nhận thấy đối tượng ảnh gray trong OpenCV 2.x không cần phải khởi tạo, đốitượng storage (đối tượng trung gian, không có ý nghĩa về mặt sử dụng)cũng không cần phải khởi tạo (và do đó không cần giải phóng)
Thư viện OpenCV 1.x tuy chứa một lượng lớn hàm xử lý và thuật toán, tuynhiên nó vẫn ở dạng sơ khai Thư viện OpenCV 2.x đã được bổ xung khá nhiềuhàm, thuật toán và được tối ưu khá nhiều đặc biệt trong các khía cạnh về phát hiệnđối tượng (detection), nhận dạng đối tượng (partten regconition) và theo dõi đốitượng (tracking) Hơn thế nữa, tuy có giao diện là C++ nhưng OpenCV 2.x vẫn dữmột phần giao diện C để tương thích với các phiên bản của OpenCV 1.x
Từ một số đặc điểm trên có thể thấy rằng thư viện OpenCV phiên bản 2.x là cónhiều điểm nổi trội hơn so với phiên bản 1.x, tuy nhiên trong một số trường hợpnhư ở các hệ thống nhúng khi mà trình dịch chỉ đơn thuần chấp nhận ngôn ngữ Cthì phiển bản 1.x vẫn còn giá trị Trong đồ án này, các nội dung cài đặt, thuật toán,ứng dụng … chỉ dành cho OpenCV phiên bản 2.x trên nền tảng hệ điều hànhWindow
1.3 Hướng dẫn sử dụng thư viện Open CV trên Window
Trước hết chúng ta download thư viện OpenCV về máy tính, tốt hơn là luôndownload bản mới nhất tại địa chỉ http://sourceforge.net/projects/opencvlibrary/ Chọn bản đã build sẵn phù hợp với hệ điều hành đang dùng, bản OpenCV được sửdụng trong đề tài này là bản 2.4.3 Sau khi download về máy, tiến hành cài đặt bình
Trang 8thường, để mặc định thư mục cài đặt là C:\ thư mục cài đặt xong sẽ có dạng C:\opencv Tiếp theo tiến hành tùy chỉnh để có thể làm việc với OpenCV qua hai IDEthông dụng là Microsoft Visual Studio và Eclipse
Trên Microsoft Visual Studio
Phiên bản Visual studio sử dụng ở đây là phiên bản Visual Studio 2010, các phiên bản trước hoàn toàn có thể cấu hình một cách tương tự
Tạo một project mới: New > Project, trong cửa sổ New Project chọn Visual C++, Win32 Console Application Đặt tên project là opencv
Hình 1.1
Chọn OK, sau đó nhấn Next, hộp thoại tiếp theo xuất hiện, ở hộp thoại này chọn Application type là Console application và Additional option là Empty project, nhấn Finish để kết thúc quá trình khởi tạo.
Trang 9Hình 1.2
Project mới được tạo ra là project hoàn toàn trống, chúng ta phải thêm vào đó ít nhất một file nguồn để chương trình
có thể chạy được, trong Solution
Explorer click chuột phải vào Source
Files, chọn Add -> New Item… Hộp
thoại Add New Item hiện ra, ta chọn
kiểu cần thêm vào là C++ File(.cpp)
đồng thời trong ô Name ta đặt tên cho
file thêm vào, giả sử là
FirstApp.cpp
Hình 1.3
Trang 10Bây giờ trong file này chúng ta có thể thêm vào các #include và gọi hàm main() để
Chúng ta sẽ chỉ đường dẫn hai thư mục này đến các phần tương ứng của thưviệnOpenCV
Mục Include Directories, ta tùy chỉnh ở ô bên phải tới C:\opencv\build\include Mục Library Directories trỏ đến thư mục C:\opencv\build\x86\vc10\lib nếu như sử dụng hệ điều hành 32bit hoặc C:\opencv\build\x64\vc10\lib cho hệ điều hành
Trang 11release Cuối cùng, khi dịch xong một chương trình, để nó có thể chạy được ta cần chú ý tới cácfile *.dll Cách đơn giản là chúng ta copy các file *.dll tương ứng (như opencv_core243d.dll, opencv_imgproc243d.dll) vào thư mục chứa file chạy của chươngtrình (file *.exe) Các file *.dll này nằm trong mục C:\opencv\build\x86\bin với win 32 bit hoặc C:\opencv\build\x64\bin với win 64 bit Với các phiên bản OpenCV cũ hơn, ta cần copy luôn file tbb_debug.dll (trong chế độ debug) hoặc tbb.dll (trong chế độ release) vào thư mục chứa file *.exe tbb.dll (Thread building block) là file khá quan trọng, thiếu nó chương trình sẽ báo lỗi.
Sau khi đã hoàn tất việc chỉ dẫn thư mục chứa header, library và link tới cáclibrary tương ứng, ta có thể include các header của opencv vào chương trình và cóthể gọi các hàm làmviệc của OpenCV
Trang 12CHƯƠNG 2 : CÁC PHÉP XỬ LÝ ẢNH ĐƠN GIẢN TRONG OPENCV 2.1 Giới thiệu chương
Chương 2 sẽ giới thiệu các chương trình đơn giản để xử lý một bức ảnh Trongchương này em giới thiệu 3 chương trình chính đó là: chương trình để load và hiểnthị một ảnh, chương trình nhị phân hóa với ngững động, chương trình về các phéptoán hình thái học về việc giản nỡ và nối liền biên ảnh để tìm biển số xe ô tô
2.2 Chương trình đầu tiên
Trong ví dụ này, em sẽ viết một chương trình Hello world để load và hiển thị
cout<<"Chuong trinh dau tien"<<endl;
Mat img = imread("vietnam.jpg", CV_LOAD_IMAGE_COLOR);
namedWindow("Viet Nam", CV_WINDOW_AUTOSIZE);
imshow("Viet Nam", img);
waitKey(0);
return 0;
}
Như đã nói ở trên, trong Opencv với giao diện C++, tất cả các kiểu dữ liệu ảnh,
ma trận điều được lưu ở dạng cv::Mat Hàm imread sẽ đọc ảnh đầu vào và lưu vào
Trang 13không nằm trong thư mục làm việc hiện hành thì chúng ta phải chỉ ra đường dẫn có
dạng như D:\Anh\vietnam.jpg hoặc D://Anh//Vietnam.jpg Flags là tham số loại ảnh
mà chúng ta muốn load vào, cụ thể nếu muốn load ảnh màu thì chúng ta để
CV_LOAD_IMAGE_COLOR, còn nếu muốn là ảnh xám thì để CV_LOAD_IMAGE_GRAYSCALE…Sau khi đã load ảnh thành công, muốn hiển thị ảnh lên màn hình cần tạo ra một cửa sổ, hàm namedWindow(const std::string
&winname, int flags) sẽ tạo ra cửa sổ với tiêu đề cửa sổ là một chuỗi string
winname Tham số flags sẽ chỉ ra kiểu cửa sổ muốn tạo: nếu tham số
CV_WINDOW_AUTOSIZE được sử dụng thì kích cỡ cửa sổ tạo ra sẽ được hiển thị
một cách tự động tùy thuộc vào kích thước của ảnh, nếu là tham số
CV_WINDOW_AUTOSIZE_FULLSCREEN kích thước cửa sổ sẽ khít với màn hình máy tính …Cuối cùng, hàm imshow(const std::string &winname, cv::InputArray Mat) sẽ hiển thị ảnh ra cửa sổ đã được tạo ra trước đó.
Hàm waitKey(int delay) sẽ đợi cho đến khi có một phím được bấm vào trong khoảng thời gian là delay Chú ý là nếu không có hàm này thì chương trình sau khi
chạy sẽ không dừng lại màn hình và kết thúc luôn, ta dùng hàm này mục đích là để
dừng màn hình lại trong một khoảng thời gian bằng tham số delay (tính theo đơn vị millisecond) Nếu muốn dừng màn hình lại mãi mãi ta đặt tham số delay bằng 0.
Và sau đây là kết quả của chương trình chạy:
Trang 14Hình 2.1 2.3 Ảnh nhị phân, nhị phân hóa với ngưỡng động
Ảnh nhị phân là ảnh mà giá trị của các điểm ảnh chỉ được biểu diễn bằng haigiá trị 0 hoặc 255 tương ứng với hai màu đen hoặc trắng Nhị phân hóa một ảnh là
quá trình biếnmột ảnh xám thành ảnh nhị phân Gọi f(x,y) là giá trị cường độ sáng của một điểm ảnh ở vị trí (x,y),T là ngưỡng nhị nhị phân Khi đó, ảnh xám f sẽ được chuyển thành ảnh nhị phân dựa vào công thức f(x,y) = 0 nếu f(x,y) ≤ T và f(x,y)
=255 nếu f(x,y) > T
Hình sau mô tả một ảnh nhị phân với ngưỡng nhị phân T = 100
Trang 15Hình 2.2
Hàm để chuyển nhị phân hóa ảnh trong OpenCV là hàm threshold() Nguyên
mẫu hàm như sau:
threshold(cv::InputArray src, cv::OutputArray dst, double thresh, int maxval, int type)
Trong đó, src là ảnh đầu vào một kênh màu (ảnh xám …), dst là ảnh sau khi được nhị phân hóa, thresh là ngưỡng nhị phân, maxval là giá trị lớn nhất trong ảnh (maxval = 255 đối với ảnh xám), type là kiểu nhị phân có thể là
CV_THRESH_BINARY,CV_THRESH_BINARY_INV, CV_THRESH_OTSU…lần lượt là nhị phân hóa thông thường, nhị phân hóa ngược và nhị phân hóa theothuật toán Otsu …
Kết quả của việc nhị phân hóa một ảnh phụ thuộc vào ngưỡng T, có nghĩa làvới mỗi ngưỡng T khác nhau thì ta có những ảnh nhị phân khác nhau Hình sau mô
tả 3 ảnh nhị phân tương ứng với ngưỡng T = 50, T = 100 và T = 150
Trang 16Hình 2.3
Để thu được một ảnh nhị phân tốt mà không cần phải quan tâm tới các điều kiệnánh sáng khác nhau (không cần quan tâm tới ngưỡng T), người ta dùng một kĩ thuậtsao cho với mọi ngưỡng xám khác nhau ta luôn thu được một ảnh nhi phân tốt Kĩthuật đó gọi là kĩ thuật nhị phân hóa với ngưỡng động (Dymamic threshold) hay nhịphân thích nghi (Adaptive threshold)
Có nhiều phương pháp khác khác nhau để thực hiện việc này, tuy nhiên chúngđều dựa trên ý tưởng chính là chia ảnh ra thành những vùng nhỏ, với mỗi vùng ápdụng việc nhị phân cho vùng đó với những ngưỡng nhị phân khác nhau Cácngưỡng nhị phân ở các vùng được tính toán dựa trên độ lớn mức xám của chính cácpixel trên vùng đó Giả sử ta tính toán ngưỡng cho một vùng nào đó dựa trên độtrung bình của các pixel trong vùng đó (có thể xem một vùng là một cửa sổ) Ta xétquá trình nhị phân với ngưỡng động trong một vùng cửa sổ 5x5:
Trang 17Hình 2.4
Vùng ảnh nhị phân thu được ở trên là vùng ảnh được nhị phân với ngưỡng là trung
bình cộng của tất cả các ô trong cửa sổ T = (55 + 10 + 100 + …)/25 = 65.6.
Chương trình nhị phân hóa với ngưỡng động như sau:
cout<<"Nhi phan anh voi nguong dong"<<endl;
Mat src = imread("Thap_But.jpg", CV_LOAD_IMAGE_GRAYSCALE);
Mat dst;
adaptiveThreshold(src, dst, 255, CV_ADAPTIVE_THRESH_MEAN_C,
CV_THRESH_BINARY, 35, 5);
imshow("Anh xam goc", src);
imshow("Anh nhi phan voi nguong dong", dst);
waitKey(0);
Trang 18return 1;
}
Trong chương trình trên, hàm thực hiện việc nhị phân hóa với ảnh động
là hàm adaptiveThreshold Nguyên mẫu của hàm như xau:
cv::adaptiveThreshold(cv::InputArray src,OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C)
Trong đó, src là ảnh xám cần nhị phân, dst là ảnh kết quả thu được, maxValue
là giá trị lớn nhất trong ảnh xám (thông thường là 255), adaptiveMethod là cách
thức nhị phân với ngưỡng động, nó chính là cách tính giá trị ngưỡng nhị phân trong
từng vùng cần nhị phân, thresholdType như đã nói ở trên, blockSize là kích thước của sổ áp dụng cho việc tính toán ngưỡng động, và C là một thông số để bù trừ
trong trường hợp ảnh có độ tương phản quá lớn
Kết quả khi chạy chương trình như sau:
Hình 2.5
Trang 192.4 Các phép toán hình thái học trong ảnh
Các phép toán hình thái học là những phép toán liên quan tới cấu trúc hình học(hay topo) của các đối tượng trong ảnh Các phép toán hình thái học tiêu biểu baogồm phép giãn nở (dialation), phép co (erosion), phép mở (opening) và phép đóng(closing)
2.4.1 Phép toán giãn nở (dialation)
Phép toán giãn nở được định nghĩa A ⊕ B = UB x với x ⊂ A trong đó, A là đốitượng trong ảnh, B là một cấu trúc phần tử ảnh Phép toán này có tác dụng làm chođối tượng ban đầu trong ảnh tăng lên về kích thước (giãn nở ra)
Cấu trúc phần tử ảnh (image structuring element) là một hình khối được địnhnghĩa sẵn nhằm tương tác với ảnh xem nó có thỏa mãn một số tính chất nào đókhông, một số cấu trúc phần tử hay gặp là cấu trúc theo khối hình vuông và hìnhchữ thập
Hình 2.6
Xét một ảnh với đối tượng trong ảnh được biểu diễn bằng màu nền nâu, sau đódùng cấu trúc phần tử hình vuông (màu đỏ) để làm giãn nở ảnh, kết quả là ảnh đượcgiãn nở ra, chúng ta đánh dấu là dấu x
Trang 20Hình 2.7
Ứng dụng của phép giãn nở là làm cho đối tượng trong ảnh được tăng lên vềkích thước, các lỗ nhỏ trong ảnh được lấp đầy, nối liền đường biên ảnh đối vớinhững đoạn rời nhỏ…
2.4.2 Phép toán co (erosion)
Phép toán co được định nghĩa A⊝ B = {x |(B) x ⊆ A} trong đó A là đối tượng
trong ảnh, B là cấu trúc phần tử ảnh Ta cũng sẽ xét một ví dụ như trên với phép cotrong ảnh Đối tượng trong ảnh được biểu diễn bởi màu xám, cấu trúc phần tử ảnh làkhối có viền màu đỏ, x là điểm sau phép thỏa mãn phép co ảnh, 0 là điểm khôngthỏa mãn
Trang 21Nhận thấy rằng sau phép toán này đối tượng trong ảnh bị co lại, chính vì vậy
mà nó được ứng dụng trong việc giảm kích thước của đối tượng, tách rời các đốitượng gần nhau và làm mảnh, tìm xương đối tượng
2.4.3 Phép toán đóng và mở ảnh (closing & opening)
Phép toán mở (opening) và đóng (closing) là sự kết hợp của phép co (erosion)
và phép giãn nở (dialation), chúng được định nghĩa như sau :
+ Phép toán mở : Opening(I) = D(E(I))
+ Phép toán đóng : Close(I) = E(D(I))
Phép toán mở được ứng dụng trong việc loại bỏ các phần lồi lõm và làm chođường bao quanh đối tượng trong ảnh trở nên mượt mà hơn
Phép toán đóng được ứng dụng trong việc làm trơn đường bao quanh đối tượng,lấp đầy các khoảng trống trên biên và loại bỏ những hố nhỏ (một số pixel đứngthành cụm độc lập)
Trong OpenCV, các phép toán hình thái học trong ảnh được cài đặt trong hàm
cv::morphologyEx, riêng phép giãn nở và phép co có thể gọi trực tiếp từ hàm cv::dilate và cv::erode: morphologyEx(const Mat& src, Mat& dst, int op, const Mat& element, Point anchor, int iterations, int borderType, const Scalar& borderValue)
Trong đó src, dst là ảnh đầu vào và ảnh sau phép xử lý hình thái học op là kiểulựa chọn phép hình thái học, chẳng hạn như phép giãn nở là MORPH_DILATE,phép đóng là MORPH_OPEN… element là cấu trúc phần tử ảnh, có ba cấu trúc cơbản là theo khối hình vuông, hình chữ thập và hình elip Để tạo ra các cấu trúc này
ta có thể tự định nghĩa một ma trận với các hình khối tương ứng hoặc sử dụng hàm
getStructuringElement, hàm này có cấu trúc như sau: getStructuringElement(int shape, Size ksize, Point anchor), với shape là kiểu hình khối (một trong 3 hình khối trên), ksize là kích thước của hình khối và là kích thước của hai số nguyên lẻ, anchor là điểm neo và thông thường nhận giá trị là((ksize.width – 1)/2, (ksize.height – 1)/2) Thông số tiếp theo anchor cũng có ý nghĩa tương tự iterations là số lần lặp
Trang 22lại của phép toán hình thái và hai thông số cuối cùng là về giới hạn biên của nhữngđiểm ảnh nằm ngoài kích thước ảnh trong quá trình tính toán.
Phép toán về giản nở và co có thể được gọi từ hàm cv::morphologyEx thông qua hai đối số op là MORPH_DILATE và MORPH_ERODE hoặc chúng có thể được gọi trực tiếp từ hàm cv::dilate và cv::erode, Cấu trúc của hai hàm này là tương
tự nhau và các tham số hoàn toàn giống với tham số trong hàm morphologyEx: dilate(const Mat& src, Mat& dst, const Mat& element, Point anchor=Point(-1, -1), int iterations, int borderType, const Scalar& borderValue)
Một điều cần chú ý là trái với cách diễn đạt về các phép hình thái như trên,OpenCV có cách cài đặt ngược lại giữa đối tượng và nền ảnh, nghĩa là trong phépgiản nở (dilate), phần được giản nở là nền của ảnh (background) chứ không phải cácđối tượng vật thể trong ảnh, điều đó cũng có nghĩa rằng phép giản nở sẽ làm co lạicác đối tượng vật thể trong ảnh và phép co (erode) sẽ làm co nền của ảnh đồng thờigiản nở các đối tượng vật thể trong ảnh
Sau đây chúng ta sẽ xét một ví dụ về việc sử dụng các phép toán hình thái trongảnh trong việc phát hiện biển số xe ô tô và cách ly các kí tự trong biển số Giả sử ta
có một ảnh chứa biển số, vì biển số có nền trắng và kí tự màu đen, nên trước tiên tanhị phân ảnh đó, sau đó tìm đường bao của các đối tượng (contour) để từ đó xácđịnh được xem những đường bao nào có khả năng là biển số xe nhất dựa trên tỉ lệcác cạnh chiều dài, rộng của hình chữ nhật bao quanh đường bao đó (đây là mộtcách tiếp cận rất đơn giản) Tuy nhiên, chỉ xác định được đường bao quanh đốitượng khi tập các điểm nằm trên biên của nó tạo thành một vùng kín Trong nhiềutrường hợp của biển số, việc bị mất một vài điểm ảnh trong quá trình nhị phân trênbiên là chuyện thường xuyên xảy ra và do đó sẽ khó xác định được một đường baoquanh biển số Để khắc phục tình trạng này, ta sẽ làm giãn nở đường biên (điều đó
sẽ tương đương với việc gọi hàm erode để làm co lại nền ảnh) để các điểm ảnh trênbiên trở nên liền hơn
Trang 23Mat src1 = imread("BienSo.jpg", CV_LOAD_IMAGE_COLOR);
Mat src2 = src1.clone(); // copy anh
Mat gray, binary;
cvtColor(src1, gray, CV_BGR2GRAY);
threshold(gray, binary, 100, 255, CV_THRESH_BINARY);
imshow("Anh nhi phan goc", binary);
Mat morpho;
Mat element = getStructuringElement(MORPH_CROSS, Size(3,3),
Point(1,1));
erode(binary, morpho, element, Point(-1,-1), 3);
imshow("Anh sau khi thuc hien phep gian no", morpho);
Trang 24imshow("Ket qua phat hien truoc phep gian no", src1);
thông qua hàm erode HàmfindContours sẽ tìm đường bao quanh của các đối tượng
đã được nhị nhâp hóa và lưu đường bao này vào một vector là các điểm nằm trên
đường bao đó, hàm boundingRect sẽ tìm ra một hình chữ nhật bao một tập điểm của một đường bao được tìm ra từ hàm findContours Dựa vào tỉ lệ kích thước chiều
dài/rộng của hình chữ nhật này ta có thể xem xét liệu đường bao mà ta tìm được cóphải là một vùng của biển số hay không, nếu phải ta sẽ vẽ hình chữ nhật này thông
qua hàm rectangle với một màu khác (Scalar(0, 0, 255) :màu đỏ) và nét đậm hơn
Kết quả chạy chương trình như sau:
Trang 252.5 Kết luận chương
Đây là công đoạn đầu tiên mang tính quyết định đối với quá trình xử lý ảnh.Ảnh đầu vào sẽ được thu nhận qua các thiết bị camera, máy scanner…và sau đó tínhiệu này sẽ được số hóa và qua các công đoạn xử lý như trên với mục đích nâng caochất lượng ảnh để chuẩn bị cho các bước xử lý phức tạp hơn về sau
Trang 26CHƯƠNG 3 : LẬP TRÌNH XỬ LÝ ẢNH VỚI GIAO DIỆN MFC
3.1 Giới thiệu chương:
Chương 3 sẽ giới thiệu về MFC (Microsoft Foundation Classes), đó là bộ thưviện được Microsoft phát triển phục vụ cho việc lập trình trên Window Bản chấtcủa thư viện này là cung cấp cho ta các lớp, các công cụ để làm việc với các hàmAPI của Window, do vậy việc lập trình trở nên đơn giản hơn rất nhiều
Trong chương này và chương sau, các ví dụ và ứng dụng sẽ được tạo ra nhờ vàogiao diện Dialog của MFC
3.2Khởi tạo project MFC
Khởi động Visual Studio, từ menu chọn File -> New - > Project (hoặc nhấn Ctrl + Shift +N) Hộp thoại New Project hiện ra, chọn Visual C++ (có thể sẽ phải chọn mục Other language trước khi hiển ra Visual C++) sau đó chọn MFC Application Ta đặt tên cho project trong trường Name (giả sử tên là xyz) sau đó click OK để đến bước tiếp theo Ở bước tiếp theo, tiếp tục chọn Next ta được hộp
thoại sau:
Trang 27Application type chọn Dialog based, Project type chọn MFC standard… Chú ý rằng việc tick vào chọn Use Unicode libraries sẽ có những ý nghĩa và cách dùng khác nhau, ta sẽ xét trường hợp này sau Ta nhấn next để đi tới hộp thoại tiếp theo, hộp
thoại này cho phép ta tùy chỉnh một số chức năng của cửa sổ như có thêm nútphóng to, thu nhỏ, menu… có thể để mặc định và chọn next
Hình 3.2
Nhấn Next để đi tới tùy chỉnh tiếp một số tùy chọn nâng cao, nếu chưa hiểu rõ
có thể để mặc định và nhấn Next Hộp thoại cuối cùng xuất hiện yêu cầu ta chọn để MFC khởi tạo lớp Ta chọn là CxyzDlg và nhấn Finish.
Trang 28Hình 3.3
Đến đây ta đã khởi tạo xong một project MFC có dựa trên nền Dialog, Dialog hiện
ra mặc định có một button OK, button Cancel và một label, ta có thể để sử dụnghoặc xóa đi và thiết kế theo ý riêng của mình
3.3Làm việc với các điều khiển (Control) của MFC
3.3.1 Đặt tên biến cho các control
Khi muốn sử dụng một Control
nào, ta kéo control đó từ Toolbox và
cho vào dialog Để làm việc với các
tên biến cho các control
Để đặt tên biến cho một control, ta
click chuột phải vào control, sau đó
chọn AddVariable.Một hộp thoại Add
Member Variable Wizard hiện ra và
Trang 29control mà ID của nó có dạng IDC_STATIC (như StaticText) thì ta chỉ có thể đặttên biến được khi đổi ID của nó, chẳng hạn như đổi thành IDC_STATIC,IDC_LABEL…
Hình 3.5 3.3.2 Lấy giá trị nhập vào từ một Edit Control
Để lấy giá trị nhập vào từ một Edit Control, ta sử dụng hàm GetWindowText().
Giả sử như Edit Control được đặt tên là text_box, khi đó ta có thể lấy giá trị texttrong ô Edit Control như sau:
CString text;
text_box.GetWindowTextW(text);
Giá trị lấy được sẽ được lưu trong một chuỗi CString text Một điểm cần chú ý
về sau là các hàm trong MFC khi được build ở chế độ Unicode và chế độ Multi-bytethì các gọi các hàm, cách sử dụng các hàm cũng có những điểm khác nhau nho nhỏ,chẳng hạn như cũng là lấy giá trị trong một ô Edit Control nhưng nếu ở chế độMulti-byte ta sẽ dùnglệnh sau:
CString text;
text_box.GetWindowTextA(text);
Ta có thể tùy chỉnh để trình dịch build theo chế độ Unicode hoặc Multi-byte
bằng cáchvào Project -> Properties (hoặc nhấn Alt + F7), hộp thoại