103
Nguyễn Văn Long Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV Tác giả: Nguyễn Văn Long – [email protected] Page 1 ng dng xnh trong thc thế với thư vin OpenCV C/C++ Nguyn Văn Long [email protected]

Ung dung xu ly anh trong thuc te voi thu vien open cv

Embed Size (px)

Citation preview

Page 1: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 1

Ứng dụng xử lý ảnh trong thực thế với thưviện OpenCV C/C++

Nguyễn Văn [email protected]

Page 2: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 2

Mở đầu

Xử lý ảnh và thị giác máy là lĩnh vực mà ngày nay được phát triển và ứng dụng rất rộngrãi trong nhiều lĩnh vực khác nhau nhờ vào sự phát triển ngày càng mạnh mẽ của các hệthống máy tính, các thuật toán và công trình nghiên cứu khác nhau của nhiều nhà khoahọc trên thế giới.Ở Việt Nam, các ứng dụng về xử ảnh đã bư ớc đầu được triển khai trên một số lĩnh vựcnhư lắp đặt hệ thống nhận dạng biển biển số xe ở các bãi đổ xe, hệ thống nhận dạng vântay chấm công ở các công sở … môn học xử lý ảnh ở các trường đại học được xem làmôn học bắt buộc ở một số ngành như công nghệ thông tin, điện tử viễn thông …Tuynhiên nhìn một cách khách quan thì số lượng các ứng dụng được triển khai trên thực tế làquá ít ỏi, lĩnh vực này sẽ còn phát triển mạnh mẽ trong tương lai nếu như được quan tâmmột cách nghiêm túc.Xuất phát từ thực tế rằng môn học xử lý ảnh ở các trường đại học là một môn học mangnặng tính học thuật, khô khan, các vấn đề được mô tả dưới dạng toán học, sinh viên nắmbắt môn học một cách chung chung mà không đi vào bản chất vấn đề, ứng dụng thực tiễncủa môn học, thêm vào đó số lượng tài liệu về chuyên ngành này bằng tiếng Việt là khôngnhiều, bằng quá trình nghiên cứu nghiêm túc, kinh nghiệm thực tế tác giả đã cố gắng chora đời cuốn sách Ứng dụng xử lý ảnh trong thưc tế với thư viện OpenCV.Cuốn đề cập tới một số phần của lĩnh vực xử lý ảnh và thị giác máy, thông qua sự diễngiải trực quan, không xa vào những công thức toán học trừu tượng, phức tạp nhưng vẫnlàm nổi bật nên được vấn đề, giúp người đọc có được cái nhìn tổng quát, hiểu được kháiniệm và hơn nữa biết được những vấn đề đó ứng dụng vào thực tế như thế nào. Các chủđề trong cuôn sách này đều đi kèm với một chương trình mô phỏng được viết bằng ngônngữ C++ với sự giúp đỡ của thư viện OpenCV, một thư viện mã nguông mở được đánhgiá là mạnh mẽ về tốc độ xử lý đáp ứng được các ứng dụng trong thời gian thực.Cuốn sách được chia thành bốn phần, phần đầu giới thiệu về thư viện OpenCV, phần thứhai nói về một số vấn đề chọn lọc thường gặp trong xử lý ảnh như không gian màu, cácbộ lọc, cách phát hiện đường thẳng đường tròn trong ảnh …, phần thứ ba nói về một sốthủ thuật để lập trình với thư viện MFC và phần cuối cùng nói về một số ứng dụng thực tếnhư bài toán nhận dạng biển số xe …Cuốn sách không chỉ là tài liệu tham khảo bổ ích trong quá trình học tập của các bạn sinhviên, quá trình làm luận văn, đồ án … mà còn là công cụ tốt hỗ trợ cho việc triển khai cácứng dụng thương mại của các kĩ sư, doanh nghi ệp và những người quan tâm tới lĩnh vực.Cuối cùng dù đã dành nhiều tâm huyết để hoàn thành cuốn sách nhưng chắc chắn cuốn

sách vẫn còn nhiều sai xót, tác giả mong được sự góp ý của bạn đọc. Xin gửi lời chúc tốttốt đẹp và lời cảm ơn sâu sắc tới độc giả

Page 3: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 3

Hướng dẫn sử dụng sáchCuốn sách được viết dựa trên những nghiên cứu và quá trình làm việc thực tế của tác giả,với mỗi vấn đề nêu trong sách bạn đọc có thể đọc qua để nắm bắt được ý tưởng chính, sauđó có thể tìm thêm tài liệu để nâng cao hơn vấn đề và có thể thực hành dựa vào mẫuchương trình, source code đi kèm.

Thư viện OpenCV được viết trong sách là bản OpenCV 2.4.3, đối với các bản OpenCVkhác thì bạn đọc có thể tùy chỉnh lại một chút tuy nhiên về bản chất của vấn đề là tươngđối giống nhau. Ngôn ngữ lập trình cho các ví dụ là C/C++, IDE sử dụng là Visual Studio2010. Tuy nhiên đa số chương trình trong cuốn sách này đều được tách biệt phần xử lýchính ra vào một file *.cpp nào đó nên ta có thể lấy nó để áp dụng vào các trình dịchkhác.

Có 10 chủ đề chính bao quát một số khía cạnh của lĩnh vực xử lý ảnh được viết khá chitiết và giải thích đầy đủ, 3 project được tác giả mô tả chung chung hơn. Do đó bạn đọcnếu chưa thực sự quen với thư viện OpenCV nên đọc theo thứ tự từ đầu tới cuối

Trong cuốn sách có nhiều vấn đề liên quan tới kĩ thuật lập trình nhưng do phạm vi giớihạn, tác giả chỉ có thể nói qua được một số khía cạnh, trên thực tế có nhiều cách khácnhau để giải quyết cùng một công việc, với những vấn đề lập trình bạn đọc chưa rõ có thểtham khảo thêm tài ở các nguồn khác nhau hoặc giải quyết theo hướng mà bạn đọc cảmthấy là thỏa đáng nhất

Page 4: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 4

Mục Lục

Chương I. Làm quen với thư viện OpenCV

1. Giới thiệu về thư viện OpenCV 52. Phiên bản OpenCV 1 hay OpenCV 2 53. Hướng dẫn sử dụng OpenCV trên Window 6

Chương II. Các phép xử lý đơn giản trong OpenCV

1. Chương trình đầu tiên 122. Không gian màu, chuyển đổi không gian màu 133. Điều chỉnh độ sang, độ tương phản 174. Ảnh nhị phân, nhị phân hóa với ngưỡng động 195. Histogram, cân bằng histogram 236. Phóng to, thu nhỏ, xoay ảnh 277. Lọc số trong ảnh 308. Các phép toán hình thái học trong ảnh 379. Tìm biên ảnh với bộ lọc Canny 4310. Chuyển đổi Hough, Phát hiện đường thẳng, đường tròn trong ảnh 46

Chương III. Lập trình xử lý ảnh với giao diện MFC

1. Giới thiệu về MFC 512. Khởi tạo project MFC 513. Làm việc với các điều khiển (Control) 544. Chuyển đổi các kiểu dữ liệu trong MFC 595. Chương trình tải ảnh và hiển thị ảnh lên giao diện MFC 61

Chương IV. Một số ứng dụng trong thực tế

1. My Photo Editor, phần mềm chỉnh sửa ảnh đơn giản 642. Nhận dạng biển số xe 733. MyCam, một số hiệu ứng ảnh trong video 90

Page 5: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 5

Chương I. Làm quen với thư viện OpenCV

1. Giới thiệu về thư viện OpenCV

OpenCV (Open Source Computer Vision) là một thư viện mã nguồn mở về thị giác máyvớ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 được thiết kế một cách tối ưu, sử dụng tối đa sứcmạ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. OpenCVlà 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ệnOpenCV tuân theo các quy định về sử dụng phần mềm mã nguồn mở BSD do đó bạn 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ươngmại.

Dự án về OpenCV được khởi động từ những năm 1999, đến năm 2000 nó được giới thiệutrong 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ênbản OpenCV 1.0 mãi tới tận nă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ảntrướ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ẫnlà thư viện mở, được phát triển bởi nguồn quỹ không lợi nhuận (none -profit foundation)và được sự hưởng ứng rất lớn của cộng đồng.

2. Phiên bản OpenCV 1 hay 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ện này thành hai bảnchính dựa trên những đặc điểm khác biệt lớn nhất của chúng: phiên bản OpenCV thế hệthứ nhất (hay còn gọi là phiên bản OpenCV 1.x) và phiên bản OpenCV thứ hai (hay còngọi là phiên bản OpenCV 2.x). Sau đây ta sẽ chỉ ra một số điểm khác biệt cơ bản giữa haiphiê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, 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.

Page 6: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 6

- 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ểuhơ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 ta hãyxé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ác bước để thực hiện là load một ảnh mầu, chuyển sang ảnh nhị phân, tìm biêndựa trên bộ lọc canny và phát hiện đường tròn dựa trên thuật toán Hough. OpenCV1.x thực hiện như sau:

// Phát hiện đường tròn trong ảnh OpenCV 1.xIplImage* 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);

Trong khi đó, OpenCV 2.x thực hiện như sau:

// Phát hiện đường tròn trong ảnh OpenCV 1.xMat src = imread(“image.jpg”);Mat gray;CvtColor(src, gray, CV_BGR2GRAY);Canny(gray, gray, 10, 30, 3);Vector<Vec3f> circles;HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 50, 100, 50);

Ta thấy rằng đố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ũngkhô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, tuy nhiênnó vẫn ở dạng sơ khai. Thư viện OpenCV 2.x đã được bổ xung khá nhiều hà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 đốitượng (detection), nhận dạng đối tượng (partten regconition) và theo dỗi đối tượng(tracking). Hơn thế nữa, tuy có giao diện là C++ nhưng OpenCV 2.x vẫn dữ mộtphầ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 ta 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ợp như ở các hệthống nhúng khi mà trình dịch chỉ đơn thuần chấp nhận ngôn ngữ C thì phiển bản 1.x vẫncòn giá trị. Trong cuốn sách này, các nội dung cài đặt, thuật toán, ứng dụng … chỉ dànhcho OpenCV phiên bản 2.x trên nền tảng hệ điều hành Window.

3. Hướng dẫn sử dụng thư viện OpenCV trên Window

Page 7: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 7

Trước hết ta cần download thư viện OpenCV về máy tính, tốt hơn là luôn download bảnmới nhất tại địa chỉ http://sourceforge.net/projects/opencvlibrary/ . Chọn bản đã build sẵnphù hợp với hệ điều hành đang dùng, bản OpenCV được sử dụng trong cuốn sách này làbản 2.4.3 với lần update cuối cùng là vào ngày 25 tháng 12 năm 2012. Sau khi downloadvề máy, tiến hành cài đặt bình thường, ta để 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 ta sẽ tiến hành tùy chỉnh để có thể làm việc vớiOpenCV qua hai IDE thông dụng là Microsoft Visual Studio và Eclipse CDT

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ảntrước ta 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++, Win32console application. Đặt tên project là opencv

Chọn OK, sau đó nhấn Next, hộp thoại tiếp theo xuất hiện, ở hộp thoại này ta chọnApplication type là Console application và Additional option là Empty project, nhấnFinish để kết thúc quá trình khởi tạo

Page 8: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 8

Project mới được tạo ra là project hoàn toàn trống, ta phải thêm vào đó ít nhất một filenguồn để chương trình có thể chạyđược, trong Solution Explorer taclick chuột phải vào Source Files,chọn Add -> New Item… Hộp thoạiAdd New Item hiện ra, ta chọn kiểucần thêm vào là C++ File(.cpp) đồngthời trong ô Name ta đặt tên cho filethêm vào, giả sử là FirstApp.cpp.Bây giở trong file này ta có thể thêmvào các #include và gọi hàm main()để chạy chương trình.

Để chương trình có thể chạy đượcvới thư viện OpenCV ta cần tùy chỉnh lại một sô thuộc tính của project như sau

Vào Project -> Properties (hoặc nhấn tổ hợp phím Alt + F7) để mở hộp thoại Properties.Hộp thoại opencv Property Pages hiện ra, trong mục Configuration Properties chọnVC++ Directories, tương ứng bên phải, ta tìm mục Include Directories và LibraryDirectories. 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\includeMục Library Directories trỏ đến thư mục C:\opencv\build\x86\vc10\lib nếu như ta sửdụng hệ điều hành 32bit hoặc C:\opencv\build\x64\vc10\lib cho hệ điều hành 64bit.

Page 9: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 9

Tiếp theo, trong hộp thoại opencv Property Pages -> Configuration Properties -> Linkerchọn Input, tương ứng ở ô bên phải, thêm vào các giá trị cho mục AdditionalDependencies là opencv_core243d.lib, opencv_imgproc243d.lib, opencv_highgui243d.lib.

Chú ý làcác lib thêm vào sẽ tương ứng với cácheader ta khai báo trong chương trình, vàtùy thuộc vào mục đích sử dụng mà ta cóthể thêm vào các lib các nhau, giả sử ta cầnsử dụng tới các hàm về video, khi đó tathêm header #include<opencv2/video/video.hpp> thì trong phầnAdditional Dependencies ta phải khai báothêm opencv_video243d.lib. Chữ d đứngcuối các file trên thể hiện ta đang hoạtđộng ở chế độ debug, ta có thể thêm các libkhông có chữ “d” ở cuối nhưopencv_core243.lib … trong chế độrelease. Tuy nhiên khi đang còn học tập và cần nhiều chỉnh sửa ta nên để ở chế độ debug.

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 nhất là 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 bithoặ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ầncopy luôn file tbb_debug.dll (trong chế độ debug) hoặc tbb.dll (trong chế độ release) vàothư 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ác library 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.

#include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp>#include <opencv2/highgui/highgui.hpp>#include <iostream>

using namespace std;using namespace cv;

void main(){

...}

Page 10: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 10

Với Eclipse CDT

Khởi động Eclipse, Từ cửa sổ Eclipse chọn New -> C++ Project , hộp thoại C++ Projectxuất hiện, trong hộp thoại ta chọn Project name là opencv, Project type là Hello WorldC++ Project (Có thể chọn là EmptyProject), Toolchains là MinGW GCC,Chọn Finish và ta có một Project mới.Bây giờ tùy chỉnh cho project này hoạtđộng được với OpenCV.Trong cửa sổ của Eclipse chọn Project ->Properties, cửa sổ Properties hiện ra.Tron cửa sổ Properties chọn C/C++Build->Settings. Trong tab Tool Settings.Ở phần GCC C++ Compiller chọnInclude rồi dẫn đường dẫn tới mụcInclude của OpenCVlà C:\opencv\build\include. Trong phầnMinGW C++ Linker chọn Library vàchọn các mục như sau: click vào dấucộng ở Library search path (-L) và dẫntới thư mụclib: C:\opencv\build\x86\mingw\lib đốivới Windows 32 bit hoặcC:\opencv\build\x64\mingw\lib đối vớiWindows 64 bit. Tiếp đó click vào dấu "cộng" để thêm Library(-I) vào, các library cầnthêm lần lượt là: opencv_core243, opencv_highgui243, opencv_imgproc243 ... nói chunglà tùy vào nhu cầu sử dụng có thể thêm một hoặc nhiều lib vào.

Page 11: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 11

Ta cũng cần phải copy các *.dll tương ứng vào thư mục chứa file chạy *.execủa chươngtrình để chương trình có thể chạy thành công.

Page 12: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 12

Chương II. Các phép xử lý ảnh và ứng dụng cơ bản

1. Chương trình đầu tiên

Trong bài này ta sẽ tìm hiểu một ví dụ đầu tiên như một chương trình Hello world để loadvà hiển thị một ảnh. Chương trình như sau:

#include "stdafx.h"#include <iostream>#include <opencv2\highgui\highgui.hpp>#include <opencv2\core\core.hpp>

using namespace std;using namespace cv;

int main(){

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 biến img.Nguyên mẫu của hàm này như sau: cv::Mat imread(const std::string &filename, int flags)trong đó, filename là đường dẫn tới file ảnh, nếu file ảnh không nằm trong thư mục làmviệc hiện hành thì ta phải chỉ ra đường dẫn tương đối dạng như D:\Anh\vietnam.jpg hoặcD://Anh//Vietnam.jpg. Flags là tham số loại ảnh mà ta muốn load vào, cụ thể nếu nếumuốn load ảnh mầu thì ta để CV_LOAD_IMAGE_COLOR, nếu là ảnh xám thì ta đểCV_LOAD_IMAGE_GRAYSCALE…Sau khi đã load ảnh thành công, muốn hiển thị ảnh lên màn hình ta phải 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ửasổ là một chuỗi string winname. Tham số flags sẽ chỉ ra kiểu cửa sổ muốn tạo: nếu thamsố 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áytính …Cuối cùng, hàm imshow(const std::string &winname, cv::InputArray Mat) sẽ hiển thị ảnhra 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ờigian là delay. Chú ý là nếu không có hàm này thì chương trình sau khi chạy sẽ khôngdừng lại màn hình và kết thúc luôn, ta dung hàm này mục đích là để dừng màn hình lại

Page 13: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 13

trong một khoảng thời gian bằng tham số delay (tính theo đơn vị millisecond). Nếu muốndừ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ả chương trình chay:

2. Không gian màu, chuyển đổi giữa các không gian màu

Khôn gian màu là một mô hình toán học dùng để mô tả các màu sắc trong thực tế đượcbiểu diễn dưới dạng số học. Trên thực tế có rất nhiều không gian màu khác nhau được môhình để sử dụng vào những mục đích khác nhau. Trong bài này ta sẽ tìm hiểu qua về bakhông gian màu cơ bản hay được nhắc tới và ứng dụng nhiều, đó là hệ không gian màuRGB, HSV và CMYK.Không gian màu RGBRGB là không gian màu rất phổ biến được dùng trong đồ họa máy tính và nhiều thiết bị kĩthuật số khác. Ý tưởng chính của không gian màu này là sự kết hợp của 3 màu sắc cơ bản: màu đỏ (R, Red), xanh lục (G, Green) và xanh lơ (B, Blue) để mô tả tất cả các màu sắckhác.Nếu như một ảnh số được mã hóa bằng 24bit, nghĩa là 8bit cho kênh R, 8bit cho kênh G,8bit cho kênh B, thì mỗ kênh này màu này sẽ nhận giá trị từ 0-255. Với mỗi giá trị khácnhau của các kênh màu kết hợp với nhau ta sẽ được một màu khác nhau, như vậy ta sẽ cótổng cộng 255x255x255 = 1.66 triệu màu sắc.

Page 14: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 14

Ví dụ: màu đen là sự kết hợp của các kênh màu (R, G, B)với giá trị tương ứng (0, 0, 0) màu trắng có giá trị (255,255, 255), màu vàng có giá trị (255, 255, 0), màu tím đậmcó giá trị (64, 0, 128) ...Nếu ta dùng 16bit để mã hóa mộtkênh màu (48bit cho toàn bộ 3 kênh màu) thì dãi màu sẽtrãi rộng lên tới 3*2^16 = ... Một con số rất lớn.

Không gian màu CMYK.

CMYK là không gian màu được sử dụng phổ biến trongngành công nghiệp in ấn.Ý tưởng cơ bản của hệ khônggian này là dùng 4 màu sắc cơ bản để phục vụ cho việc pha trộn mực in. Trên thực tế,người ta dùng 3 màu là C=Cyan: xanh lơ, M=Magenta: hồng xẫm, và Y=Yellow: vàng đểbiểu diễn các màu sắc khác nhau. Nếu lấy màu hồng xẫm cộng với vàng sẽ ra màu đỏ,màu xẫm kết hợp với xanh lơ sẽ cho xanh lam ... Sự kết hợp của 3 màu trên sẽ cho ra màuđen, tuy nhiên màu đen ở đây khôn phải là đen tuyệt đối và thường có độ tương phản lớn,nên trong ngành in, để tiết kiệm mực in người ta thêm vào màu đen để in những chi tiếtcó màu đen thay vì phải kết hợp 3 màu sắc trên. Và như vậy ta có hệ màu CMYK. chữ Kở đây là để kí hiệu màu đen (Black), có nhẽ chữ B đã được dùng để biểu diễn màu Bluenên người ta lấy chữ cái cuối K để biểu diễn màu đen?

Nguyên lý làm việc của hệ màu này như sau : Trên mộtnền giấy trắng, khi mỗi màu này được in lên sẽ loại bỏ dầnđi thành phần màu trắng. 3 màu C, M, Y khác nhau in theonhững tỉ lệ khác nhau sẽ loại bỏ đi thành phần đó một cáchkhác nhau và cuối cùng cho ta màu sắc cần in. Khi cần inmàu đen, thay vì phải in cả 3 màu người ta dùng màu đenđể in lên. Nguyên lý này khác với nguyên lý làm việc củahệ RGB ở chỗ hệ RGB là sự kết hợp của các thành phầnmàu, còn hệ CMYK là sự loại bỏ lẫn nhau của các thànhphần màu.

Không gian màu HSV.HSV và cũng gần tương tự như HSL là không gian màu được dùng nhiều trong việc ch ỉnhsữa ảnh, phân tích ảnh và một phần của lĩnh vực thị giác máy tính. Hệ không gian này dựavào 3 thông số sau để mô tả màu sắcH = Hue: màu sắc, S = Saturation: độ đậm đặc, sự bảo hòa, V = value: giá trị cường độsáng.Không gian màu này thường được biểu diễn dưới dạng hình trụ hoặc hình nón . Theo đó,

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 14

Ví dụ: màu đen là sự kết hợp của các kênh màu (R, G, B)với giá trị tương ứng (0, 0, 0) màu trắng có giá trị (255,255, 255), màu vàng có giá trị (255, 255, 0), màu tím đậmcó giá trị (64, 0, 128) ...Nếu ta dùng 16bit để mã hóa mộtkênh màu (48bit cho toàn bộ 3 kênh màu) thì dãi màu sẽtrãi rộng lên tới 3*2^16 = ... Một con số rất lớn.

Không gian màu CMYK.

CMYK là không gian màu được sử dụng phổ biến trongngành công nghiệp in ấn.Ý tưởng cơ bản của hệ khônggian này là dùng 4 màu sắc cơ bản để phục vụ cho việc pha trộn mực in. Trên thực tế,người ta dùng 3 màu là C=Cyan: xanh lơ, M=Magenta: hồng xẫm, và Y=Yellow: vàng đểbiểu diễn các màu sắc khác nhau. Nếu lấy màu hồng xẫm cộng với vàng sẽ ra màu đỏ,màu xẫm kết hợp với xanh lơ sẽ cho xanh lam ... Sự kết hợp của 3 màu trên sẽ cho ra màuđen, tuy nhiên màu đen ở đây khôn phải là đen tuyệt đối và thường có độ tương phản lớn,nên trong ngành in, để tiết kiệm mực in người ta thêm vào màu đen để in những chi tiếtcó màu đen thay vì phải kết hợp 3 màu sắc trên. Và như vậy ta có hệ màu CMYK. chữ Kở đây là để kí hiệu màu đen (Black), có nhẽ chữ B đã được dùng để biểu diễn màu Bluenên người ta lấy chữ cái cuối K để biểu diễn màu đen?

Nguyên lý làm việc của hệ màu này như sau : Trên mộtnền giấy trắng, khi mỗi màu này được in lên sẽ loại bỏ dầnđi thành phần màu trắng. 3 màu C, M, Y khác nhau in theonhững tỉ lệ khác nhau sẽ loại bỏ đi thành phần đó một cáchkhác nhau và cuối cùng cho ta màu sắc cần in. Khi cần inmàu đen, thay vì phải in cả 3 màu người ta dùng màu đenđể in lên. Nguyên lý này khác với nguyên lý làm việc củahệ RGB ở chỗ hệ RGB là sự kết hợp của các thành phầnmàu, còn hệ CMYK là sự loại bỏ lẫn nhau của các thànhphần màu.

Không gian màu HSV.HSV và cũng gần tương tự như HSL là không gian màu được dùng nhiều trong việc ch ỉnhsữa ảnh, phân tích ảnh và một phần của lĩnh vực thị giác máy tính. Hệ không gian này dựavào 3 thông số sau để mô tả màu sắcH = Hue: màu sắc, S = Saturation: độ đậm đặc, sự bảo hòa, V = value: giá trị cường độsáng.Không gian màu này thường được biểu diễn dưới dạng hình trụ hoặc hình nón . Theo đó,

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 14

Ví dụ: màu đen là sự kết hợp của các kênh màu (R, G, B)với giá trị tương ứng (0, 0, 0) màu trắng có giá trị (255,255, 255), màu vàng có giá trị (255, 255, 0), màu tím đậmcó giá trị (64, 0, 128) ...Nếu ta dùng 16bit để mã hóa mộtkênh màu (48bit cho toàn bộ 3 kênh màu) thì dãi màu sẽtrãi rộng lên tới 3*2^16 = ... Một con số rất lớn.

Không gian màu CMYK.

CMYK là không gian màu được sử dụng phổ biến trongngành công nghiệp in ấn.Ý tưởng cơ bản của hệ khônggian này là dùng 4 màu sắc cơ bản để phục vụ cho việc pha trộn mực in. Trên thực tế,người ta dùng 3 màu là C=Cyan: xanh lơ, M=Magenta: hồng xẫm, và Y=Yellow: vàng đểbiểu diễn các màu sắc khác nhau. Nếu lấy màu hồng xẫm cộng với vàng sẽ ra màu đỏ,màu xẫm kết hợp với xanh lơ sẽ cho xanh lam ... Sự kết hợp của 3 màu trên sẽ cho ra màuđen, tuy nhiên màu đen ở đây khôn phải là đen tuyệt đối và thường có độ tương phản lớn,nên trong ngành in, để tiết kiệm mực in người ta thêm vào màu đen để in những chi tiếtcó màu đen thay vì phải kết hợp 3 màu sắc trên. Và như vậy ta có hệ màu CMYK. chữ Kở đây là để kí hiệu màu đen (Black), có nhẽ chữ B đã được dùng để biểu diễn màu Bluenên người ta lấy chữ cái cuối K để biểu diễn màu đen?

Nguyên lý làm việc của hệ màu này như sau : Trên mộtnền giấy trắng, khi mỗi màu này được in lên sẽ loại bỏ dầnđi thành phần màu trắng. 3 màu C, M, Y khác nhau in theonhững tỉ lệ khác nhau sẽ loại bỏ đi thành phần đó một cáchkhác nhau và cuối cùng cho ta màu sắc cần in. Khi cần inmàu đen, thay vì phải in cả 3 màu người ta dùng màu đenđể in lên. Nguyên lý này khác với nguyên lý làm việc củahệ RGB ở chỗ hệ RGB là sự kết hợp của các thành phầnmàu, còn hệ CMYK là sự loại bỏ lẫn nhau của các thànhphần màu.

Không gian màu HSV.HSV và cũng gần tương tự như HSL là không gian màu được dùng nhiều trong việc ch ỉnhsữa ảnh, phân tích ảnh và một phần của lĩnh vực thị giác máy tính. Hệ không gian này dựavào 3 thông số sau để mô tả màu sắcH = Hue: màu sắc, S = Saturation: độ đậm đặc, sự bảo hòa, V = value: giá trị cường độsáng.Không gian màu này thường được biểu diễn dưới dạng hình trụ hoặc hình nón . Theo đó,

Page 15: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 15

đi theo vòng tròn từ 0 -360 độ là trường biểu diễn màu sắc(Hue). Trường này bắt đầu từmàu đỏ đầu tiên (red primary) tới màu xanh lục đầu tiên (green primary) nằm trongkhoảng 0-120 độ, từ 120 - 240 độ là màu xanh lục tới xanh lơ (green primary - blueprimary). Từ 240 - 360 là từ màu đen tới lại màu đỏ.

Không gian màu HSV Hình tròn biểu diễn màu sắc (HUE)

Chuyển đổi giữa các không gian màu.

Chuyển đổi RGB sang CMYK và ngược lại.Như đã nói ở trên, thành phần K là thành phần phụ dùng để in cho những điểm màu có

màu đen trong hệ CYMK, do vậy để chuyển không gian màu từ RGB sang CMYK trướchết ta chuyển RGB sang CMY sau đó tìm thành phần K còn lại. Cô ng thức chuyển từRGB sang CMY:(C', M', Y') = ((255 - R), (255 - G), (255 - B)).Việc tính giá trị của K lại là một vấn đề khác vì nó liên quan tới nhà sản xuất công nghệin, tuy nhiên về mặt lý thuyết có thể chấp nhận rằng K = min {C'/2,55, M'/2,55, Y'/2,55} ,như vậy 0<= K <=100.Nếu K = 100, thì C = M = Y =0 (trương hợp in màu đen)Nếu 0< K < 100: C = (C'/2.55 - K) * 100 /(100 - K), M = (M'/2.55 - K) * 100 /(100 - K),Y = (Y'/2.55 - K) *100 /(100 - K) và K = K. Trong đó, C, M, Y, K được làm tròn tới đểlấy chỉ số nguyên.Chuyển đổi RGB sang HSV và ngược lạiGiả sử ta có một điểm màu có giá trị trong hệ RGB là (R, G, B). ta chuyển sang khônggian HSV như sau:Đặt M = Max(R, G, B), m = Min(R, G, B) và C = M - m.Nếu M = R, H' = (G - B)/C mod 6. Nếu M = G, H' = (B - R)/C + 2. Nếu M = B, H' = (R -G)/C + 4. Và H = H'x60. Trong trường hợp C = 0, H = 00

V = M.

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 15

đi theo vòng tròn từ 0 -360 độ là trường biểu diễn màu sắc(Hue). Trường này bắt đầu từmàu đỏ đầu tiên (red primary) tới màu xanh lục đầu tiên (green primary) nằm trongkhoảng 0-120 độ, từ 120 - 240 độ là màu xanh lục tới xanh lơ (green primary - blueprimary). Từ 240 - 360 là từ màu đen tới lại màu đỏ.

Không gian màu HSV Hình tròn biểu diễn màu sắc (HUE)

Chuyển đổi giữa các không gian màu.

Chuyển đổi RGB sang CMYK và ngược lại.Như đã nói ở trên, thành phần K là thành phần phụ dùng để in cho những điểm màu có

màu đen trong hệ CYMK, do vậy để chuyển không gian màu từ RGB sang CMYK trướchết ta chuyển RGB sang CMY sau đó tìm thành phần K còn lại. Cô ng thức chuyển từRGB sang CMY:(C', M', Y') = ((255 - R), (255 - G), (255 - B)).Việc tính giá trị của K lại là một vấn đề khác vì nó liên quan tới nhà sản xuất công nghệin, tuy nhiên về mặt lý thuyết có thể chấp nhận rằng K = min {C'/2,55, M'/2,55, Y'/2,55} ,như vậy 0<= K <=100.Nếu K = 100, thì C = M = Y =0 (trương hợp in màu đen)Nếu 0< K < 100: C = (C'/2.55 - K) * 100 /(100 - K), M = (M'/2.55 - K) * 100 /(100 - K),Y = (Y'/2.55 - K) *100 /(100 - K) và K = K. Trong đó, C, M, Y, K được làm tròn tới đểlấy chỉ số nguyên.Chuyển đổi RGB sang HSV và ngược lạiGiả sử ta có một điểm màu có giá trị trong hệ RGB là (R, G, B). ta chuyển sang khônggian HSV như sau:Đặt M = Max(R, G, B), m = Min(R, G, B) và C = M - m.Nếu M = R, H' = (G - B)/C mod 6. Nếu M = G, H' = (B - R)/C + 2. Nếu M = B, H' = (R -G)/C + 4. Và H = H'x60. Trong trường hợp C = 0, H = 00

V = M.

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 15

đi theo vòng tròn từ 0 -360 độ là trường biểu diễn màu sắc(Hue). Trường này bắt đầu từmàu đỏ đầu tiên (red primary) tới màu xanh lục đầu tiên (green primary) nằm trongkhoảng 0-120 độ, từ 120 - 240 độ là màu xanh lục tới xanh lơ (green primary - blueprimary). Từ 240 - 360 là từ màu đen tới lại màu đỏ.

Không gian màu HSV Hình tròn biểu diễn màu sắc (HUE)

Chuyển đổi giữa các không gian màu.

Chuyển đổi RGB sang CMYK và ngược lại.Như đã nói ở trên, thành phần K là thành phần phụ dùng để in cho những điểm màu có

màu đen trong hệ CYMK, do vậy để chuyển không gian màu từ RGB sang CMYK trướchết ta chuyển RGB sang CMY sau đó tìm thành phần K còn lại. Cô ng thức chuyển từRGB sang CMY:(C', M', Y') = ((255 - R), (255 - G), (255 - B)).Việc tính giá trị của K lại là một vấn đề khác vì nó liên quan tới nhà sản xuất công nghệin, tuy nhiên về mặt lý thuyết có thể chấp nhận rằng K = min {C'/2,55, M'/2,55, Y'/2,55} ,như vậy 0<= K <=100.Nếu K = 100, thì C = M = Y =0 (trương hợp in màu đen)Nếu 0< K < 100: C = (C'/2.55 - K) * 100 /(100 - K), M = (M'/2.55 - K) * 100 /(100 - K),Y = (Y'/2.55 - K) *100 /(100 - K) và K = K. Trong đó, C, M, Y, K được làm tròn tới đểlấy chỉ số nguyên.Chuyển đổi RGB sang HSV và ngược lạiGiả sử ta có một điểm màu có giá trị trong hệ RGB là (R, G, B). ta chuyển sang khônggian HSV như sau:Đặt M = Max(R, G, B), m = Min(R, G, B) và C = M - m.Nếu M = R, H' = (G - B)/C mod 6. Nếu M = G, H' = (B - R)/C + 2. Nếu M = B, H' = (R -G)/C + 4. Và H = H'x60. Trong trường hợp C = 0, H = 00

V = M.

Page 16: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 16

S = C/V. Trong trường hợp V hoặc C bằng 0, S = 0.Để chuyển từ HSV sang RGB ta làm như sau:Giả sử ta có không gian màu HSV với H = [0, 360], S = [0, 1], V = [0, 1]. Khi đó, ta tínhC = VxS. H' = H/60.X = C(1 - |H' mod2 -1|). Ta biểu diễn hệ (R1, G1, B1) như sau:

( 1, 1, 1) =⎩⎪⎪⎨⎪⎪⎧(0, 0, 0) ế ℎư đượ á đị ℎ( , , 0) ế 0 ≤ < 1( , , 0) ế 1 ≤ < 2(0, , ) ế 2 ≤ < 3(0, , ) ế 3 ≤ < 4( , 0, ) ế 4 ≤ < 5, 0, ) ế 5 ≤ < 6

Chương trình chuyển đổi các không gian màu

Trong OpenCV, các không gian màu được được chuyển đổi qua lại nhờ hàm cvtColor(convert color), nguyên mẫu hàm này như sau:

cv::cvtColor(cv::InputArray src, cv::OutputArray dst, int code)Trong đó, src, dst là ảnh gốc và ảnh thu được sau khi chuyển đổi không gian màu. codemà mã chuyển đổi không gian màu. OpenCV định nghĩa khá nhiều chuyển đổi giữa cáckhông gian màu chẳng hạn như code = CV_BGR2GRAY sẽ chuyển ảnh ở không gian màuRGB sang ảnh xám, code = CV_HSV2BGR sẽ chuyển ảnh ở không gian màu HSV sangkhông gian màu RGB …

#include "stdafx.h"#include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp>#include <opencv2/highgui/highgui.hpp>

using namespace cv;

void main(){

Mat src = imread("LucBinh.jpg", CV_LOAD_IMAGE_COLOR);Mat gray, hsv, ycrcb;cvtColor(src, gray, CV_BGR2GRAY);cvtColor(src, hsv, CV_BGR2HSV);cvtColor(src, ycrcb, CV_BGR2YCrCb);imshow("src", src);imshow("gray", gray);imshow("hsv", hsv);imshow("ycrcb", ycrcb);waitKey(0);

Page 17: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 17

}

Sau đây là hình ảnh khi chuyển đổi các không gian màu trên

3. Điều chỉnh độ sang và độ tương phản trong ảnh

Trong bài này ta sẽ tìm hiểu về cấu trúc của một bức ảnh, cách tiếp cận và điều chỉnh tớitừng điểm ảnh.Một ảnh số được lưu trữ trên máy tính là một ma trận các điểm ảnh (hay pixel). TrongOpenCV nó được biểu diễn dưới dạng cv::Mat. Ta xét một kiểu ảnh thông thường nhất,đó là ảnh RGB. Với ảnh này, mỗi pixel ảnh quan sát được là sự kết hợp của các thànhphần màu R (Red), Green (Green) và Blue (Blue). Sự kết hợp này theo những tỉ lệ R, G,B khác nhau sẽ tạo ra vô số các màu sắc khác nhau. Giả sử ảnh được mã hóa bằng 8 bitvới từng kênh màu, khi đó mỗi giá trị của R, G, B sẽ nằm trong khoảng [0, 255]. Như vậyta có thể biểu diễn tới 255*255*255 ~ 1.6 triệu màu sắc từ ba màu cơ bản trên. Ta có thểxem cách biểu diễn ảnh trong OpenCV ở định dạng cv::Mat qua hình ảnh sau:

Page 18: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 18

Cột 0 Cột 1 Cột mHàng 0 0, 0 0, 0 0, 0 0, 1 0, 1 0, 1 0, m 0, m 0, mHàng 1 1, 0 1, 0 1, 0 1, 1 1, 1 1, 1 1, m 1, m 1, mHàng 2 2, 0 2, 0 2, 0 2, 1 2, 1 2, 1 2, m 2, m 2, mHàng n n, 0 n, 0 n, 0 N, 1 n, 1 n, 1 n, m n, m n, m

Như vậy, mỗi ảnh sẽ có n hàng và m cột, m gọi là chiều dài của ảnh (width) và n gọi làchiều cao của ảnh (heigh). Mỗi pixel ở vị trí (i,j) trong ảnh sẽ tương ứng với 3 kênh màukết hợp trong nó. Để truy xuất tới từng pixel ảnh với những kênh màu riêng rẽ ta sử dụngmẫu sau:

img.at<cv::Vec3b>(i,j)[k]Trong đó, i ,j là pixel ở hàng thứ i và cột thứ j, img là ảnh mà ta cần truy xuất tới các pixelcủa nó. cv::Vec3b là kiểu vector uchar 3 thành phần, dung để biểu thị 3 kênh màu tươngứng. k là kênh màu thứ k, k = 0, 1, 2 tương ứng với kênh màu B, G, R. Chú ý là trongOpenCV, hệ màu RGB được biểu diễn theo thứ tự chữ cái là BGR.Sau đây ta sẽ áp dụng kiến thức trên để làm tăng, giảm độ sang và tương phản của mộtảnh màu, việc làm này cũng hoàn toàn tương tự đối với ảnh xám, chỉ khác biệt là ảnh tadung một kênh duy nhất để biểu diễn ảnh xám.Chương trình tăng, giảm độ sáng và độ tương phản của một ảnhGiả sử f là một hàm biểu diễn cho một ảnh nào đó, f(x,y) là giá trị của pixel trong ảnh ở vịtrí (x,y). Đặt g(x,y) = αf(x,y) + β. Khi đó, nếu α ≠ 1, thì ta nói ảnh g(x,y) có độ tương phảngấp α lần so với ảnh f(x,y). Nếu β ≠ 0ta nói độ sáng của ảnh g(x,y) đã thay đổi một lượnglà β. Dựa vào công thức trên ta có chương trình thay đổi độ sáng và tương phản của ảnhnhư sau:

#include "stdafx.h"#include <iostream>#include <opencv2\core\core.hpp>#include <opencv2\highgui\highgui.hpp>

using namespace std;using namespace cv;

int main(){

cout<<"Chuong trinh dieu chinh do sang va tuong phan"<<endl;Mat src = imread("hoa_huong_duong.jpg", 1);Mat dst = src.clone();double alpha = 2.0;int beta = 30;for(int i = 0; i < src.rows; i++)

for(int j = 0; j < src.cols; j++)for(int k = 0; k < 3; k++)

Page 19: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 19

dst.at<Vec3b>(i,j)[k] = saturate_cast<uchar>(alpha*(src.at<Vec3b>(i,j)[k] ) + beta);

imshow("anh goc", src);imshow("anh co sau khi chinh do tuong phan va do sang", dst);waitKey(0);return 0;

}

Trong chương trình trên, hàm clone() sẽ sao chép một ảnh giống hệt như ảnd gốc cho vàoảnh đích (dst = src.clone()). Giá trị của các pixel ảnh f(x,y) và g(x,y) ở đây phải nằm trongkhoảng [0, 255], trong khi phép biến đổi g(x,y) = αf(x,y) + β có thể khiến cho giá trị củag(x,y) vượt qua ngưỡng đó. Để tránh tình trạng tràn số hoặc kiểu dữ liệu không tươngthích, ta dùng thêm hàm saturate_cast<uchar>(type). Hàm này sẽ biến kiểu dữ liệutype nếu không phải là uchar thành kiểu dữ liệu ucharSau đây là kết quả chương trình với giá trị α = 2.0 và β = 30

4. Ả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 hai giá trị 0hoặ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) > THình sau mô tả một ảnh nhị phân với ngưỡng nhị phân T = 100

Page 20: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 20

Ảnh xám Ảnh nhị phân

Hàm để chuyển nhị phân hóa ảnh trong OpenCV là hàm threshold(). Nguyên mẫu hàmnhư 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ôngthường, nhị phân hóa ngược và nhị phân hóa theo thuậ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ỗingưỡ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.

T = 50 T = 100 T = 150

Để 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ángkhác nhau (không cần quan tâm tới ngưỡng T), người ta dùng một kĩ thuật sao cho vớimọ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ĩ

Page 21: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 21

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ựatrên ý tưởng chính là chia ảnh ra thành những vùng nhỏ, với mỗi vùng áp dụng việc nhịphân cho vùng đó với những ngưỡng nhị phân khác nhau.Các ngưỡng nhị phân ở cácvùng được tính toán dựa trên độ lớn mức xám của chính các pixel trên vùng đó. Giả sử tatí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 đó(ta có thể xem một vùng là một cửa sổ). Ta xét quá trình nhị phân với ngưỡng động trongmột vùng cửa sổ 5x5:

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ìnhcộ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

// Adaptive Threshold#include <opencv2\core\core.hpp>#include <opencv2\highgui\highgui.hpp>#include <opencv2\imgproc\imgproc.hpp>

#include <iostream>

using namespace std;using namespace cv;

int main(){

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);

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 21

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ựatrên ý tưởng chính là chia ảnh ra thành những vùng nhỏ, với mỗi vùng áp dụng việc nhịphân cho vùng đó với những ngưỡng nhị phân khác nhau.Các ngưỡng nhị phân ở cácvùng được tính toán dựa trên độ lớn mức xám của chính các pixel trên vùng đó. Giả sử tatí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 đó(ta có thể xem một vùng là một cửa sổ). Ta xét quá trình nhị phân với ngưỡng động trongmột vùng cửa sổ 5x5:

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ìnhcộ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

// Adaptive Threshold#include <opencv2\core\core.hpp>#include <opencv2\highgui\highgui.hpp>#include <opencv2\imgproc\imgproc.hpp>

#include <iostream>

using namespace std;using namespace cv;

int main(){

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);

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 21

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ựatrên ý tưởng chính là chia ảnh ra thành những vùng nhỏ, với mỗi vùng áp dụng việc nhịphân cho vùng đó với những ngưỡng nhị phân khác nhau.Các ngưỡng nhị phân ở cácvùng được tính toán dựa trên độ lớn mức xám của chính các pixel trên vùng đó. Giả sử tatí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 đó(ta có thể xem một vùng là một cửa sổ). Ta xét quá trình nhị phân với ngưỡng động trongmột vùng cửa sổ 5x5:

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ìnhcộ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

// Adaptive Threshold#include <opencv2\core\core.hpp>#include <opencv2\highgui\highgui.hpp>#include <opencv2\imgproc\imgproc.hpp>

#include <iostream>

using namespace std;using namespace cv;

int main(){

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);

Page 22: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 22

return 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àmadaptiveThreshold, 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ớingưỡ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ínhtoán ngưỡng động, và C là một thông số để bù trừ trong trường hợp ảnh có độ tương phảnquá lớn.Kết quả khi chạy chương trình như sau:

Ảnh xám Ảnh nhị phân

Page 23: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 23

5. Histogram, cân bằng histogram trong ảnh

Histogram của một ảnh là một biểu đồ nói lênmối quan hệ giữa các giá trị của pixel ảnh (điểmảnh) và tần suất xuất hiện của chúng. Nhìn vàobiểu đồ histogram ta có thể đoán được một ảnhsáng tối như thế nào.Nếu một ảnh có histogram lệch về phía phảibiểu đồ, ta nói ảnh đó thừa sáng. Nếu lệch vềphía trái thì ảnh đó thiếu sáng. Hình bên mô tảhistogram của một ảnh xám, ảnh này cóhistogram lệch về phía trái của biểu đồ và do đóảnh này là khá tối. Đối với ảnh màu, ta có thểtính toán histogram cho từng kênh màu một.Sau đây là chương trình tính và vẽ biểu đồhistogram của một ảnh màu.

#include <iostream>#include <opencv2\highgui\highgui.hpp>#include <opencv2\imgproc\imgproc.hpp>

using namespace std;using namespace cv;

int main(){

std::cout<<"Tim histogram anh mau"<<std::endl;

Mat src = imread("buoi.jpg");vector<Mat> img_rgb;Mat img_r, img_g, img_b;int w = 400, h = 400;int size_hist = 255;float range[] = {0, 255};const float* hist_range = {range};

split(src, img_rgb);calcHist(&img_rgb[0], 1, 0, Mat(), img_b, 1, &size_hist, &hist_range, true, false);calcHist(&img_rgb[1], 1, 0, Mat(), img_g, 1, &size_hist, &hist_range, true, false);calcHist(&img_rgb[2], 1, 0, Mat(), img_r, 1, &size_hist, &hist_range, true, false);

int bin = cvRound((double)w/size_hist);

Mat disp_r(w, h, CV_8UC3, Scalar( 255,255,255) );Mat disp_g = disp_r.clone();Mat disp_b = disp_r.clone();

normalize(img_b, img_r, 0, disp_b.rows, NORM_MINMAX, -1, Mat() );normalize(img_g, img_g, 0, disp_g.rows, NORM_MINMAX, -1, Mat() );normalize(img_r, img_b, 0, disp_r.rows, NORM_MINMAX, -1, Mat() );

Page 24: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 24

for( int i = 1; i < 255; i++ ){line(disp_r, Point(bin*(i), h), Point(bin*(i), h - cvRound(img_r.at<float>(i))),

Scalar(0, 0, 255), 2, 8, 0 );line(disp_g, Point(bin*(i), h), Point(bin*(i), h - cvRound(img_g.at<float>(i))),

Scalar( 0, 255, 0), 2, 8, 0 );line(disp_b, Point( bin*(i), h), Point(bin*(i), h - cvRound(img_b.at<float>(i))),

Scalar(255, 0, 0), 2, 8, 0 );}namedWindow("src", 0);imshow("src", src);imshow("Histogram of Blue chennel", disp_b);imshow("Histogram of Green chennel", disp_g);imshow("Histogram of Red chennel", disp_r);

cv::waitKey(0);return 1;

}

Trong chương trình trên, đầu tiên ảnh được đưa vào là một ảnh mầu, để tính histogramcủa từng kênh mầu một ta sẽ phân tách ảnh này thành 3 kênh mầu riêng rẽ theo thứ tự làBlue, Green và Red. Hàm cv::split(const cv::Mat src, cv::Mat *mvbegin) sẽ tách thànhsrc thành các kênh màu tương ứng lưu trong mvbegin.Hàm cv:: calcHist(const cv::Mat *images, int nimages, const int *channels,cv::InputArray mask, cv::OutputArray hist, int dims, const int histSize, const float**ranges, bool uniform = true, bool accumulate = false) sẽ tính histogram của ảnh đầuvào images và lưu kết quả tính toán vào mảng hist. Các thông số khác bao gồm: nimageslà số lượng ảnh đầu vào, channels là danh sách chiều các kênh dung để tính histogram,mask là một mặt nạ tùy chỉnh, nếu không dung gì thì để là cv::Mat(). dims là chiều củahistogram, bản OpenCV hiện tại hỗ trợ tính toán histogram với số chiều lên tới 32.histSize là kích thước dãy histogram mỗi chiều, hai tham số cuối có thể để mặc định,Sau đây là kết quả chương trình

Page 25: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 25

Đối với ảnh xám, ta có thể xem như nó là một kênh màu, do vậy để tính histogram củaảnh xám ta cũng có thể làm hoàn toàn tương tự bằng cách gọi hàm caclHist()Cân bằng histogramCân bằng histogram (histogram equalization) làphương pháp làm cho biểu đồ histogram của ảnh đượcphân bố một cách đồng đều. Đây là một biến đổi kháquan trọng giúp nâng cao chất lượng ảnh, thôngthường đây là bước tiền xử lý của một ảnh đầu vàocho các bước tiếp theo.Để cân bằng histogram, ta dung hàmequalizeHist(cv::InputArray src, cv::OuputArray dst)trong đó, src là ảnh đầu vào một kênh màu (ảnh xámchẳn hạn), dst là ảnh sau khi cân bằng. Ví dụ:

Mat src = imread("src.jpeg", CV_LOAD_IMAGE_GRAYSCALE); // Load anh xamimshow("Anh xam goc", src);Mat dst;equalizeHist(src, dst);imshow("anh xam sau khi can bang histogram", dst);

Ta có kết quả sau:

Page 26: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 26

Để cân bằng histogram của một ảnh màu, trước hết ta chuyển ảnh màu ở dạng RGB sangHSV, sau đó cân bằng thành phần kênh màu V (Value tức độ sáng) và biến đổi ngược lại.Chương trình sau cân bằng histogram của ảnh màu

// Can bang histogram

#include <opencv2\core\core.hpp>#include <opencv2\highgui\highgui.hpp>#include <opencv2\imgproc\imgproc.hpp>

#include <iostream>

using namespace std;using namespace cv;

int main(){

cout<<"Chuong trinh can bang histogram"<<endl;Mat src = imread("Cho_Ben_Thanh.jpg", CV_LOAD_IMAGE_COLOR);

Mat hsv, disp;cvtColor(src, hsv, CV_BGR2HSV);vector<Mat> hsv_channels;// Tach hsv thanh 3 kenh mausplit(hsv, hsv_channels);// Can bang histogram kenh mau v (value)equalizeHist(hsv_channels[2], hsv_channels[2]);// Tron anhmerge(hsv_channels, hsv);// Chuyen doi hsv sang rgb de hien thicvtColor(hsv, disp, CV_HSV2BGR);imshow("anh mau goc", src);imshow("anh mau sau khi can bang histogram", disp);waitKey(0);

}

Sau đây là kết quả của chương trình trên

Page 27: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 27

6. Phóng to, thu nhỏ và xoay ảnh

Như đã nói ở trên, ảnh số thực chất là một ma trận các điểm ảnh, do đó để có thể phóngto, thu nhỏ hay xoay một tấm ảnh ta có thể sử dụng các thuật toán tương ứng trên ma trận.

Ta sẽ sử dụng biển đổi affine để quay và thay đổi tỉ lệ to, nhỏ của một ma trận.

Biến đổi affine:

Giả sử ta có vector = [ , ] và ma trận M 2x2. Phép biển đổi affine trong không gianhai chiều có thể được định nghĩa p’ = Mp trong đó = [ , ] . Viết một cách tườngminh ta có: ′′ =Hay x’ = αx + δy, y’ = γx + βy .Xét ma trận = . Nếu δ = γ = 0, khi đó x’ = αx và y’ = βy, phép biến đổi này làm

thay đổi tỉ lệ của ma trận. Nếu là trong ảnh nó sẽ phóng to hoặc thu nhỏ ảnh. Hình sau môtả phép biến đổi với tỉ lệ α = β = 2

Page 28: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 28

Nếu ta định nghĩa ma trận = cos ( ) −sin ( )sin ( ) cos ( ) thì phép biến sẽ quay p thành p’ với

góc quay là θ.

Nếu ma trận M được định nghĩa thành = α. cos (θ) −sin ( )sin ( ) . cos ( ) thì phép biến đổi sẽ

vừa là phép biến đổi theo tỉ lệ và quay.

Bây giờ ta sẽ xét chương trình phóng to, thu nhỏ và quay ảnh.

// Chuong trinh phong to, thu nho va quay anh

#include <opencv2\core\core.hpp>#include <opencv2\highgui\highgui.hpp>#include <opencv2\imgproc\imgproc.hpp>

using namespace cv;

int main(){

Mat src = imread("HoaSen.jpg");Mat dst = src.clone();

double angle = 45.0;

Page 29: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 29

double scale = 1.5;Point2f center(src.cols/2, src.rows/2);

Mat mat_rot = getRotationMatrix2D(center, angle, scale);warpAffine(src, dst, mat_rot, src.size());imshow("Anh goc", src);imshow("Anh sau phep bien doi", dst);waitKey(0);

return 1;}

Trong chương trình trên, hàm cv::getRotationMatrix2D(cv::Point center, doubleangle, double scale) sẽ tạo ra ma trận với tâm quay center, góc quay angle và tỉ lệscale. Ma trận này được tính toán trong Opencv là ma trận như sau:

= (1 − ). . − . .− . . − (1 − ). .Với = . cos ( ) và = . sin ( )Ta thấy rằng ma trận này là hoàn hoàn tương đương với ma trận của phép biến đổi affineđã nói ở trên, ngoại trừ thành phần thứ 3 là thành phần giúp dịch chuyển tâm quay vàochính giữa của bức ảnh. Chú ý là có sự khác biệt một chút về chiều của hệ tọa độ trongảnh, hệ tọa độ trong ảnh lấy góc trên bên trái làm gốc tọa độ (0,0) còn hệ tọa độ thôngthường ta hay lấy điểm dưới bên trái làm gốc, do đó có sự ngược chiều.

Kết quả của chương trình trên với tỉ lệ scale = 1.5 và góc quay = 450 như hình sau

Ngoài hai phép biến đổi là tỉ lệ và quay như trên, ta có thể thực hiện các biến đổi khác củaphép biến đổi affine như phép trượt (shearing), hoặc phép phản chiếu (reflection) bằng

Page 30: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 30

việc định nghĩa lại ma trận M. Ta thử định nghĩa lại ma trận M để được một ảnh trượt củaảnh gốc. Quay lại ma trận M như trên, nếu ta định nghĩa α = β = 1 còn δ nh ận một giá trịbất kì, khi đó ta sẽ có:= += ta sẽ định nghĩa ma

trận = 1 0.5 00 1 0 để trượt ảnh

ban đầu thành ảnh mới với hệ số trượt

δ = 0.5, chú ý là thành phần thứ ba 00định nghĩa ma trận trong Opencv sẽthể hiện dộ dịch chuyển, giống như trong ví dụ trên ta chuyển tâm quay về tâm của bứcảnh chẳng hạn. Ta sẽ làm giống hệt ví dụ trên, chỉ thay ma trận M là ma trận ta tự địnhnghĩa

double I[2][3] = {1,0.5,0, 0,1,0}; // cac phan tu cua ma tranMat mat_rot(2,3,CV_64F, I); // khoi tao ma tranwarpAffine(src, dst, mat_rot, src.size());

và ta có kết quả:

7. Lọc số trong ảnh

Lọc số trong ảnh có ý nghĩa quan trọng trong việc tạo ra các hiệu ứng trong ảnh, một sốhiệu ứng nhờ sử dụng các bộ lọc như làm mờ ảnh(Blur), làm trơn ảnh(Smooth) …Nguyên tắc chung của phương pháp lọc ảnh là cho ảnh nhân chập với một ma trận lọc,

Idst = M*Isrc

Page 31: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 31

Isrc, Idst là ảnh gốc và ảnh sau khi thực hiện phép lọc ảnh bằng cách nhân với ma trận lọcM. Ma trận M đôi khi còn gọi là mặt nạ (mask), nhân (kernel). Với mỗi phép lọc ta cónhững ma trận lọc M khác nhau, không có quy định cụ thể nào cho việc xác định M, tuynhiên ma trận này có một số đặc điểm như sau:

- Kích thước của ma trận thường là một số lẻ chẳng hạn 3x3, 5x5 … Khi đó, tâm củama trận sẽ nằm ở giao của hai đường chéo và là điểm áp đặt lên ảnh mà ta cần tínhnhân chập.

- Tổng các phần tử trong ma trận thông thường bằng 1. Nếu tổng này lớn hơn 1, ảnhqua phép lọc sẽ có độ sáng lớn hơn ảnh ban đầu. Ngược lại ảnh thu được sẽ tối hơnảnh ban đầu.

Ví dụ về ma trận lọc (ma trận lọc Sobel theo hướng x, y và ma trận Gausian Blur)

−1 0 +1−2 0 +2−1 0 +1 −1 −2 −10 0 0+1 +2 +1 ⎣⎢⎢⎢⎡1 4 7 4 14 16 26 16 47 26 41 26 74 16 26 16 41 4 7 4 1⎦⎥⎥

⎥⎤Công thức cụ thể cho việc lọc ảnh như sau:

( , ) = ( , ) ∗ ( , ) = ( − , − ) × ( , )Trong đó, ta đang tính phép nhân chập cho điểm ảnh có tọa độ (x,y) và vì ta lấy tâm củama trận lọc là điểm gốc nên u chạy từ -n (điểm bên trái) và v chạy từ -n (điểm phía trên)đến n, với n = (kích thước mặt nạ - 1)/2. Để dễ hiểu hơn ta xét một ví dụ về việc làm trơnnhờ sử dụng một ma trận lọc như sau:

1/9 1/9 1/9

1/9 1/9 1/9

1/9 1/9 1/9

100 100 100 100 100

100 200 205 203 100

100 195 200 200 100

100 200 205 195 100

100 100 100 100 100

100 100 100 100 100

100 144 205 203 100

100 195 200 200 100

100 200 205 195 100

100 100 100 100 100

* =

nhân chập

pixel (2,2)

Page 32: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 32

1/9 1/9 1/9

1/9 1/9 1/9

1/9 1/9 1/9

……

Và kết quả cuối cùng ta có:

1/9 1/9 1/9

1/9 1/9 1/9

1/9 1/9 1/9

Ta thấy rằng, ảnh ban đầu với là ảnh có độ tương phản khá lớn (các giá trị độ lớn pixelchênh lệch lớn: 100, 200, …), sau phép lọc ảnh có độ tương phản giản đi hay bị làm mờđi(lúc này độ chênh lệch giá trị giữa các pixel giảm đi: 100, 144, 167 …). Sau đây ta sẽxem xét một số bộ lọc trong OpenCV.

Lọc Blur:

100 100 100 100 100

100 200 205 203 100

100 195 200 200 100

100 200 205 195 100

100 100 100 100 100

100 100 100 100 100

100 144 167 203 100

100 195 200 200 100

100 200 205 195 100

100 100 100 100 100

100 100 100 100 100

100 200 205 203 100

100 195 200 200 100

100 200 205 195 100

100 100 100 100 100

100 100 100 100 100

100 144 167 145 100

100 167 200 168 100

100 144 166 144 100

100 100 100 100 100

* =

nhân chập

pixel (2,3)

* =

nhân chập

Page 33: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 33

Ma trận lọc của phương pháp này có dạng : = ∗ ⎣⎢⎢⎢⎡1 1 . . 11 1 . . 1. . . . .. . . . .1 1 . . 1⎦⎥⎥

⎥⎤Bộ lọc này có tác dụng làm trơn ảnh, khử nhiễu hạt.Hàm cài đặt trong OpenCV có dạng:cv::blur(const Mat& src, const Mat& dst, Size ksize, Point anchor, int borderType)trog đó, src và dst là ảnh gốc và ảnh sau phép lọc. ksize là kích thước ma trận lọc,ksize = Size(rows, cols), anchor là điểm neo của ma trận lọc, nếu để mặc định là (-1,-1) thì điểm này chính là tâm của ma trận. borderType là phương pháp để ướclượng và căn chỉnh các điểm ảnh nếu qua phép lọc chúng bị vượt ra khỏi giới hạncủa ảnh. Thông thường giá trị mặc định của nó là 4.Ảnh qua phép lọc blur

Lọc Sobel:Lọc sobel chính là cách tính xấp xỉ đạo hàm bậc nhất theo hướng x và y, nó cũngchính là cách tính gradient trong ảnh. Bộ lọc này thông thường được áp dụng chomục đích tìm biên trong ảnh. Ma trận lọc theo các hướng x, y lần lượt như sau:−1 0 +1−2 0 +2−1 0 +1 −1 −2 −10 0 0+1 +2 +1Trong OpeCV, hàm cài đặt phép lọc này như sau:cv::Sobel(const Mat& src, Mat& dst, int ddepth, int xorder, int yorder, int ksize,double scale, double delta, int borderType)Trong đó, src và dst là ảnh gốc và ảnh qua phép lọc. ddepth là độ sâu của ảnh sauphép lọc, có thể là CV_32F, CV_64F … . xoder và yoder là các đạo hàm theo

Page 34: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 34

hướng x và y, để tính đạo hàm theo hướng nào ta đặt giá trị đó lên 1, ngược lại nếugiá trị bằng 0, hàm cài đặt sẽ bỏ qua không tính theo hướng đó. Scale và delta làhai thông số tùy chọn cho việc tính giá trị đạo hàm lưa giá trị vi sai vào ảnh sauphép lọc, chúng có giá trị mặc định là 1 và 0. borderType là tham số như trên.Ảnh qua phép lọc Sobel:

Lọc Laplace:Lọc Laplace là cách tính xấp xỉ đạo hàm bậc hai trong ảnh, nó có ý nghĩa quantrọng trong việc tìm biên ảnh và phân tích, ước lượng chuyển động của vật thể.= ∆ = +Ma trận lọc có dạng như sau: 0 1 01 −4 10 1 0Trong OpenCV, bộ lọc này được cài đặt qua hàm:cv::Laplacian(const Mat& src, Mat& dst, int ddepth, int ksize=1, double scale=1,double delta=0, int borderType)Các thông số này có ý nghĩa giống như các thông số trong bộ lọc Sobel, chỉ khác ởchỗ ksize là một giá trị int mặc định bằng 1 và khi đó ma trận lọc laplace trên đượcáp dụng.Ảnh qua phép lọc Laplace:

Page 35: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 35

Ngoài 3 bộ lọc trên, OpenCV còn cài đặt khá nhiều bộ lọc khác như lọc trung vị(medianBlur), lọc Gause (gaussianBlur), pyrDown, pyrUp … Tuy nhiên, ta hoàn toàn cóthể cài đặt bộ lọc cho riêng mình thông qua hàm cv::filter2D. Nguyên mẫu hàm này nhưsau:

filter2D(const Mat& src, Mat& dst, int ddepth, const Mat& kernel, Point anchor, doubledelta, int borderType).

Trong đó, src và dst là ảnh gốc và ảnh thu được qua phép lọc, kernel là ma trân lọc.Thông số anchor để chỉ ra tâm của ma trận, delta điều chỉnh độ sáng của ảnh sau phép lọc(ảnh sau phép lọc được cộng với delta và borderType là kiểu xác định những pixel nằmngoài vùng ảnh.

Hàm cv::filter2D thực chất là hàm tính toán nhân chập giữa ảnh gốc và ma trận lọc để chora ảnh cuối sau phép lọc. Như vậy qua trên ta thấy rằng để tiến hành việc lọc ảnh ta chỉcần định nghĩa một ma trận lọc kernel. Có rất nhiều nghiên cứu về toán học đã định nghĩacác ma trận này, do đó việc áp dụng vào lọc ảnh sẽ là khá đơn giản.

Chương trình demo phương pháp lọc ảnh.

Chương trình sau sẽ thực hiện một số bộ lọc ảnh do chính ta định nghĩa, chẳng hạn ta có

ma trận lọc = −1 −1 0−1 0 10 1 1 , ma trận này là ma trận sẽ làm nổi bật ảnh giống như

ảnh khắc 3D, ta sẽ áp dụng ma trận này để lọc một ảnh.

#include "stdafx.h"#include <opencv2\core\core.hpp>#include <opencv2\imgproc\imgproc.hpp>#include <opencv2\highgui\highgui.hpp>

Page 36: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 36

using namespace cv;

void main(){

Mat src = imread("HoaDao.jpg", CV_LOAD_IMAGE_COLOR);Mat dst;double m[3][3] = { -1, -1, 0,

-1, 0, 1,0, 1, 1

};Mat M = cv::Mat(3,3,CV_64F, m, Point(-1,-1), 128.0);cv::filter2D(src, dst, src.depth(), M);imshow("Anh goc", src);imshow("Anh qua phep loc", dst);cv::waitKey(0);

}

Kết quả của chương trình như sau:

Một số bộ lọc sau có thể hữu ích cho việc tạo ra các hiệu ứng ảnh đẹp mắt:

= 1 1 11 −7 11 1 1 , = 0

Page 37: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 37

= −1 −1 −1−1 8 −1−1 −1 −1 , = 0

= −1 −1 −1−1 8 −1−1 −1 −1 , = 0

8. 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 bao gồm phép giãn nở(dialation) phép co (erosion), phép mở (opening) và phép đóng (closing).

Phép toán giãn nở (dilation)

Phép toán giãn nở được định nghĩa ⊕ = ⋃ ớ ⊂ trong đó, A là đối tượngtrong ả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ượngban đầu trong ảnh tăng lên về kích thước (giản nở ra)

Page 38: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 38

Cấu trúc phần tử ảnh (image structuring element) là một hình khối được định nghĩa sẵnnhằ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ấutrúc phần tử hay gặp là cấu trúc theo khối hình vuông và hình chữ thập

Ta hãy 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 được giảnnở ra và phần giản nở ra ta đánh dấu là dấu x.

x x xxx

Ứ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ới những đoạn rời nhỏ …

Phép toán co (erosion)

Phép toán co trong ảnh được định nghĩa ⊖ = { |( ) ⊆ } trong đó A là đối tượngtrong ả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 co trongả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ông thỏa mãn.

Page 39: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 39

0

x

Ta 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 đối tượng gần nhau vàlàm mảnh, tìm xương đối tượng.

Phép toán mở (opening) và đóng (closing) là sự kết hợ của phép co (erosion) và giản(dialation) như trên, chúng được định nghĩa như sau:

Phép toán mở : ∘ = ( ⊖ ) ⊕Phép toán đóng: ∙ = ( ⊕ ) ⊖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đối tượng trong ảnh trở lên mượt mà hơn.

Phép toán đóng được ứng dụng trong việc làm trơn đường bao đối tượng, lấp đầy cáckhoảng trống trên biên và loại bỏ những hố nhỏ (một số pixel đứng thà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àmcv::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)

Page 40: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 40

Trong đó, src, dst là ảnh đầu vào và ảnh sau phép xử lý hình thái học. op là kiểu lựa chọnphé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ốihì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ĩamột ma trận với các hình khối tương ứng hoặc sử dụng hàm getStructuringElement, hàmnày có cấu trúc như sau: getStructuringElement(int shape, Size ksize, Point anchor), vớishape 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à khí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ĩatương tự. iterations là số lần lặp lạ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,nó có ý nghĩa như các ví dụ trong bài trước.

Phép toán về giản nở và co có thể được gọi từ hàm cv::morphologyEx thông qua hai đốisố 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ép giả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ại cá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ời giản nở các đối tượng vậtthể trong ảnh.

Sau đây 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ệcphá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ướctiên ta nhị 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áccạnh chiều dài, rộng của hình chữ nhật bao quanh đường bao đó (dĩ nhiên đây là một cáchtiếp cận rất đơn giản!). Tuy nhiên, ta chỉ xác định được đường bao quanh đối tượng khitậ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ều trường hợp củabiển số, việc bị mất một vài điểm ảnh trong quá trình nhị phân trên biên là chuyện thườngxuyên xảy ra và do đó sẽ khó xác định được một đường bao quanh biển số. Để khắc phục

Page 41: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 41

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àmerode để làm co lại nền ảnh) để các điểm ảnh trên biên trở lên liền lơn.

#include "stdafx.h"#include <opencv2/core/core.hpp>#include <opencv2/highgui/highgui.hpp>#include <opencv2/imgproc/imgproc.hpp>

using namespace cv;using namespace std;

void main(){

Mat src1 = imread("BienSo.jpg", CV_LOAD_IMAGE_COLOR);Mat src2 = src1.clone(); // copy anhMat 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);

vector<vector<Point> > contours1;findContours(binary, contours1, CV_RETR_LIST, CV_CHAIN_APPROX_NONE );for(size_t i = 0; i < contours1.size(); i++){

Rect r = boundingRect(contours1[i]);if(r.width/(double)r.height > 3.5f && r.width/(double)r.height <

4.5f)rectangle(src1, r, Scalar(0, 0, 255), 2, 8, 0);

elserectangle(src1, r, Scalar(0, 255, 0), 1, 8, 0);

}imshow("Ket qua phat hien truoc phep gian no", src1);

vector<vector<Point> > contours2;findContours(morpho, contours2, CV_RETR_LIST, CV_CHAIN_APPROX_NONE );

for(size_t i = 0; i < contours2.size(); i++){

Rect r = boundingRect(contours2[i]);if(r.width/(double)r.height > 3.5f && r.width/(double)r.height <

4.5f)rectangle(src2, r, Scalar(0, 0, 255), 2, 8, 0);

elserectangle(src2, r, Scalar(0, 255, 0), 1, 8, 0);

}

Page 42: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 42

imshow("Ket qua phat hien sau khi phep gian no", src2);

waitKey(0);}

Trong chương trình trên, ta lần lượt thực hiện tìm biên với hai ảnh nhị phân, một ảnh nhịphân thông thường và một ảnh nhị phân đã được làm giản nở 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àmfindContours. 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ể xemxé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 tasẽ 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

Ta thấy rằng, điểm đứt trong ảnh được nối liền nhờ sự giản nở của biển cạnh biển số (sựco lại của nền ảnh) nên ta tìm được một hình bao khép kín quanh biển số, tuy nhiên tacũng nhận thấy rằng khi các đối tượng vật thể trong ảnh giản nở ra, các kí tự sẽ có xuhướng dính vào nhau và việc tách các kí tự ra là khó khăn, chẳng hạn trên hình do có đinhvit ở giữ mà số 2 và số 9 gần như nối liền. Đây là lúc ta cần thực hiện việc co lại của cácđối tượng (sự giản nở của nền ảnh), và do đó bạn đọc hoàn toàn có thể cài đặt để tách racác kí tự này.

Page 43: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 43

9. Tìm biên ảnh dựa trên bộ lọc Canny

Bộ lọc Canny là sự kết hợp của nhiều bước khác nhau để tìm và tối ưu đường biên, kếtquả là cho ra một đường biên khá mảnh và chính xác. Quá trình tìm biên sử dụng phươngpháp canny có thể được thực hiện qua 4 bước sau:

Bước 1: Loại bớt nhiễu trong ảnh.

Người ta loại nhiễu trong ảnh, làm cho ảnh mờ đi bằng cách nhân chập ảnh với một bộlọc Gause, chẳng hạn bộ lọc Gaus 5x5 với hệ số σ = 1.4:

= 1159 ⎣⎢⎢⎢⎡2 4 5 4 24 9 12 9 45 12 15 12 54 9 12 9 42 4 5 4 2⎦⎥⎥

⎥⎤Bước 2: Tính toán giá trị gradient trong ảnh

Vì đường biên trong ảnh là nơi phân cách giữa các đối tượng khác nhau, nên tại đógradient của nó sẽ biến đổi mạnh mẽ nhất. Để tính toán gradient trong ảnh, ta có thể sửdụng bộ lọc Sobel, hoặc trực tiếp nhâp chập ma trận ảnh với các mặ nạ theo hướng x và y

chẳng hạn = −1 0 +1−2 0 +2−1 0 +1 , = −1 −2 −10 0 0+1 +2 +1Sau đó tính độ lớn gradient trong ảnh := + và =Trong đó, Gx, Gy chính là đạo hàm theo hướng X, Y của điểm ảnh ta đang xét. Góc θ sẽđược làm tròn theo các hướng thẳng đứng, nằm ngang và hướng chéo, nghĩa là nó sẽ đượclàm tròn để nhận các giá trị 0, 45, 90 và 135 độ.

Bước 3: Loại bỏ các giá trị không phải là cực đại

Bước này sẽ tìm ra những điểm ảnh có khả năng là biên ảnh nhất bằng cách loại bỏ đinhững giá trị không phải là cực đại trong bước tìm gradient ảnh ở trên. Ta thấy rằng, vớigiá trị của góc θ ở trên thì biên của đối tượng có thể tuân theo bốn hướng, và ta có bốnkhả năng sau:

Page 44: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 44

4. Nếu θ = 00, khi đó, điểm A sẽ được xem làmột điểm trên biên nếu độ lớn gradient tại Alớn hơn độ lớn gradient của các điểm tại A3,A7.

5. Nếu θ = 450, khi đó, điểm A sẽ được xem làmột điểm trên biên nếu độ lớn gradient tại Alớn hơn độ lớn gradient của các điểm tại A4,A8

6. Nếu θ = 900, khi đó, điểm A sẽ được xem làmột điểm trên biên nếu độ lớn gradient tại Alớn hơn độ lớn gradient của các điểm tại A1,A5.

7. Nếu θ = 1350, khi đó, điểm A sẽ được xem là một điểm trên biên nếu độ lớngradient tại A lớn hơn độ lớn gradient của các điểm tại A2, A6

Bước 4: Chọn ra biên của đối tượng trong ảnh

Sau bước trên, ta thu được tập các điểm tương ứng trên đường biên khá mỏng. Vì nhữngđiểm có giá trị gradient lớn bao giờ cũng có xác suất là biên thật sự hơn những điểm cógiá trị gradient bé, đo đó để xác định chính xác hơn nữa biên của các đối tượng, ta sửdụng các ngưỡng. Theo đó, bộ lọc canny sẽ sử dụng một ngưỡng trên (upper threshold) vàmột ngưỡng dưới (lower threshold), nếu gradient tại một điểm trong ảnh có giá trị lớn hơnngưỡng trên thì ta xác nhận đó là một điểm biên trong ảnh, nếu giá trị này bé hơn ngưỡngdưới thì đó không phải điểm biên. Trong trường hợp giá trị gradient nằm giữa ngưỡngtrên và ngưỡng dưới thì nó chỉ được tính là điểm trên biên khi các điểm liên kết bên cạnhcủa nó có giá trị gradient lớn hơn ngưỡng trên.

Tìm biên ảnh bằng bộ lọc canny trong OpenCV:

OpenCV cung cấp một hàm cho ta xác định biên trong ảnh của các đối tượng. Nguyênmẫu của hàm này như sau :

canny(Mat src, Mat dst, int lower_threshold, int upper_threshold, int kernel_size)

Trong đó, src là ảnh cần phát hiện biên (là ảnh xám), dst là ảnh chứa biên được phát hiện,lower_threshold, upper_threshold là ngưỡng dưới, ngưỡng trên như đã nói bên trên,kernel_size là kích thước của mặt nạ dùng cho bước thứ hai để tính toán gradient của cácđiểm trong ảnh.

Chương trình demo tìm biên trong ảnh dựa trên bộ lọc canny:

Page 45: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 45

#include "stdafx.h"#include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp>#include <opencv2/highgui/highgui.hpp>

using namespace cv;

void main(){

Mat gray = cv::imread("TuoiTho.jpg", CV_LOAD_IMAGE_GRAYSCALE);Mat dst1, dst2;imshow("Anh xam", gray);GaussianBlur(gray, gray, Size(9,9), 2);double t1 = 30, t2 = 200;Canny(gray, dst1, t1, t2, 3, false);t1 = 100; t2 = 120;Canny(gray, dst2, t1, t2, 3, false);imshow("Bien trong anh voi nguong 1", dst1);imshow("Bien trong anh voi nguong 2", dst2);waitKey(0);

}

Trong chương trình trên, trước khi tìm biên bằng phương pháp canny ta đã làm trơn mộtảnh xám bằng bộ lọc GaussianBlur (việc làm này có ý rất lớn trong việc giảm thiểu cáctiểu tiết không mong muốn trong đường biên sau này). Về nguyên tắc, bộ lọcGaussianBlur cũng là một bộ lọc số như đã giới thiệu ở bài trước về lọc số trong ảnh, cấutrúc của hàm GaussianBlur như sau:

cv::GaussianBlur(cv::InputArray src, cv::OutputArray dst, cv::Size ksize, double sigmaX,double sigmaY = 0, int borderType = 4), trong đó src và dst sẽ là các mảng dữ liệu đầuvào và kết quả. Một ảnh cũng có thể coi là một mảng dữ liệu, đo đó src và dst chính làảnh đầu và ảnh thu được sau khi biến đổi, ksize là kích thước của ma trận lọc, sigmaX,sigmaY là độ lệch chuẩn theo hướng x và y. Nếu sigmaY = 0, độ lệch chuẩn theo hướng ysẽ được lấy theo sigmaX. BorderType là cách nội suy biên đối với những điểm ảnh tràn rangoài kích thước của ảnh.

Kết quả thu được sau các ngưỡng t1, t2 khác nhau, ta được những biên có độ chi tiết khácnhau như hình sau.

Page 46: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 46

10. Chuyển đổi Hough, Phát hiện đường thẳng, đường tròn trong ảnh

Chuyển đổi Hough (Hough transformation) là một phương pháp được dùng nhiều trongphân tích và xử lý ảnh, mục đích chính của phương pháp này là tìm ra những hình dángđặc trưng trong ảnh bằng cách chuyển đổi không gian ảnh ban đầu sang một không giancủa các tham số nhằm đơn giản quá trình tính toán, trong bài này ta xét chuyển đổi Houghcho đường thẳng và đường tròn.

Page 47: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 47

Chuyển đổi Hough cho đường thẳng.

Ta đã biết rằng, một đường thẳng trong không gian hai chiều có thể được biểu diễn dướidạng y = kx + m và cặp hệ số góc k, giá trị m có thể được chọn để làm đặc trưng cho mộtđường thẳng. Tuy nhiên, cách biểu diễn theo cặp (k, m) khó thỏa mãn với những đườngthẳng thẳng đứng khi mà hệ số góc là một số vô cùng. Để tránh trường hợp này, ta sẽ biểudiễn đường thẳng trong hệ tọa độ cực.

Phương trình đường thẳng trong hệ tọa độ cựccó dạng như sau:= ( ) + ( )Trong đó, r là khoảng cách từ gốc tọa độ O tớiđường thẳng, θ là góc cực.

Như vậy, với mỗi điểm (x0, y0) ta có một họcác đường thẳng đi qua thõa mãn phương trình= ( ) + ( )Phương trình này biểu diễn một đường cong, như vậy trong một tấm ảnh có n điểm (npixel) ta sẽ có n các đường cong. Nếu đường cong của các điểm khác nhau giao nhau, thìcác điểm này cùng thuộc về một đường thẳng.Bằng cách tính các giao điểm này, ta sẽ xác địnhđược đường thẳng, đó là nội dung ý tưởng củathuật toán Hough cho đường thẳng.

Chuyển đổi Hough cho đường tròn

Chuyển đổi Hough cho đường tròn cũng tươngtự như với đường thẳng, phương trình đườngtròn được xác định bởi:= += +Trong đó, (u,v) là tâm đường tròn, R là bán kính đường tròn, θ là góc có giá trị từ 0 tới360 độ. Một đường tròn sẽ hoàn toàn được xác định nếu ta biết được bộ ba thông số(u,v,R). Từ phương trình trên ta có thể chuyển đổi tương đương= −= −

Page 48: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 48

Ta xét với trường hợp đã biết trước giá trị của R. Khi đó, với mỗi điểm ảnh (x,y) ta sẽ xácđịnh được một giá trị (u,v) và lưu nó vào một mảng. Tâm của đường tròn sẽ là giá trị xuấthiện trong mảng với tần suất lớn nhất. Trong trường hợp R chưa biết, ta tăng giá trị của Rtừ một ngưỡng min tới ngưỡng max nào đó và tiến hành như với trường hợp đã biết trướcgiá trị R

Tìm đường thẳng, đường tròn trong ảnh.

Để tìm đường thẳng trong ảnh, thư viện OpenCV có hỗ trợ hàm cv::HoughLines vàcv::HoughLinesP, hai hàm này về cơ bản đều thực hiện một thuật toán, nhưng đưa ra kếtquả ở hai dạng khác nhau.

Dạng 1: đưa ra kết quả của đường thẳng là một cặp giá trị (r, θ)

cv::HoughLines(src, lines, rho, theta, threshold, param)

Trong đó, src là ảnh nhị phân chứa biên của các đối tượng cần được phát hiện đườngthẳng (trong thực tế ta có thể sử dụng là một ảnh xám), lines là vector chứa kết quả đầu racủa (r, θ) đã được phát hiện, rho là độ phân giải của r (tính theo pixel, thực chất đó làbước tăng nhỏ nhất của r để tính toán), theta là độ phân giải của góc θ (có giá trị từ 0 tới360 độ), threshold là giá trị nhỏ nhất của tổng các giao điểm của các đường cong để xácđịnh đường thẳng, param là một thông số mặc định của OpenCV chưa sử dụng đến (nó cógiá trị bằng 0)

Dạng 2: đưa ra kết quả là tọa độ điểm đầu và điểm cuối của đường thẳng

cv::HoughLinesP(src, lines, rho, theta, threshold, minLenght, maxGap)

Trong đó, các thông số src, rho, theta, threshold giống như dạng 1, lines là vector chứatọa độ điểm đầu và tọa độ điểm cuối của đường thẳng phát hiện được (x1, y1, x2, y2),minLength là độ dài nhỏ nhất để có thể xem nó là một đường thẳng (tính theo đơn vịpixel), maxGap là khoảng cách lớn nhất của hai điểm cạnh nhau để xác định chúng cóthuộc về một đường thẳng hay không (tính theo đơn vị pixel)

Đối với đường tròn, ta cũng áp dụng hàm tương tự: cv::HougCircles(), nguyên mẫu củahàm như sau:

cv::HougCircles(src, circles, method, dp, min_dist, param1, param2, min_radius,max_radius)

Trong đó, src có thể là một ảnh nhị phân chứa biên của đối tượng hoặc ảnh xám (chươngtrình sẽ tự động dò biên bằng phương pháp canny), circles là vector chứa kết quả phát

Page 49: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 49

hiện đường tròn với 3 tham số (a, b, R), method là phương pháp phát hiện đương tròn(hiện tại phương pháp trong OpenCV là CV_HOUGH_GRADIENT), dp là tỉ số nghịchđảo của độ phân giải (áp dụng trong phương pháp hiện tại), min_dist là khoảng cách nhỏnhất giữa hai tâm đường tròn phát hiện được, param1, param2 là các thông số ngưỡngtrên và ngưỡng dưới phục vụ cho việc phát hiện biên bằng phương pháp canny,min_radius và max_radius là giới hạn nhỏ nhất, lơn nhất của bán kính đường tròn ta cầnphát hiện(Nếu ta không biến bán kính, để mặc định hai giá trị này bằng không thì chươngtrình sẽ lần tăng giá trị bán kính một cách tự động để tìm ra tất cả các đường tròn trongảnh).

Trong đó : …

Chương trình tìm đường thẳng, đường tròn trong ảnh:

#include "stdafx.h"#include <opencv2\core\core.hpp>#include <opencv2\imgproc\imgproc.hpp>#include <opencv2\highgui\highgui.hpp>#include <iostream>

using namespace cv;using namespace std;

void main(){

Mat src = imread("DongHo.jpg", 1);Mat gray;cvtColor(src, gray, CV_BGR2GRAY);GaussianBlur(gray, gray, Size(9, 9), 2, 2 );

// Tim duong thangMat canny;Canny(gray, canny, 100, 200, 3, false);vector<Vec4i> lines;HoughLinesP(canny, lines, 1, CV_PI/180, 50, 60, 10);

// Tim duong tronvector<Vec3f> circles;HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 100, 200, 100, 0, 0);

// Ve duong thang, duong tron len anhfor(int i = 0; i < lines.size(); i++ ){

Vec4i l = lines[i];line(src, Point(l[0], l[1]), Point(l[2], l[3]), Scalar(0,0,255), 2);

}

Page 50: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 50

for(int i = 0; i < circles.size(); i++ ){

Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); // Tamint radius = cvRound(circles[i][2]); // Ban kinhcircle(src, center, radius, Scalar(0,0,255), 2, 8, 0 );

}

imshow("Anh sau khi tim thay duong thang - Duong tron", src);waitKey(0);

}

Trong chương trình trên, đầu tiên ảnh được tải vào một biến src, sau đó được chuyển đổiqua ảnh xám gray và được làm trơn đi nhờ hàm GaussianBlur. Đối với việc phát hiệnđường thẳng, ta cần phát hiện ra tập các điểm biên của đối tượng, do vậy ta sử dụng hàmcanny để cho ra ảnh nhị phân chứa tập các điểm biên. Trong trường hợp phát hiện đườngtròn, hàm HoughCircles đã ngầm tìm biên cho ra thông thông qua hai ngưỡng đặt sẵn(trong chương trình trên là 200 và 100) và do đó ta không cần phải thực hiện việc này.Kết quả thu được được vẽ lên ảnh gốc như hình sau.

Page 51: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 51

Chương III. Lập trình xử lý ảnh với giao diện MFC

1. Giới thiệu về MFC

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ất của thư viện này là cung cấp cho ta các lớp, cáccông để làm việc với các hàm API của Window, do vậy việc lập trình trở nên đơn giảnhơn rất nhiều.Trong phần này và phần sau, các ví dụ và ứng dụng sẽ được tạo ra nhờ vào giao diệnDialog của MFC. Việc nghiên cứu một cách đầy đủ và bài bản về MFC là một việc cầnđầu tư về thời gian và công sức. Trong khuôn khổ cuốn sách này, ta chỉ đi xem xét mộtphần nhỏ và các thủ thuật để làm việc nhanh nhất được với MFC.

2. Khở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, họn Visual C++ (có thể sẽ phải chọn mục Otherlangguge trước khi hiển ra Visual C++) sau đó chọn MFC Application. Ta đặt tên choproject 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

Page 52: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 52

Application type chọn Dialog based, Project type chọn MFC standard… Chú ý rằng việctick 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épta tùy chỉnh một số chức năng của cửa sổ như có thêm nút phóng to, thu nhỏ, menu… cóthể để mặc định và chọn next

Page 53: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 53

Ta 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õ ta 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ởitạo lớp. Ta chọn là CxyzDlg và nhấn Finish

Page 54: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 54

Đế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đinh có một button OK, button Cancel và một label, ta có thể để sử dụng hoặc xóa đi vàthiết kế theo ý riêng của mình.

3. Làm việc với các điều khiển (Control) của MFC

Đặt tên biến cho các controlKhi muốn sử dụng một Control nào, ta kéocontrol đó từ Toolbox và cho vào dialog. Đểlàm việc với các control một cách dễ dàng, tanê đặt tên biến cho các control.Để đặt tên biến cho một control, ta click chuộtphải vào control, sau đó chọn Add Variable.Một hộp thoại Add Member Variable Wizardhiện ra và trong mục variable name ta đặt têncho control đó. Chú ý là đối với các control màID của nó có dạng IDC_STATIC (như StaticText) thì ta chỉ có thể đặt tên biến được khi đổi

Page 55: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 55

ID của nó, chẳng hạn như đổi thành IDC_STATIC1, IDC_LABEL …

Lấy giá trị nhập vào từ một Edit ControlĐể lấy giá trịn 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ị text trong ô EditControl 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ú ý từ giờ vềsau là các hàm trong MFC khi được build ở chế độ Unicode và chế độ Multi-byte thì cácgọ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ạnnhư 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 Properties hiện ra, chọnConfiguration Properties -> General và tùy chỉnh ở mục Character Set.

Hiển thị text lên các controlĐể hiển thị text lên các control (Button, Edit control, Static Text ..), ta dùng hàmSetWindowText(CString text)text_box.SetWindowTextW(_T("text")); // Unicodetext_box.SetWindowTextA("text"); // Multi-byte

Page 56: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 56

Hiển thị ảnh lên một controlĐể hiển thị ảnh lên một control, ta dùng hàm SetBitmap(). Đoạn code sau load một ảnhbitmap từ ổ D và hiển thị lên một button có tên là btn:HBITMAP image = (HBITMAP)LoadImageA(0, "D:/test.bmp", IMAGE_BITMAP, 0, 0,LR_LOADFROMFILE);btn.SetBitmap(image);Enable, disable một controlTa sử dụng hàm EnableWindow để cho phép control có được phép sử dụng hay khôngtext_box.EnableWindow(flase); // Vo hieu hoa edit controltext_box.EnableWindow(true); // Cho phep edit control hoat dong

Lấy giá trị từ thanh trượt (Slider Control)Giả sử thanh trượt được đặt tên là slider, khi đó ta có thể lấy giá trị hiện tại của thanhtrượt bằng hàm GetPos() : int value = slider.GetPos();Ta cũng có thể dùng hàm SetRange để đặt giá trị lớn nhất và bé nhất cho thanh trượt, vàdùng hàm SetPos để đặt vị trí cho thanh trượt:slider.SetRange(0, 100); slider.SetPos(50);

Lấy giá trị lựa chọn từ Combo BoxCombo box cho phép ta lựa chọn, chuyển đổi giữa các lựa chọn một cách nhanh chóng.Để thêm các lựa chọn vào Combo Box ta có thể điển trực tiếp vào mục Data trongproperties của nó, (giả sử ta có các lựa chọn về tỉnh thành ở Việt Nam như HaNoi,ThanhHoa, DaNang …ta sẽ click chuột phải vào Combo box, chọn properties, trong bảngproperties ở mục Data ta điền vào HaNoi;ThanhHoa;DaNang… các lựa chọn được phâncách bởi dấu ";" ). Để xem lựa chọn nào đang được chọn, ta dùng hàm GetCurSel().int choice = combo_box.GetCurSel(); Giá trị trả về là thứ tự các dữ liệu trong mụcData của Combo box

Dialog mở file và lưu fileMục đích của loại dialog này là tạo ra một hộp thoại cho phép người dùng chọn đếnđường dẫn để mở file và lưu file. Kết quả cuối cùng mà ta quan tâm nhất là lấy đượcđường dẫn mà người dùng lựa chọn.CString Filter=_T("image files (*.bmp; *.jpg) |*.bmp;*.jpg|All Files(*.*)|*.*||");CFileDialog dlg(TRUE, _T("*.jpg"), NULL,OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,Filter,NULL);// Mo fileCFileDialog dlg(FALSE, _T("*.jpg"), NULL,OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,Filter,NULL);// Luu fileFilter sẽ lọc và chỉ hiển thị những file tương ứng mà ta cần quan tâm, trong trường hợptrên ta đang xét mở hoặc lưu một file ảnh do đó ta để file mở rộng là bmp hoặc jpg. Nếumuốn hiển thị tất cả các loại file ta chỉ việc để filter là *.*, hộp thoại mở file và lưu file

Page 57: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 57

chỉ khác nhau ở thông số đầu tiên khi tạo đối tượng dlg, nếu là mở file ta đặt là TRUE, lưufile ta đặt là FALSE.Ta hiển thị hộp thoại này và lấy đường dẫn người dùng chọn như sau:if(dlg.DoModal() == IDOK){

CString file_name = dlg.GetPathName();// lay duong dan day du

}

Xử lý sự kiện khi click chuột vào buttonHầu hết các control trong MFC đều có một hoặc nhiều sự kiện khi người dùng tương tácvới nó, chẳng hạn sự kiện click chuột vào button, sự kiện kéo thanh trượt của slidercontrol … Để xử lý các sự kiện cho các control, trong mục properties của control tươngứng ta chọn vào icon sự kiện sau đó chọn các sựkiện cần xử lý. Chẳng hạn đối với button, tachọn BN_CLICKED rồi chọnOnBnClickedButton1, khi đó ta sẽ có một hàmđược tự động sinh ra và ta có thể xử lý các vấnđề liên quan tới sự kiện click chuột. Hàm sau sẽsinh ra một Message Box khi click vào button1void CxyzDlg::OnBnClickedButton1(){

MessageBoxA(NULL, "Button1 duocclick", "Thong bao", 0);

}

Xử lý sự kiện khi thay đổi lựa chọn Combo BoxSự kiện thay đổi lựa chọn của Combo Box là CBN_SELENDOK. Đoạn code sau mô tả sựthay đổi lựa chọn của người dùng trên Combo Box, với mỗi lựa chọn ta sinh ra mộtMessage Box tương ứng.void CxyzDlg::OnCbnSelendokCombo1(){

int index = combo_box.GetCurSel();switch(index){case 0:

MessageBoxA(NULL, "Ban chon HaNoi", "Thong bao", 0);break;

case 1:MessageBoxA(NULL, "Ban chon ThanhHoa", "Thong bao", 0);break;

Page 58: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 58

case 2:MessageBoxA(NULL, "Ban chon DaNang", "Thong bao", 0);break;

default:MessageBoxA(NULL, "Khong chon?", "Thong bao", 0);break;

}

}

Thêm menu vào chương trình, xử lý sự kiện khi click vào menuMenu trong MFC được xem là resource của chương trình. Việc thêm menu vào dialog đòihỏi ta phải thêm nó vào resource của chương trình. Trước hết ta hiển thị cửa sổ xem cácresource của chương trình bằng cách từ menu của Visual Studio chọn View -> ResourceView (hoặc nhận tổ hợp phìm Ctrl + Shift + E). Cửa sổ Resource View hiện ra, ta clickchuột phải vào đó, chọn Add->Resource… Cửa sổ Add Resource hiện ra, ta chọn Menuvà click vào button New. Ngay sau đó ta sẽ có mộtresource chứa menu chống, ta tiến hành điền têncác menu mà ta muốn chương trình thực hiện. Hìnhbên ta điền 3 menu chưc năng nhỏ là Open, Save vàExit… Khi đã tạo xong menu, nó vẫn chỉ nằm ởtrong resource của chương trình, muốn menu nàyđược gắn vào dialog khi chạy, ta vào properties củadilog, trong mục Menu chọn IDR_MENU1, với IDR_MENU1 chính là ID của menu tavừa tạo ra.Để xử lý sự kiện click chuột của menu nào, ta click chuột phải vào mune ấy và chọn AddEvent Handler…

Page 59: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 59

Ở hộp thoại Event Handler Wizard ta chọn lớp mà ta muốn thêm menu vào sau đó clickvào button Add and Edit để đi tới hàm xử lý sự kiện khi click vào menu, ví dụ sau là tagọi hàm OnCancel() để thoát khỏi chương trình

void CxyzDlg::OnUpdateFileExit(CCmdUI *pCmdUI){

OnCancel();}

Ngoài một số điều khiển thông dụng đã nhắc tới ở trên, MFC còn cung cấp rất nhiều cácđiều khiển khác giúp cho việc tạo ra giao diện một cách dễ dàng và đẹp mắt hơn. Bạn đọctham khảo thêm các tài liệu khác về phần này.

4. Chuyển đổi các kiểu dữ liệu trong MFC

Các kiểu dữ liệu của MFC về cơ bản là giống với các kiểu dữ liệu trong C, tuy nhiên cómột số trường hợp ta phải chuyển đổi qua lại giữa các kiểu dữ liệu để phù hợp với đầuvào, đầu ra của một việc nào đó, chẳng hạn khi ta dùng CFileDialog để mở một đườngdẫn sau đó đọc ảnh từ đường dẫn này, kết quả trả về đường dẫn của CFileDialog là mộtchuỗi CString tuy nhiên hàm cv::imread lại đọc ảnh từ một chuỗi string, do đó ta phải

Page 60: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 60

chuyển đổi từ CString sang string. Một số chuyển đổi sau đây là hữu ích cho việc hiển thịgiao diện, và lấy dữ liệu từ giao diện người dùng.

Chuyển đổi string sang CString và ngược lạiVới chế độ biên dịch không sử dụng Unicode, ta có thể chuyển đổi hai kiểu dữ liệu này dễdàng:

8. Chuyển từ string sang CString:std::string str = "chuoi string";CString cstr = str.c_str();

9. Chuyển từ CString sang stringCString cstr = "chuoi cstring";std::string str = std::string(cstr);

với chế độ biên dịch có sử dụng Unicode:

10. Chuyển từ string sang CString:std::string str = "chuoi string";CStringW cstr = (CStringW)(str.c_str());

11. Chuyển từ CString sang stringCString cstr = _T("chuoi cstring");std::wstring wstr = (std::wstring)cstr;std::string str;str.assign(wstr.begin(), wstr.end());

Chuyển đổi số sang CString và ngược lạiCách chuyển đổi này giúp ta lấy số liệu dưới dạng số của các ô Edit Control hoặc hiển thịsố lên các control.

12. Chuyển đổi CString sang sốCString cstr1 = "123";int num1 = atoi(cstr1); // Chuyen sang so nguyen khong su dung Unicodeint num1 = _wtoi(cstr1); // Chuyen sang so nguyen su dung UnicodeCString cstr2 = "10.5";float num2 = atof(cstr2) // Chuyen sang so thuc khong su dung Unicodefloat num2 = _wtof(cstr2) // Chuyen sang so thuc su dung Unicode

13. Chuyển số sang CStringchar s1[20];int num = 10;sprintf(s1, "%d", num);CString cstr1 = s1; // chuyen so nguyen sang cstring - khong unicodeCString cstr2;cstr2.Format(_T("%d"), num); // chuyen so nguyen sang cstring unicodeTương tự với số thực ta có thể chuyển đổi tương tự và thay "%d" bằng "%f".

Page 61: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 61

5. Chương trình tải ảnh và hiển thị ảnh trên giao diện MFC

Ta tạo một mới một project, đặt tên project là HienThiThongTinAnh và thiết kế giao diệnnhư hình sau:

Trong đó, các Labels hiển thị thông tin của ảnh chính là Static Text, ID IDC_STATIC củachúng được đổi và chúng được đựt tên biến là l_width, l_height, l_channels, l_path (đốivới các static text dùng để hiển thị kết quả). ID của Picture Control được đổi thànhIDC_STATIC_PICTURE.Bây giờ, ta sẽ tiến hành xử lý sự kiện cho các button Tai Anh, Thong Tin và Thoat

void CHienThiThongTinAnhDlg::OnBnClickedButton1(){

// Load anh va hien thi anh

static CString Filter=_T("image files (*.bmp; *.jpg) |*.bmp;*.jpg|AllFiles(*.*)|*.*||");

CFileDialog Load(TRUE, _T("*.jpg"), NULL,OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,Filter,NULL);Load.m_ofn.lpstrTitle= _T("Load Image");if (Load.DoModal() == IDOK){

path = Load.GetPathName();std::string filename(path);src = cv::imread(filename,1);cv::namedWindow("Hien thi anh", 1);HWND hWnd = (HWND) cvGetWindowHandle("Hien thi anh");HWND hParent = ::GetParent(hWnd);::SetParent(hWnd, GetDlgItem(IDC_STATIC_PICTURE)->m_hWnd);

Button

Picturecontrol LabelLabels

Group Box

Page 62: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 62

::ShowWindow(hParent, SW_HIDE);// resize va hien thi anhcv::Mat dst;cv::resize(src, dst, cv::Size(320, 240), 0, 0, 1);cv::imshow("Hien thi anh", dst);

}

}

Trong hàm trên hàm cvGetWindowHandle() trả về một HWND của cửa sổ hiển thị ảnhtrong OpenCV, sau đó hàm SetParent() sẽ đặt cửa sổ này vào điều khiển Picture Controlcủa MFC, đây là cách ta dùng để hiển thị một ảnh lên bất kì một control nào của MFCthay vì phải chuyển đổi ảnh sang HBITMAP và dùng hàm SetBitmap. Hàm cv::resize()giúp chuyển từ một ảnh bất kì về ảnh có kích thước 320x240 giúp hiển thị trọn trong kíchthước của Picture Control. Cuối cùng, khi gọi hàm cv::imshow() trong OpenCV, ta sẽthấy ảnh được hiển thị trong Picture Control.

void CHienThiThongTinAnhDlg::OnBnClickedButton2(){

// Hien thi thong tin anhif(src.empty())

MessageBoxA("Chua tai anh", "Thong bao", 0);else{

int width_ = src.cols;int height_ = src.rows;int channels_ = src.channels();

CString result;result.Format("%d", width_);l_width.SetWindowTextA(result);

result.Format("%d", height_);l_height.SetWindowTextA(result);result.Format("%d",channels_);

l_channels.SetWindowTextA(result);l_path.SetWindowTextA(path);

}

}

Trong hàm trên, đầu tiên ta kiểm tra xem ảnh đã được tải ở bước trên hay chưa, nếu chưathì hiện ra thông báo chưa tải được ảnh, nếu đã tải được ảnh rồi thì tính toán chiều dài,chiều rộng, kênh màu của ảnh … sau đó hiển thị thông tin này lên các label tương ứng.Để hiện lên label hoặc bất kì một điều khiển nào của MFC ta dùng thuộc tính

Page 63: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 63

SetWindowText(). Tuy nhiên đối số đầu vào của hàm này lại có định dạng CString, do vậycác giá trị như int, float, double .. muốn hiển thị được trên các điều khiển của MFC taphải chuyển đổi kiểu dữ liệu như đã nói ở trên.Ở button Thoat, khi click vào vào chương trình sẽ thoát ra, việc thoát khỏi chương trình làrất đơn giản, có thể đạt được bằng cách gọi hàm OnCancel().

void CHienThiThongTinAnhDlg::OnBnClickedButton3(){

// Thoat khoi chuong trinhthis->OnCancel();

}

Kết quả chạy chương trình như sau:

Page 64: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 64

Chương IV Một số ứng dụng xử lý ảnh trong thực tế.

1. My Photo Editor, phần mềm chỉnh sữa ảnh đơn giản

Mục đích của bài này là tổng hợp lại các kiến thức và kĩ năng cơ bản của các thuật toánxử lý ảnh thong qua việc viết một chương trình tương có khả năng chỉnh sửa ảnh tương tựnhư phần mềm Photoshop, tuy nhiên xét về mọi mặt thì đơn giản hơn rất nhiều. Một sốchức năng của phần mềm mà ta có thể sử dụng để chỉnh sửa ảnh như: chuyển đổi qua lạigiữa các không gian màu(HSV, YCmCb, RGB…), Tăng giảm độ sang/độ tương phản,đảo ngược ảnh(ảnh âm bản), nhị phân ảnh, quay ảnh, thay đổi kích thước ảnh, cân bằnghistogram trong ảnh, làm mờ ảnh, méo ảnh … Ngoài ra ta có thể xem xét các thông tin vềảnh, xem biểu histogram ảnh và lịch sử chỉnh sủa ảnh và xem ảnh trước khi đồng ý chỉnhsửa. Giao diện chương trình như sau:

Để quản lý chương trình được đơn giản, ta chia chương trình làm 3 phần, một phầnchuyên về các hàm xử lý ảnh được lưu trong một lớp và đặt tên là ImgProcessing. Phầncho phép ta xem trước kết quả xử lý là một Dialog có chứa các thanh trượt giúp ta điềuchỉnh các tham số đồng thời xem trước kết quả của quá trình xử lý. Phần này được địnhnghĩa trong lớp ImgPreview. Cuối cùng là phần chứa giao diện chính của chương trình là

Dialog được thiết kế từ đầu, ta định nghĩa các biến và xử lý sự kiện trong file My PhotoEditorDlg.h và Photo EditorDlg.cpp. Ta sẽ lần lượt xem qua các phần này.

Phần chứa các hàm xử lý ảnh chính

Page 65: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 65

Các hàm xử lý trong ImgProcessing có dạng void TenHam(cv::Mat &src, cv::Mat& dst,type value) trong đó, src là ảnh đưa vào xử lý, dst là ảnh chứa kết quả và value thì tùythuộc vào đặc trưng của từng hàm mà có các kiểu và giá trị khác nhau. Ta định nghĩaheader của lớp ImgProcessing như sau:

// heade file

#pragma once

#include <opencv2\core\core.hpp>

using namespace cv;

class ImgProcessing{public:ImgProcessing(void);~ImgProcessing(void);// Ham xu lyvoid Grayscale(Mat&, Mat&);void ColorSpace(Mat&, Mat&, int);void Brightness(Mat&, Mat&, int);

….}

Và file cài đặt ImgProcessing.cpp có dạng như sau (đối với hàm tăng giảm độ sang):

// Brightnessvoid ImgProcessing::Brightness(Mat &src, Mat &dst, int value){

if(src.channels() == 1){

for(int i = 0; i < src.rows; i++)for(int j = 0; j < src.cols; j++)

dst.at<uchar>(i,j) =saturate_cast<uchar>(src.at<uchar>(i,j) + value);

}

if(src.channels() == 3){

for(int i = 0; i < src.rows; i++)for(int j = 0; j < src.cols; j++)

for(int k = 0; k < 3; k++)dst.at<Vec3b>(i,j)[k] =saturate_cast<uchar>(src.at<Vec3b>(i,j)[k] +value);

}}

Page 66: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 66

Các hàm khác có thể cài đặt một cách tương tự giống như nhiều bài trước đã đề cập.

Phần xem trước kết quả chỉnh sửa

Phần này nằm trong lớp ImgPreview kế thừa tử lớp CDialogEx của MFC. Để tạo ra lớpnày, cách đơn giản nhất là thêm vào project một dialog và thêm lớp vào cho dialog đó. Cụthể, từ cửa sổ Resource View -> My Photo Editor -> My Photo Editor.rc -> Dialog hãyclick chuột phải và chọn Insert Dialog, thiết kế một dialog có dạng như sau:

Để thêm lớp vào dialog này ta click chuột phải vào dialog sau đó chọn Add Class, hộpthoại MFC Add Class Wizard hiện ra, ta đặt tên cho class ở mục Class name làImgProcessing và ở mục Base class ta chọn là CDialogEx. Nhấn Finish để hoàn thànhquá trình khởi tạo lớp và ta có hai file mới được thêm vào, đó là ImgProcessing.h vàImgProcessing.cpp . Tiếp đó ta thêm biến cho các điều khiển(Control) bằng cách clickchuột phải vào các điều khiển sau đó chọn Add Variable . Các biến của slider tùy chọntham số có tên là silder1, slider2, slider3 , các biến là nhãn (Static text) dung để đặt tênhiển thị cho slider có tên lần lượt là l_disp1, l_disp2, l_disp3 và cuối cùng tên của biếnhiển thị kết quả khi kéo trượt slider là param1, param2, param3.

Vì trong chương trình có rất nhiều các hàm xử lý cần đến việc phải xem trước kết quả vàviệc với mỗi hàm xử lý cần đến một dialog để xem trước là rất phức tạp vì ta phải tạo rasố lượng dialog lớn tương ứng với lượng hàm xử lý. Để khác phục tình trạng này, ta chỉtạo ra một dialog preview duy nhất và tùy thuộc vào yêu cầu xử lý của từng hàm mà ta

Picture Control

Slider Control

Button

Static text

Page 67: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 67

cho nó một kiểu nhất định. Do vậy ta sẽ định nghĩa một kiểu liệt kê các kiểu xử lý trongfile ImpProcessing.h như sau:

enum {m_brightness = 0, m_contrast = 1,m_threshold = 2, m_rotation = 3,m_size = 4, m_blur = 5, m_noise = 6, m_distort = 7, m_edge = 8};Và khi dialog Image Preview được khởi tạo, trong hàm OnInitDialog() ta, sẽ căn cứ vàoyêu cầu của hàm cần xử lý để tạo ra cửa sổ thích hợp

BOOL ImgPreview::OnInitDialog(){

CDialogEx::OnInitDialog();

// Khoi tao cac thanh phanthis->slider1.SetRange(0, 450);this->slider1.SetPos(225);this->slider2.SetRange(0, 450);this->slider2.SetPos(225);this->slider3.SetRange(0, 450);this->slider3.SetPos(225);

this->slider2.EnableWindow(false);this->slider3.EnableWindow(false);

switch(type){

case m_brightness:{

this->param1.SetWindowTextA("Brightness");}break;

case m_contrast:{

this->param1.SetWindowTextA("Contrast");this->slider1.SetRange(0, 100);this->slider1.SetPos(10);

}….

}}

Trong đó, type là là kiểu xử lý được truyền từ chương trình chính tương ứng với từng hàmxử lý khác nhau. type được định nghĩa trong file ImageProcessing.h làprivate:

int type;và được gán giá trị thông qua phương thức SetType(int type_) :void ImgPreview::SetType(int type_){

this->type = type_;

Page 68: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 68

}Khi thanh trượt điều chỉnh các thong số thay đổi vị trí, ảnh phải được xử lý và hiển thịtrực quan lên Picture Control. Sự kiện thay đổi vị trí thanh trượt được cài đặt như sau:

void ImgPreview::OnNMCustomdrawSlider1(NMHDR *pNMHDR, LRESULT *pResult){

LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);switch(type){case m_brightness:{

i_param1 = slider1.GetPos() - (int)slider1.GetRangeMax()/2;l_disp1.SetWindowTextA(ToString(i_param1));process.Brightness(img_display, img_dst, i_param1);cv::imshow("Image Preview", img_dst);

}break;

case m_contrast:{

d_param1 = double(slider1.GetPos() / 10.0);l_disp1.SetWindowTextA(ToString(d_param1));process.Contrast(img_display, img_dst, d_param1);cv::imshow("Image Preview", img_dst);

}break;…

}}

Trong đó, img_dst là một ảnh thu nhỏ của ảnh gốc, được thay đổi kích thước để hiển thịphù hợp trong khung hình thông qua hàm ImageDisplay()

Khi việc xem trước hoàn tất, nếu ta đồng ý với kết quả đó và bấm vào nút OK, một biếnthành viên is_ok sẽ được gán giá trị cho chương trình chính biết rằng người dung đã đồngý với việc chỉnh sữa đó, ngược lại nếu nút Cancel được bấm, biến này sẽ có giá trị false.

void ImgPreview::OnBnClickedButton1(){

// Button OKthis->is_ok = true;ImgPreview::OnCancel();

}

Page 69: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 69

Phần chương trình chính

Phần này chứa giao diện người dung trong quá trình xử lý ảnh. Nó bao gồm các thao tácmở file ảnh, thực hiện các hàm xử lý như đã nêu ở trên, hiển thị các cửa sổ view khácnhau và lưa file ảnh sau khi hoàn tất xử lý.

Ta thiết kế giao với hệ thống menu, button và các cửa sổ hiển thị ảnh được tạo ra từPicture Control … như hình 1. Các Control khác trong MFC là khá quen thuộc, đối với hệthống menu, để thêm vào dialog đâu tiên ta thiết kế hệ thống menu bằng cách click chuộtphải vào Project sau đó chọn Add -> Resource-> Menu. Sau khi đã hoàn thành hệ thốngmenu ta hãy vào dialog chính, ở mục Properties của dialog này chọn Menu và chọn IDcủa menu mà ta vừa tạo ra, giả sử là IDR_MENU1

Để xử lý sự kiện click chuột cho menu, ta hãy quay lại phần thiết kế menu, click chuộtphải vào menu cần xử lý sau đó chọn Add Event Handler. Sự kiện click chuột vào menuOpen có dạng như sau:

void CMyPhotoEditorDlg::OnUpdateFileOpen1(CCmdUI *pCmdUI){

// Open Image

static CString Filter=_T("image files (*.bmp; *.jpg) |*.bmp;*.jpg|AllFiles (*.*)|*.*||");

CFileDialog Load(TRUE, _T("*.bitmap"), NULL,OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,Filter,NULL);Load.m_ofn.lpstrTitle= _T("Load Image");if (Load.DoModal() == IDOK){

img_path = Load.GetPathName();img_name = Load.GetFileName();src = cv::imread(img_path);…

}…

}

Sự kiện mở file ảnh của nút Open cũng có thể được định nghĩa một cách tương tự.

Sau khi file ảnh được mở, ta có thể tiến hành thực hiện các phép xử lý ảnh thống qua hệthống menu vừa thiết kế ở trên. Đối với các phép xử lý cho kết quả trực tiếp mà không

Page 70: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 70

cần phải xem trước kết quả (như chuyển sang màu xám, làm ngược ảnh …) thì sự kiệnclick chuột vào menu đó được sử lý có dạng như sau:

void CMyPhotoEditorDlg::OnUpdateAdjustmentsInvert(CCmdUI *pCmdUI){

// Invert Imageprocess.Invert(src, src);ImageDisplay(src);…

}

Đối với những menu cần xem trước kết quả thì ta chỉ sử lý ảnh khi đã nhận được tham sốtừ dialog preview, như vậy trước khi xử lý ta phải khởi tạo một dialog preview, truyềntham số cho dialog này và nhận tham số để xử lý. Sự kiện của những menu đó có dạngnhư sau:

void CMyPhotoEditorDlg::OnUpdateAdjustmentsThreshold(CCmdUI *pCmdUI){

// ThresholdImgPreview dlg;dlg.SetType(m_threshold);dlg.src = src;dlg.DoModal();if(dlg.is_ok){

process.Threshold(src, src, dlg.i_param1);ImageDisplay(src);

}}Trong đoạn code trên, ta truyền hai tham số cho lớp ImgPreview là m_threshold để cholớp này biết kiểu cần xử lý là kiểu gì và src là ảnh gốc cần được xem trước trong quá trìnhxử lý. Hàm ImageDisplay() có tác dụng tùy chỉnh kích thước để hiện thị ảnh trong phùhợp trong khung hình sau đó hiển thị ảnh đó lên. Process là một đối tượng của lớpImgProcessing được khai báo trong file My Photo EditorDlg.h

Bên cạnh quá trình xử lý, ta thiết kế thêm các cửa sổ nhìn khác để nắm được thông tin chitiết hơn về ảnh đang xử lý , đó là cửa sổ cho phép xem thông tin chi tiết về ảnh (kíchthước chiều dài, rộng, …), cửa sổ hiện thị thông tin histogram ảnh (một hoặc nhiều kênhmàu) và cửa sổ cho phép xem lịch sử chỉnh sửa ảnh. Các cửa sổ này thông qua hệ thốngmenu view để cho phép nó hoạt động hay không hoạt động và ta sử dụng các biến kiểmtra xem cửa sổ này có được kích hoạt hay không trong các hàm xử lý. Nếu nó được kíchhoạt (có check ở menu view) thì sau mỗi hàm xử lý ta phải cập nhật thông tin cho nó, nếukhông thì bỏ qua bước này, sẽ tiết kiệm được thời gian xử lý.

void CMyPhotoEditorDlg::OnUpdateViewHistogram(CCmdUI *pCmdUI)

Page 71: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 71

{// View histogramis_histogram = !is_histogram;CMenu *m_histogram = GetMenu();m_histogram->GetSubMenu(3);if(is_histogram){

m_histogram->CheckMenuItem(ID_VIEW_HISTOGRAM, MF_CHECKED);UpdateHistogram(0);

}else{

m_histogram->CheckMenuItem(ID_VIEW_HISTOGRAM, MF_UNCHECKED);cv::imshow("Histogram", hist_init);

}

}Và trong các hàm xử lý ta kiểm tra biến is_histogram để update cửa sổ view

void CMyPhotoEditorDlg::OnUpdateAdjustmentsInvert(CCmdUI *pCmdUI){

// Invert Imageprocess.Invert(src, src);ImageDisplay(src);if(is_histogram) UpdateHistogram(0);if(is_history) history_list.AddString("Invert Image");

}Trong đó hàm UpdateHistogram sẽ tính toán histogram và hiển thị histogram của ảnh lêncửa sổ, history_list là một Edit Control sẽ hiển thị lịch sử của bước vừa thao tác .

Khi quá trình chỉnh sữa ảnh hoàn tất ta có thể lưu ảnh lại thông qua nút Save hoặc menuSave. Khi lưu ảnh, ta cho phép người sử dụng được lựa chọn chất lượng ảnh thông qua tỉsố nén ảnh. Tỉ số này càng cao thì ảnh nén có dung lượng càng thấp, tuy nhiên thông tinảnh có thể bị mất nhiều. Ngược lại nếu tỉ số nén thấp thì ảnh sẽ chi tiết tuy nhiên dunglượng lại lớn. Ta thiết kế một dialog SaveOption và xử lý sự kiện như đối với lớpImgPreview

Page 72: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 72

Một phần nữa cũng rất quan trọng trong quá trình xử lý đó là xác định tính hợp lệ củatừng menu. Theo đó, để hệ thống tránh gặp những lỗi phát sinh thì menu chỉ được phép sửdụng khi đã có các điều kiện có sẵn. Chẳng hặn như khi ảnh chưa được tải vào chươngtrình thì toàn bộ các menu sẽ ẩn, chỉ có menu và nút Open và được phép hoạt động nếukhông khi click vào bất kì menu xử lý nào hệ thống sẽ gặp lỗi bộ nhớ do ảnh xử lý là ảnhrỗng. Hoặc khi đang làm việc với ảnh xám (grayscale) thì menu chuyển đổi sang cáckhông gian màu khác (HSV chẳng hạn) phải bị ẩn nếu không muốn chương trình gặp sựcố và thoát ra ngoài ý muốn. Ta xác định tính hợp lệ của hệ thống menu thông qua cácbiến và hàm ValidateMenu(). Ví dụ như biến is_load sẽ kiển tra xem ảnh được tải haychưa, biến is_gray kiểm tra ảnh có phải là ảnh xám hay không… các biến này được đặtngay sau các hàm tương ứng của chúng :

void CMyPhotoEditorDlg::OnUpdateFileOpen1(CCmdUI *pCmdUI){

// Open Image…if (Load.DoModal() == IDOK){

…is_load = true;…

}}

Và hàm ValidateMenu() có dạng như sau:

void CMyPhotoEditorDlg::ValidateMenu(){

// Kiem tra tinh hop le cua menu trong tung truong hopCMenu *m_file = GetMenu();m_file->GetSubMenu(0);

CMenu *m_image = GetMenu();m_image->GetSubMenu(1);

…if(!is_load){

m_file->EnableMenuItem(ID_FILE_SAVE1, MF_DISABLED|MF_GRAYED);m_image->EnableMenuItem(ID_MODE_GRAYSCALE, MF_DISABLED|MF_GRAYED);save_btn.EnableWindow(false);…

}

else{

m_file->EnableMenuItem(ID_FILE_SAVE1, MF_ENABLED);m_image->EnableMenuItem(ID_MODE_GRAYSCALE, MF_ENABLED);…

}if(is_gray)

Page 73: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 73

{m_image->EnableMenuItem(ID_MODE_HSV, MF_DISABLED|MF_GRAYED);m_image->EnableMenuItem(ID_MODE_YCrCb, MF_DISABLED|MF_GRAYED);…

}}

Hàm EnableMenuItem() là hàm cho phép một menu ẩn hoặc hiển thị thông qua tham sốMF_DISABLED hoặc MF_ENABLED

Hình sau là kết quả của quá trình xử lý khi ta điều chỉnh độ sáng của một ảnh.

2. Nhận dạng biển số xe

Bài toán nhận dạng biển số xe có nhiều ý nghĩa trong thực tế, nó giúp việc giám sát, quảnlý, thống kê … các phương tiện một cách dễ dàng, tiện lợi và nhanh chóng. Một số ứngdụng điển hình đã đư ợc triển khai trong thực tế như ứng dụng trong quản lý bãi đỗ xethông minh, ứng dụng thu phí ở các trạm thu phí, ứng dụng phát hiện lỗi vi phạm giaothông một cách tự động … Trong phần này ta nghiên cứu về mặt kĩ thuật của một bài toánnhận dạng biển số xe, viết chương trình demo và các bư ớc cần thiết để đưa bài toán vàoứng dụng thực tế.

Giả sử ta đang xây dựng bài toán nhận dạng biển số xe ô tô, với đầu vào là một ảnh chứabiển số xe và đầu ra là một chuỗi kí tự của biển số đã được nhận dạng. Nếu quan sát bằngmắt người ta có thể dễ dàng nhận biết được một biển số, tuy nhiên với máy tính, đó là mộtđiều không dễ dàng gì, nó rất dễ bị nhiễu, bị nhầm bởi các hình khối xung quanh tương

Page 74: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 74

tự, bởi các điều kiện thời tiết, góc độ … Để hạn chế bớt được những khó khăn này, nhiềuhệ thống nhận dạng trong thực tế thường giới hạn các điều kiện, chẳng hặn như camerathu ảnh được cố định ở một vị trí, xe được đi vào một khe hẹp nhất định…

Có nhiều các khác nhau để thực hiện bài toán nhận dạng này, ở các phần mềm thương mạihoàn chỉnh, nó là sự kết hợp và tối ưu của khá nhiều thuật toán phức tạp, trong bài này, tađi theo hướng tiếp cận chia bài toán thành hai bài toán nhỏ: phát hiện biển số xe, cách lykí tự và nhận dạng các kí tự.

Phát hiện vùng chứa biển số xe và cách ly kí tự.

Vì biển số xe có những đặc trưng cơ bản được quy định bởi các cơ quan chức năng nên tacó thể dựa vào đặc trưng này để phân biệt với các đối tượng khác. Theo quy định của bộcông an, biển số xe đằng trước của các loại xe dân dụng là một hình chữ nhật, có kíchthước 470x110 (mm), phông nền màu trắng và các kí tự chữ cái in hoa màu đen. Các kí tựchữ số bao gồm từ 0 tới 9 và các kí tự chữ số bao gồm A, B, C, D, E, F, G, H, K, L ,M ,N, P, S, T, U, V, X, Y, Z (20 kí tự). Ta sẽ dựa vào các đặc trưng hình học này để tríchchọn ra vùng chứa biển số xe, các bước thực hiện như sau:

Bước 1: Load ảnh, tiền xử lý ảnh (khử nhiễu, làm mịm …)

Bước 2: Chuyển ảnh ban đầu thành ảnh xám rồi nhị phân hóa ảnh đó. Để ảnh nhị phânthu được kết quả tốt và không bị phụ thuộc vào các điều kiện ánh sáng khác nhau, tasử dụng phương pháp nhị phân hóa với ngưỡng động (adaptive threshold) như đã nóiở trên.

Bước 3: Tìm các đường bao quanh đối tượng. Sau khi nhị phân ảnh, các đối tượng sẽlà các khối hình đen trên nền trắng, với mỗi đối tượng ta luôn vẽ được một đường biên

Page 75: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 75

bao quanh đối tượng đó. Để tránh trình trạng các đối tượng không tạo ra được cácđường biên khép kín do các vết dạn nứt hoặc nhiễu gây ra, trước khi tìm biên ta nênlàm mịm đường biên bằng các phép giãn nở (hoặc co) như đã nói trong bài trước.

Bước 4: Xác định hình chữ nhật bao quanh các đường bao quanh đã tìm được từ bước3. Đường bao quanh đã tìm được ở bước 3 là một chuỗi các điểm biên nối liền của đốitượng, dựa vào tọa độ của các điểm biên này ta sẽ xác định được hình chữ nhật ngoạitiếp bao quanh đối tượng.

Bước 5: Tìm ra các hình chữ nhật có khả năng là vùng chứa biển số, nếu hình chữ nhậtthu được ở bước 4 là vùng chứa biển số thì nó phải thỏa mãn ít nhất được một số điềukiện sau:

o Tỉ lệ chiều dài/ rộng phải xấp xĩ 4.3. Trong cài đặt thực tế ta có thể cho tỉ lệnày dao động trong khoảng [4.0, 4.5].

o Số đối tượng thỏa mãn là một kí tự biển số trong vùng hình chữ nhật phải làmột số lớn hơn một ngưỡng nào đó. Với biển số xe 4 chữ số, số kí tự này là7, với biển số xe mới với 5 kí tự số, số kí tự này là 8, cũng có một số biển sốxe có nhiều hơn 8 kí tự do có 2 kí tự chữ cái … Để xác định một đối tượnglà kí tự hay không, ta cũng sẽ dựa vào đặc điểm hình học của kí tự đó như tỉlệ chiều dài/rộng đối tượng, tỉ lệ chiều cao, dài của đối tượng so với tỉ lệchiều cao, dài của hình chữ nhật đang được xem xét là biển số, tỉ lệ pixelđen/trắng của dối tượng … Nếu xác định đó là một kí tự của biển số xe tacũng sẽ đồng thời lưu nó lại, đó chính là cách ta cách ly đối tượng trongbiển số.

o Ngoài ra, tùy thuộc vào điều kiện của bài toán ta có thể cố định thêm một sốđặc tính để chắc chắn hơn vùng chứa biển số, chẳng hạn như kích thước củahình chữ nhật không được vượt quá một nửa kích thước của ảnh đầu vào, tỉlệ pixel đen/trắng trong hình chữ nhật phải nằm trong một ngưỡng nào đó…

Sau khi đã xác định được vùng có khả năng là biển số, ta sẽ cắt vùng hình đó, đồng thờilấy ra các kí tự trong vùng đó lưu vào một mảng để thực hiện việc nhận dạng.

Nhận dạng kí tự

Các kí tự sau khi được cách ly là những kí tự đơn lẽ trong một khung hình chữ nhật cókích thước nhất định. Để nhận dạng được kí tự này ta có thể sử dụng rất nhiều phươngpháp khác nhau, từ đơn giản như phương pháp sử dụng độ tương quan chéo (crosscorrelation) cho đến những phương pháp sử dụng các mô hình máy học (machinelearning) như mạng Neuron nhân tạo, SVM …

Đối với các phương pháp sử dụng máy học, ta cần sưu tầm một lượng mẫu các kí tự nhấtđịnh, từ vài trăm cho tới hàng nghìn mẫu rồi đưa vào các bộ huấn luyện, kết quả huấn

Page 76: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 76

luyện này sẽ được dùng để nhận dạng các mẫu mới. Độ chính xác của kết quả nhận dạngnói chung của phương pháp này tùy thuộc vào độ phức tạp của mô hình, khối lượng mẫuhuấn luyện, thời gian tính toán. Trong phần này ta sử dụng phương pháp SVM để nhậndạng kí tự.Phương pháp SVM. (Chi tiết về phương pháp này bạn đọc có thể tham khảo trong các tàiliệu chuyên ngành khác). SVM (Surport Vector Machine) là một mô hình máy học giámsát được dùng trong việc phân tích, phân lớpdữ liệu dựa vào các siêu phẳng. Giả sử ta cómột tập dữ liệu hai chiều như hình bên, khiđó ta có thể phân lớp dữ liệu này thành haiphần nhờ một siêu. Siêu phẳng trong mặtphẳng là một đường thằng, trong không gian3 chiều là một mặt phẳng và tổng quát trongkhông gian n chiều là một không gian n-1chiều. Trong trường hợp dữ dữ liệu là khôngtuyến tính, ta cần ánh xạ tập dữ liệu đó lênmột không gian có số chiều lớn hơn đểthuận tiện cho việc phân loại dữ liệu, nhiệmvụ là cần phải tìm siêu phẳng sao cho khoảng cách tới các biên của dữ liệu là lớn nhất.Hiểu một cách đơn giản về phương pháp này như sau: cho một tập các mẫu huấn luyện,với mỗi mẫu được gắn vào một nhãn, quá trình huấn luyện SVM sẽ xây dựng một môhình cho phép dự đoán một tập dữ diệu khác thuộc về nhãn nào, tức phân loại tập dữ liệuđó thuộc vào lớp nào.

Quay lại bài toán nhận dạng kí tự biển số xe ta đang xét, làm sao để nhận dạng được cáckí tự này dựa trên mô hình SVM?Trước hết cần phải nhận thấy rằng SVM là một bộ máy phân loại dữ liệu, muốn sử dụngđược nó ta cần phải có dữ liệu, dữ liệu đối với các kí tự mà ta cần nhận dạng ở đây chínhlà các đặc trưng trong ảnh của kí tự đó. Giả sử ta cần phân loại 30 lớp dữ liệu (tương ứngvới 30 kí tự trong biển số xe), với mỗi lớp dữ liệu, ta tính toán được 10 vector đặc trưng(10 mẫu), và mỗi vector đặc trưng tưng ứng với các đặc trưng trong một ảnh. Khi đó ta sẽđưa vào bộ huấn luyện SVM toàn bộ dữ liệu này, sau đó với một ảnh bất kì, ta sẽ tínhtoán một vector đặc trưng của ảnh đó, mô hình SVM sẽ xem xét xem dữ liệu này (tứcvector đặc trưng này) thuộc vào lớp nào trong số những lớp mà nó đã được huấn luyện.Tính toán đặc trưng trong ảnh.Đặc trưng trong ảnh là những đặc điểm riêng biệt giúp phân biệt ảnh này với ảnh khác.Việc xem xét như thế nào là các đặc trưng trong ảnh là một việc không có quy ước trước,nó phụ thuộc vào cách nghiên cứu, cài đặt của từng trường hợp cụ thể và vẫn đang đượcnghiên cứu để đưa ra những phương pháp tốt nhất. Trong phần này ta sẽ tính toán cácvector đặc trưng dựa trên ý tưởng của phương pháp Haar, chú ý rằng bạn đọc có áp dụngphương pháp tính toán đặc trưng trong ảnh hay hơn, hiệu quả hơn.Giả sử ta có hai kí tự như hình bên, nhìn bằng mắt thường ta có thể dễ dàng phân biệtđược hai kí tự 0 và 4, tuy nhiên làm sao để máy tính phân biệt được hai kí tự này? Bây giờ

Page 77: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 77

ta sẽ đưa hai kí tự này về cùng một kích thước, chia nhỏ các kí tự thành16 ô nhỏ khác nhau như sau

Ta nhận thấy rằng nếu tính tổng các pixel đen trong các ô của hai bức ảnh thì số 0 và số 4có thể phân biệt dựa vào các ô (1,1), (1, 4), (2, 2), (3,3) … tại những ô đó, tổng số cácđiểm ảnh đen là khác nhau hoàn toàn. Tính toán số điểm ảnh đen của 16 ô vuông này tacó thể thu được 16 đặc trưng của một ảnh, 16 đặc trưng này đủ để phân biệt kí tự 0 và 4.Tuy nhiên, với 30 kí tự ta cần phải tính toán nhiều hơn các đặc trưng, các đặc trưng khôngnhất thiết phải là 0 (tức không có điểm ảnh đen nào) hặc 1(tức là toàn số điểm ảnh đentrong ô) mà có thể là một tỉ lệ tương đối nào đó. Từ 16 đặc trưng cơ bản trên, ta kết hợpchúng lại để tạo ra những đặc trưng khác, chẳng hạn như lấy tổng các đặc trưng trênđường chéo (1,1) + (2,2) + (3,3) + (4,4) hoặc tổng các đặc trưng xung quanh đường biêncủa ảnh … số đặc trưng càng lớn thì việc phân loại các lớp càng ít bị sai, có nghĩa là xácsuất nhận dạng càng lớn.

Cài đặt chương trình Nhận dạng biển số xe ô tô

Ta tạo một chương trình dựa trên Dialog của MFC, đặt tên project là LPR (License PlateRecognition). Về cơ bản có thể chia chương trình thành 3 phần chính như sau: phần chứagiao diện chính của chương trình (được định nghĩa trong file LPRDlg.h và LPRDlg.cpp),phần chứa các hàm chính cho việc phát hiện, nhận dạng biển số xe và không liên quan tớigiao diện (được định nghĩa trong file LprCore.h và LprCore.cpp) và phần chứa một côngcụ giúp cho ta có thể huấn luyện được mô hình SVM một cách tùy ý (được định nghĩatrong file TrainSVM.h và TrainSVM.cpp). Ngoài ra còn có một số phần nhỏ giúp cho việclập trình được dễ dàng hơn như phần chuyển đổi các biến dữ liệu trong MFC chằng hạn(unity_conversion.h).

Huấn luyện mô hình SVMĐể tạo được mô hình SVM phục vụ cho việc nhận dạng kí tự sau này, ta cần huấn luyệnvà lưu mô hình sau khi huấn luyện.

Page 78: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 78

Chuẩn bị cơ sở dữ liệu. Ta cần chuẩn bị cơ sở dữ liệu là tập hợp của các kí tự trong biểnsố xe. Có 30 kí tự thường gặp trong biển số xe, do đó ta cần phân loại 30 lớp này, trongtrường hợp ở đây giả sử với mỗi lớp, tức là mỗi tự tự ta có 10 ảnh, ta sẽ lưu các ảnh nàyvào các folder, tên các folder được đặt tên theo các kí tự, chẳng hạn như folder 0 chứa 10ảnh của kí tự 0, folder 1 chứa 10 ảnh của kí tự 1, … folder 30 chứa 10 ảnh của kí tự Z. tacần đánh tên folder theo số tứ tự, vì số thứ tự cũng chính là nhãn tương ứng đưa vào việcnhận dạng. Ta sẽ tính toán đặc trưng của từng kí tự và lưu tất cả các đặc trưng này vàomột ma trận để phục vụ cho việc huấn luyện. Hàm tính toán đặc trưng trong một ảnh sẽdựa trên ý tưởng tổng các điểm đen trong một khung hình, tuy nhiên nó được chuẩn hóabằng cách chia cho tổng tất cả các điểm ảnh đen của kí tự.

vector<float> calculate_feature(Mat src){

Mat img;if(src.channels() == 3)

cvtColor(src, img, CV_BGR2GRAY);threshold(img, img, 100, 255, CV_THRESH_BINARY);

vector<float> r;resize(img, img, Size(40, 40));int h = img.rows/4;int w = img.cols/4;int S = count_pixel(img);int T = img.cols * img.rows;for(int i = 0; i < img.rows; i += h){

for(int j = 0; j < img.cols; j += w){

Mat cell = img(Rect(i,j, h , w));int s = count_pixel(cell);float f = (float)s/S;r.push_back(f);

}}

for(int i = 0; i < 16; i+= 4){

float f = r[i] + r[i+1] + r[i+2] + r[i+3];r.push_back(f);

}

for(int i = 0; i < 4; ++i){

float f = r[i] + r[i+4] + r[i+8] + r[i+ 12];r.push_back(f);

}

r.push_back(r[0] + r[5] + r[10] + r[15]);

Page 79: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 79

r.push_back(r[3] + r[6] + r[9] + r[12]);...return r; //32 dac trung

}

Trong đó hàm count_pixel là hàm tính toán số pixel đen trong một ảnhint count_pixel(Mat img, bool black_pixel = true){

int black = 0;int white = 0;for(int i = 0; i < img.rows; ++i)

for(int j = 0; j < img.cols; ++j){

if(img.at<uchar>(i,j) == 0)black++;

elsewhite++;

}if(black_pixel)

return black;else

return white;}

Đoạn code sau sẽ huấn luyên một mô hình svm, với đầu vào là một folder chứa dữ liệuhuấn luyện như đã nói ở trên (folder này chứa 30 folder con, mỗi folder con chứa 10 kí kítự mẫu)

const int number_of_class = 30;const int number_of_sample = 10;const int number_of_feature = 32;

CvSVMParams params;params.svm_type = CvSVM::C_SVC;params.kernel_type = CvSVM::RBF;params.gamma = 0.5;params.C = 16;params.term_crit = cvTermCriteria(CV_TERMCRIT_ITER, 100, 1e-6);SVM svm;Mat data = Mat(number_of_sample * number_of_class, number_of_feature,CV_32FC1);Mat label = Mat(number_of_sample * number_of_class, 1, CV_32FC1);int index = 0;

vector<string> folders = list_folder("D:/Data");for(size_t i = 0; i < folders.size(); ++i){

cout<<i<<"..."<<endl;vector<string> files = list_file(folders.at(i));

Page 80: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 80

string folder_path = folders.at(i);string label_folder = folder_path.substr(folder_path.length() - 1);for(size_t j = 0; j < files.size(); ++j){

src = imread(files.at(j));if(src.empty()) continue;vector<float> feature = calculate_feature(src);if(feature.size() < number_of_feature){

cout<<"error " <<endl;continue;

}

for(size_t t = 0; t < feature.size(); ++t)data.at<float>(index, t) = feature.at(t);

label.at<float>(index, 0) = i;index++;}

}

svm.train_auto(data, label, Mat(), Mat(), params);svm.save("E:/svm.txt");

Trong đoan chương trình trên, ta lần lượt đọc qua tất cả các folder con trong thư mụcD:/Data bằng hàm list_folder (hàm này ta viết dựa trên header dirent.h, với mục đích liệtkê danh sách các folder trong một thư mục), sau đó ta tính toán tất cả các đặc trưng củacác ảnh trong các folder này và lưu vào ma trận data. Ma trận label chứa nhãn của các lớpcần nhận dạng, nó cũng chính là tên của các folder tương ứng đã được đánh số thứ tự tăngdần. Hàm train_auto sẽ thực hiện việc huấn luyện một cách tự động và tối ưu các thôngsố của mô hình. Ở đây các thông số params ta sử dụng chỉ là một trong số nhiều mô cáchmà ta có thể đặt thông số. Công cụ cài đặt trong chương trình là một lớp TrainSVM kếthừa từ lớp CDialog tạo ra nhiều tùy chỉnh giúp cho việc đưa các thông số đầu vào, đầu ramột cách dễ dàng hơn.

Page 81: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 81

Phát hiện và nhận dạng biển số xeTa cài đặt một lớp nhận phát hiện và nhận dạng biển số xe theo như các bước đã nói ởtrên, và đặt tên cho lớp này LprCore. Ta sẽ cài đặt lớp này với giao diện sao cho việc sửdụng nó là dễ dàng nhất, giao diện (interface) của lớp này được định nghĩa như sau:// lprcore.h header#pragma once#include <opencv2/core/core.hpp>#include <opencv2/ml/ml.hpp>

using namespace std;using namespace cv;

class LprCore{public:

LprCore(void);~LprCore(void);void set_image(Mat);void set_svm_model(string);void do_process();vector<string> get_text_recognition();vector<Mat> get_plate_detection();vector<Mat> get_character_isolation();vector<vector<Mat> > get_character();vector<double> get_process_time();

Page 82: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 82

void clear();private:

bool done;bool ready;SVM svm;Mat image;vector<Mat> plates;vector<Mat> draw_character;vector<vector<Mat> > characters;vector<string> text_recognition;vector<double> process_time;char character_regconition(Mat);bool plate_detection();bool plate_recognition();

};

ở các lớp khác khi sử dụng giao diện này, chỉ việc đưa ảnh đầu vào, đưa mô hình hìnhsvm đã được huấn luyện, gọi hàm do_process và sau đó có thể lấy ra các kết quả như biểnsố nhận dạng được, kết quả nhận dạng được, thời gian của các quá trình …Bản chất của hàm do_process sẽ gọi hàm plate_detection và hàm plate_recognition. Nếugọi hai hàm này thành công, có nghĩa là quá trình thực hiện đã thành công và ta gán chobiến trạng thái là truevoid LprCore::do_process(){

if(this->plate_detection() && this->plate_recognition())done = true;

}Các hàm lấy kết quả sẽ trả về kết quả tương ứng như biển số, chuỗi kí tự nhận dạng được…vector<string> LprCore::get_text_recognition(){

if(done)return this->text_recognition;

}

vector<Mat> LprCore::get_plate_detection(){

if(done)return this->plates;

}

Ta sẽ xét hai hàm cài đặt hàm chính của lớp đó là hàm bool plate_detection() và hàmchar character_recognition(Mat). Hàm bool plate_recognition() thực chất là việclà việc gọi hàm char character_recognition(Mat) cho một chuỗi các ảnh kí tự trongbiển số.Hàm bool plate_detection(),trả về kết quả true nếu phát hiện được biển số đồng thờilưu các biển số phát hiện được vào vector vector<Mat> plates:

Page 83: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 83

Trước hết,ta thực hiện các phép biến đổi thông thường để thu được một ảnh nhị phân cókết quả tốt, sau đó thực hiện phép tìm đường bao quanh (contour), với mỗi đường bao, tavẽ hình chữ nhật bao quanh và xem xét các điều kiện của hình chữ nhật đó:…cvtColor(image, gray, CV_BGR2GRAY);adaptiveThreshold(gray, binary, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C,CV_THRESH_BINARY, 55, 5);…findContours(binary, contours, hierarchy, CV_RETR_TREE,

CV_CHAIN_APPROX_SIMPLE, Point(0, 0) );if(contours.size() <= 0) return false;for(size_t i = 0; i < contours.size(); ++i){

Rect r = boundingRect(contours.at(i));if(r.width > image.cols/2 || r.height > image.cols/2 || r.width < 120

|| r.height < 20 || (double)r.width/r.height > 4.5|| (double)r.width/r.height < 3.5)

continue;...

}

Nếu các hình chữ nhật bao quanh không thỏa mãn được điều kiện là biển số ta sẽ bỏ quakhông xét tiếp nữa mà chuyển tới các đường bao khac. Trong trường hợp thỏa mãn, tatiếp tục vòng lặp bằng cách tìm các hình bao quanh đối tượng trong hình chữ nhật đó thỏamãn điều kiện của một kí tự trong biển số xe…Mat sub_image = image(r);vector<Rect> r_characters;for(size_t j = 0; j < sub_contours.size(); ++j){

Rect sub_r = boundingRect(sub_contours.at(j));if(sub_r.height > r.height/2 && sub_r.width < r.width/8 && sub_r.width

> 5 && r.width > 15 && sub_r.x > 5){

Mat cj = _plate(sub_r);double ratio = (double)count_pixel(cj)/(cj.cols*cj.rows);if(ratio > 0.2 && ratio < 0.7){

r_characters.push_back(sub_r);rectangle(sub_image, sub_r, Scalar(0,0,255), 2, 8, 0);

}}

}…Trong đoạn code trên, để thỏa mãn các đường bao quanh đối tượng là một kí tự biển số,ngoài so sách tỉ lệ tương quan dài, rộng của đối tượng so với biển số ta còn so sánh tỉ lệpixel đen trên tổng số pixel của đối tượng và ta giả sử rằng nếu là một kí tự của biển số thì

Page 84: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 84

nó nằm trong khoảng 0,2 đến 0,7. Sau bước này, nếu số lượng r_characters lớn mộtngưỡng nào đó (thường là > 7) thì ta sẽ công nhận nó r là một vùng chứa biển số, các kítự cắt ra được sau đó cần phải sắp xếp lại theo thứ tự từ trái sang phải để cho nó tươngứng với thứ tự các chữ cái trong biển số trước khi áp dụng cho việc nhận dạng.

Hàm char character_regconition(Mat img_character) trả về kết quả là một kí tự kiểuchar với một ảnh đầu vào tương ứng. Để thực hiện việc nhận dạng, đầu tiên ta cần tínhtoán các đặc trưng của ảnh đầu vào img_character. Việc tính toán các đặc trưng sẽ cho tamột vector chứa các đặc trưng của ảnh đó, ta sẽ dùng hàm predict của dối tượng svm đểxem xem vector đó thuộc về lớp nào. Kết quả trả về của hàm predict là một số thực tươngứng với nhãn mà ta đã huấn luyện, có 30 nhãn tương ứng với các số từ 0 – 9 và từ A – Z.Chú ý là không phải tất cả các chữ cái đều được sử dụng, do vậy sau kết quả thu được từhàm predict cần phải được chuyển đổi sang các kí tự tương ứng. Chú ý là trước khi sửdụng hàm predict để dự đoán ta cần phải load mô hình SVM được huấn luyện từ bướctrên.void LprCore::set_svm_model(string file_name){

this->svm.load(file_name.c_str());ready = true;

}

Hàm cài đặt cho nhận dạng kí tự như sau:

char LprCore::character_recognition(Mat img_character){

char c = '*'; // truong hop khong cho ket qua tra ve *if(img_character.empty()) return c; // neu anh rongif(!ready) return c; // neu chua load mo hinh svm

vector<float> feature = calculate_feature(img_character);Mat m = Mat(number_of_feature, 1, CV_32FC1);for(size_t i = 0; i < feature.size(); ++i)

m.at<float>(i, 0) = feature[i];float r = this->svm.predict(m); // du doan mau moiint ri = (int)r;if(ri >= 0 && ri <= 9)

c = (char)(ri + 48); //ma ascii 0 = 48if(ri >= 10 && ri < 18)

c = (char)(ri + 55); //ma accii A = 65, --> tu A-Hif(ri >= 18 && ri < 22)

c = (char)(ri + 55 + 2); //K-N, bo I,Jif(ri == 22) c = 'P';if(ri == 23) c = 'S';if(ri >= 24 && ri < 27)

c = (char)(ri + 60); //T-V,if(ri >= 27 && ri < 30)

c = (char)(ri + 61); //X-Z

Page 85: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 85

return c;}

Và cuối cùng, toàn bộ kí tự trong biển số được nhận dạng qua hàm boolplate_recognition()

bool LprCore::plate_recognition(){

if(plates.size() <= 0 ) return false;double t = (double)cvGetTickCount();for(size_t i = 0; i < characters.size(); ++i){

string result;for(size_t j = 0; j < characters.at(i).size(); ++j){

char cs = character_recognition(characters.at(i).at(j));result.push_back(cs);

}

this->text_recognition.push_back(result);}t = (double)cvGetTickCount() - t;t = (double)t/(cvGetTickFrequency()*1000.); //convert to secondprocess_time.push_back(t);return true;

}

Giao diện chương trình chínhTa thiết kế giao diện chương trình chính bao gồm các picture control để hiển thị ảnh, cácbutton, menu cho việc điểu khiển và các label để hiển thị kết quả như sau:

Page 86: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 86

Các button, label … được khai báo trong trong header LPRDlg.h và các hàm xử lý sự kiệnđược cài đặt trong file LPRDlg.cpp// LPRDlg.h : header file#pragma once#include <opencv2/core/core.hpp>#include "LprCore.h"#include "afxwin.h"

// CLPRDlg dialogclass CLPRDlg : public CDialogEx{public:

CLPRDlg(CWnd* pParent = NULL);enum { IDD = IDD_LPR_DIALOG };...

private:cv::Mat src;cv::Mat plate;cv::Mat character;LprCore lpr;string file_name;string text_recognition;

// Implementationprotected:

...DECLARE_MESSAGE_MAP()

public:

Page 87: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 87

afx_msg void OnUpdateFileOpenimage(CCmdUI *pCmdUI);afx_msg void OnBnClickedButton1();afx_msg void OnBnClickedButton2();CStatic text_result;...

};

Menu Open image hoặc button Load Image có chức năng tạo ra các dilog để mở file, fileđược mở sẽ được đọc, lư vào biến src và sẽ là ảnh đầu vào cho chương trình.// CLPRDlg.cppvoid CLPRDlg::OnUpdateFileOpenimage(CCmdUI *pCmdUI){

// Open Image...CFileDialog dlg(TRUE, _T("*.bitmap"), NULL, ...)if(dlg.DoModal() == IDOK){

file_name = to_string(dlg.GetPathName());src = imread(file_name);if(src.empty()) return;...

}

}

Button Show Result sẽ thực hiện các hàm xử lý và hiển thị kết quả khi nó được nhấn vào.// CLPRDlg.cppvoid CLPRDlg::OnBnClickedButton2(){

// Show resultsif(src.empty()) return;Mat disp_plate, disp_character;

lpr.set_image(src);lpr.do_process();vector<Mat> plates = lpr.get_plate_detection();vector<Mat> characters = lpr.get_character_isolation();vector<double> t = lpr.get_process_time();vector<string> text = lpr.get_text_recognition();if(plates.size() > 0){

plate = plates[0];resize(plate, disp_plate, Size(280,50));imshow("plate", disp_plate);character = characters[0];resize(character, disp_character, Size(280,50));imshow("character", disp_character);

text_recognition = text[0];

Page 88: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 88

text_result.SetWindowTextW(to_wstring(text_recognition));num_plate.SetWindowTextW(to_wstring((int)plates.size()));time_detection.SetWindowTextW(to_wstring(t[0]) + " ms");...

}else{

...}

}

Trong đoạn code trên, ta sẽ kiểm kiểm tra xem ảnh đầu vào có rỗng không trước khi đặtnó vào đối tượng lpr để thực hiện các phép xử lý. Sau khi thực hiện các phép xử lý, ta sẽkiểm tra xem nếu như kết quả đầu ra mà lớn hơn một biển số (plates.size() > 0) thì tasẽ hiển thị các kết quả lên. Lưu ý là hiện tại ta mới chỉ hiển thị kết quả đầu tiên, trongtrong ảnh có nhiều hơn một biển số ta có thể lần lượt hiển thị các kết quả đó một cách dễdàng.

Ngoài ra, chương trình còn có một số menu khác nhằm giúp ta lưu kết quả ảnh biển sốhoặc kết quả nhận dạng được dưới dạng một chuỗi text, menu hiển thị dialog cho việchuấn luyện mô hình SVM …

Hình sau mô tả kết quả chạy chương trình:

Page 89: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 89

Bài toán nhận dạng biển số xe này xây dựng một mô hình chung tổng quát, để ứng dụngtrong thưc tế ta cần giới hạn bớt lại một số điều kiện giúp cho việc tìm kiếm biển số đượcchính xác hơn, thêm vào đó các mẫu huấn luyện kí tự cần phải được sưu tập nhiều hơn,các vector đặc trưng cũng phải được tính toán tỉ mỉ hơn để giúp cho kết quả nhận dạng cóđộ chính xác cao hơn nữa. Ngoài ra, còn nhiều khía cạnh khác liên quanh đến bài toánứng dụng này trong thực tế như cần phải xây dựng hệ cơ sở dữ liệu để lưu trữ kết quả, sosánh kết quả, xây dựng hệ thống phần cứng, điều khiển phần cứng để điều khiển hệ thốngnhư các hệ thống thẻ từ RFID, hệ camera, hệ động cơ cho các điều khiển cơ học …

Page 90: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 90

3. MyCam, một số hiệu ứng ảnh với video.

Trong phần này, ta sẽ tạo một chương trình có thể thu nhận ảnh từ webcam và bằng cáchàm xử lý ảnh đơn giản như trong các ví dụ trên để tạo ra những hiệu ứng video đẹp mắt,chương trình có một số chức năng chính như: thu nhận video từ webcam, các chức năngxử lý video dựa trên các hàm xử lý ảnh (làm mờ, làm méo, chuyển đổi không gian màu…) chức năng chụp hình, ghi hình, điều chỉnh một số thông số … Nhưng trước hết chúngta hãy tìm hiểu cấu trúc của một video, cách xử lý trên video và sự hỗ trợ của thư việnOpenCV để làm việc với video như thế nào.

Cấu trúc của một file video. Ta thường xem một bài clip ca nhạc, một bộ phim trên máytính, trên Youtube … Chúng được gọi chung là một video. Một file video có thể có nhiềuđịnh dạng khác nhau như avi, mp4, mov …, xét về cầu trúc, một file video được tạo nêntừ nhiều thành phần khác nhau gọi là các track, chẳng hạn như track về video, track vềaudio, track về phụ đề …

Ta cần phải phân biệt về định dạng một file video (fileextention) và bộ giải mã video (video codec). Địnhdạng file, chẳng hạn như avi, mp4, mov, flv … là cáchcấu trúc của một file trong máy tính, nó chỉ ra cáchlưu trữ các dữ liệu trên ổ đĩa như thế nào, các phần tửđược xắp xếp ra sao … trong khi bộ giải mã (videocodec) chỉ ra video được mã hóa như thế nào, có đượcnén hay không và cách để đọc được dữ liệu video đó…. Các bộ giải mã mà ta thường thấy như H.264,XVID, MPEG, UYVY … Cùng là một định dạng file nhưng có thể có nhiều codec khácnhau, chẳng hạn như file *.avi có thể được mã hóa và giải mã bởi bộ codec XVID, UYVY…, điều đó lý giải một số máy tính đọc được file avi này nhưng một số máy tính khác lạikhông đọc được file avi đó, nguyên nhân là do máy chưa được cài bộ codec mà file avi đóchứa bộ mã mã hóa/giải mã. Tương tự, audio cũng có bộ codec riêng của nó như ACC,MP3 … nếu máy thiếu video codec mà có audio codec thì trong một số trường hợp, filevideo chỉ chạy được phần âm thanh mà không hiển thị được hình ảnh.

Xử lý video. Xử lý video là một khái niệm chung, nếu như theo đúng như cấu trúc trên củamột video, khi nói đến xử lý video có nghĩa là ta đang xử lý tất cả các track của một filevideo bao gồm track video, track audio … tuy nhiên trong khuôn khổ của việc áp dụng xửlý ảnh trong thực tế, ta chỉ làm việc với track video mà không làm việc với các track cònlại của file video. Xét một cách tổng quát, ta có thể xem những track video (hay videostream) là một chuỗi các ảnh lien tiếp nhau gọi là các frame, cách hiển thị các frame nàyđược quy định bởi một thông số về tốc độ frame (frame rate), chính là số frame xuất hiệntrong một giây và có đơn vị tính là frame/giây (frame per second hay fps). Việc xư lývideo thành ra lại là việc xử lý các frame ảnh này. Tuy nhiên cần phải chú ý là các phépxử lý ảnh tĩnh không có nhu cầu khắc khe về thời gian, tuy nhiên với video khi mà mỗigiây ta sẽ phải xử lý tới hang chục ảnh (các video thông thường có tốc độ 15-30 hình/giây

Track Video

Track Audio

Track Phụ đề

Track thời gian

Page 91: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 91

hoặc hơn nữa) thì ta cần phải tối ưu hóa phép xử lý tới mức có thể để video có thể chạyđược trong thời gian thực (không bị giật cục, tắc hình).

Thư viện OpenCV đối với xử lý video. Thư viện OpenCV chỉ hỗ trợ ta các hàm để bắt ảnhtừ một thiết bị như webcam, camera …, ghi các frame ảnh thành file video, còn việc làmxử lý video ta có thể sử dụng các hàm xử lý ảnh mà thư viện cung cấp để xử lý các framevideo. Tuy nhiên, thư viện OpenCV là một thư viện chuyên về xử lý ảnh, nên nó chỉ hỗtrợ track video trong cấu trúc của file video nói trên, nghĩa là khi thu video, ghi video tachỉ có phẩn hình (video stream) mà không thể nghe được phần tiếng. Một hạn chế nữa làban đầu OpenCV chỉ hỗ trợ để làm việc trên các video avi không nén, điều đó sẽ làm choviệc kích thước của việc lưu trữ file tăng lên rất nhiều, giả sử ta cần ghi một đoạn videoavi không nén trong thời gian là một phút với tốc độ là 25 hình/giây và ảnh thu từwebcam có kích thức 640x480 (ảnh màu, mỗi kênh màu của một pixel là 8 bit) khi đókích thước của một file cần phải có là 1*60*25*640*480*3*8 = 11059200000 bit tứckhoảng 1Gb. Tuy nhiên, hiện tại ta có thể làm việc được trên nhiều dạng file avi nén, filempeg, flv … miễn máy có cài các bộ codec tương ứng.Để đọc video từ một file video hoặc một thiết bị webcam, camera … thư viện OpenCVcung cấp cho ta lớp cv::VideoCapture. Ví dụ ta đọc từ một file video, ta có thể khởi tạođối tượng VideoCaputre như sau:

cv::VideoCapture capture = cv::VideoCapture(“D:/test.avi”);Để đọc video từ một thiết bị:

cv::VideoCapture capture = cv::VideoCapture(int device)Trong đó, device là một số nguyên chỉ ra thiết bị video trên máy, thông thường ta đểdevice bằng 0 để chỉ ra rằng thiết bị đang sử dụng là webcam mặc định trên máy tínhlaptop, nếu không biết cụ thể thiết bị đó là gì, ta có thể viết một chương trình nhỏ để kiểmtra thiết bị phù hợp bằng cách tằn biến device từ 0 tới một số nào đó (100 chẳng hạn), vớimỗi giá trị của device, kiểm tra xem capture có được tạo ra hay không, nếu thiết bị phùhợp, capture sẽ trả về một giá trị khác null, ngược lại nó sẽ là null:for(int device = 0; device < 100; ++device){

cv::VideoCapture capture = cv::VideoCapture(int device)if(capture)

cout<<”thiet bi phu hop” <<device <<endl}Để lấy ra được các frame ảnh trong video, ta có thể sử dụng hàm read(cv::Mat& img), kếtquả là frame của video sẽ được lưu vào trong biến img. Ta cũng có thể sử dụng toán tử >>để lấy ra các frame từ đối tượng capture:

cv::Mat img;capture << img;

Page 92: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 92

Để lưu các frame ảnh thành video, OpenCV xây dựng một lớp cv::VideoWriter.Constructor của đối tượng này như sau:

VideoWriter(string& filename, int fourcc, double fps, cv::Size frameSize, bool isColor)Trong đó, filename là file đường dẫn đầy đủ của file cần lưu, chẳng hạnD:/Movie/test.avi. fourcc là một số nguyên đại diện cho bốn kí tự của bộ codec dùng đểmã hóa/giải mã video. Nếu ta biết tên của bộ mã hóa (tên của bộ mã hóa là bốn kí tự) thìta có thể tìm được số nguyên này dựa vào macro CV_FOURCC(char c1, char c2, char c3,char c4), chẳng hạn như bộ mã hóa XVID sẽ được tìm bằng CV_FOURCC('X', 'V', 'I','D'). frameSize là kích thước của các frame ảnh, chẳng hạn 640x480 … isColor là mộtbiến kiểm tra xem video lưa vào có phải là dựa trên các ảnh màu hay không, nó có giá trịmặc định là true.Để lưa trữ video, ta có thể gọi hàm write, hoặc toán tử <<. Ví dụ nhỏ sau mô tả cách đọcvideo từ webcam và lưa ảnh thu được vào một file video.cv::VideoCapture capture = cv::VideoCapture(0);if(!capture.isOpened()) exit(0);cv::Mat img;capture >> img;cv::VideoWriter writer = cv::VideoWriter("D:/test.avi", CV_FOURCC('X', 'V','I', 'D'), 25, img.size(), true);while(1){

capture >> img;writer << img;

}

Tạo chương trình MyCam.

Chương trình được tạo ra dựa trên nền Dialog của MFC, cùng các hàm xử lý ảnh trongOpenCV đã biết như trên. Ta chia chương trình làm hai phần, một phần chứa các hàm xửlý ảnh và một phần thuộc về giao diện MFC. Phần chứa các hàm xử lý ảnh được đặt tên làlớp MyCamCore, nó bao gồm một file header định nghĩa giao diện hàm mycamcore.h vàmột file cài đặt mycamcore.cpp. Cấu trúc của hai file này có dạng như sau:

Mycamcore.h:

#pragma once#include <opencv2\core\core.hpp>

class MyCamCore{public:

MyCamCore(void);~MyCamCore(void);

void Gray(cv::Mat&, cv::Mat&);

Page 93: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 93

void Invert(cv::Mat&, cv::Mat&);void ToHSV(cv::Mat&, cv::Mat&);void Blur(cv::Mat&, cv::Mat&);……

};

Và mycamcore.cpp:

#include "MyCamCore.h"#include <opencv2\core\core.hpp>#include <opencv2\highgui\highgui.hpp>#include <opencv2\imgproc\imgproc.hpp>

using namespace cv;

MyCamCore::MyCamCore(void){}

MyCamCore::~MyCamCore(void){}void MyCamCore::Gray(Mat &src, Mat &dst){

if(src.channels() != 1)cvtColor(src, dst, CV_BGR2GRAY);

elsedst = src;

}

void MyCamCore::Invert(Mat &src, Mat &dst){

…}

void MyCamCore::ToHSV(Mat &src, Mat &dst){

…}

void MyCamCore::Blur(Mat &src, Mat &dst){

…}……}

Page 94: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 94

Cách cài đặt này là khá giống với cách cài đặt của chương trình xử lý ảnh đơn giản nhưtrên, nghĩa là cứ một ảnh đầu vào src thì qua phép xử lý sẽ cho ra một ảnh dst, tuy nhiêncó một số điểm khác đó là các hàm được cài đặt tối ưu hơn để giúp cho việc hiển thị videocó thể liên tục, không bị giật cục, chẳng hạn như với hàm invert. Hàm invert có tác dụnglàm đảo ngược các giái trị pixel ảnh, nghĩa là dst(x,y) = 255 – src(x,y). Để cài đặt hàmnày theo cách thông thường, ta phải duyệt qua tất cả các phần tử của các kênh màu vàthực hiện phép toán trên. Tuy nhiên cách tiếp cận tới các điểm ảnh trong một ảnh là tốnkhá nhiều thời gian và kết quả là tốn khá nhiều thời gian xử lý, làm cho video hiển thị lênbị giật cục. Để khắc phục tình trạng này ta quy ảnh về ma trận và thực hiện phép toán trênma trận dst = matran(255) – src, với matran(255) là một ma trận có cùng kích thước vàgiá tri tất cả các pixel toàn là 255. Vì OpenCV đã tối ưu hóa các phép tính trên ma trận(như sử dụng cả block các ô nhơ của biến ma trận thay vì tiếp cận từng điểm, chia phéptoán thành nhiều tiều trình nhỏ song song …) nên phép thực hiện trên sẽ nhanh hơn rấtnhiều. Hàm invert bây giờ được cài đặt như sau:

void MyCamCore::Invert(Mat &src, Mat &dst){

static Mat mask = 255*Mat::ones(src.rows, src.cols, CV_8UC1);std::vector<Mat> src_bgr;std::vector<Mat> dst_bgr;split(src, src_bgr);for(size_t i = 0; i < src_bgr.size(); ++i){

Mat temp = mask - src_bgr[i];dst_bgr.push_back(temp);

}cv::merge(dst_bgr, dst);

}

Trong hàm trên, phép cộng trừ ma trận chỉ được thực hiên trên một kênh màu, nên đầutiên ta tách ảnh thành 3 kênh màu riêng biệt, sau khi thực hiện phép toán xong ta lại trônlẫn 3 kênh mày này vào.

Đối với phần giao diện của chương trình, ta thiết kế một giao diện trên Dialog như sau:

Page 95: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 95

Các button được đặt tên biến có dạng btn_rgb, btn_capture …Video được hiển thị nhờhiển thị liên tiếp các frame ảnh lên một cửa sổ, cửu sổ này được lấy handle sau đó đượcgán vào picture box giống như cách hiển thị ảnh trên giao diện MFC trong các ví dụ trướcđã nói. Lớp cài đặt giao diện và cũng là chương trình chính khi ch ạy là lớp CMyCam, lớpnày được khởi tạo mặc định khi ta tiến hành tạo chương trình và chọn Dialog base. Ta sẽthêm vào header MyCamDlg.h và file cài đặt MyCamDlg.cpp một số biến, một số hàm đểđiều khiển chương trình. Header CMyCam.h sẽ có dạng như sau:

// MyCamDlg.h : header file#pragma once

#include <opencv2\core\core.hpp>#include <opencv2\highgui\highgui.hpp>#include "MyCamCore.h"#include "afxwin.h"

class CMyCamDlg : public CDialogEx{

public:CMyCamDlg(CWnd* pParent = NULL);enum { IDD = IDD_MYCAM_DIALOG };

Page 96: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 96

enum {RGB = 0, GRAY = 1, INVERT = 2, HSV = 3, YCRCB = 4, CANNY = 5,SOBEL = 6, BLUR = 7, NOISE = 8, ROT = 9, DIV = 10, DISTORT =

11};

protected:virtual void DoDataExchange(CDataExchange* pDX);

private:// Các biến, các hàm chức năng trong chương trìnhbool is_stoped;bool is_captured;int type;cv::Mat src;cv::Mat dst;double fps;std::string output_folder;cv::Size size;std::string video_file_name;std::string img_file_name;cv::VideoWriter writer;cv::VideoCapture capture;MyCamCore mycam;void Start();void InitAppearance();bool ReadConfig(std::string);std::string CreateFileName();

protected:HICON m_hIcon;// cac ham overridevirtual BOOL OnInitDialog();afx_msg void OnSysCommand(UINT nID, LPARAM lParam);afx_msg void OnPaint();afx_msg HCURSOR OnQueryDragIcon();afx_msg void OnClose();DECLARE_MESSAGE_MAP()

public:// các buttonCButton btn_snapshot;CButton btn_capture;CButton btn_setting;CButton btn_rgb;CButton btn_gray;...// Các hàm khi click vào buttonafx_msg void OnBnClickedSnapshot();afx_msg void OnBnClickedCapture();afx_msg void OnBnClickedSetting();afx_msg void OnBnClickedRgb();afx_msg void OnBnClickedGray();...

Page 97: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 97

};Các biến điều khiển của chương trình bao gồm is_stoped, is_captured, type … để nhằm

kiểm tra xem vòng lặp có tiếp tục được thực hiện hay không, button capture có được bấmchưa hoặc thể hàm xử lý ảnh nào mà ta đang gọi … Một số hàm được cài đặt trong fileMyCamDlg.cpp như:

Hàm void InitAppearance(): Hàm này có tác dụng cài đặt giao diện khởi tạocho chương trình, mục đích là làm cho giao diện trở nên đẹp hơn bằng cách thêmcác ảnh trang trí vào các button. Chẳng hạn như bốn button Snapshot, Capture,Open Folder, Setting được trang trí bằng các ảnh như sau:

Trong MFC, để đặt một ảnh vào một button hay một Control nào đó ta có thể dùnghàm SetBitmap với đối số của hàm là một HBITMAP, như vậy ta có thể cài đặthàm tạo ra giao diện trang trí InitAppearance như sau:

void CMyCamDlg::InitAppearance(){

HBITMAP snapshot_bmp = (HBITMAP)LoadImageA(0,"../MyCam/res/snapshot.bmp", IMAGE_BITMAP, 0, 0,LR_LOADFROMFILE);btn_snapshot.SetBitmap(snapshot_bmp);

HBITMAP capture_bmp = (HBITMAP)LoadImageA(0,"../MyCam/res/capture.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);btn_capture.SetBitmap(capture_bmp);

HBITMAP setting_bmp = (HBITMAP)LoadImageA(0,"../MyCam/res/setting.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);btn_setting.SetBitmap(setting_bmp);

HBITMAP folder_bmp = (HBITMAP)LoadImageA(0,"../MyCam/res/folder.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);btn_folder.SetBitmap(folder_bmp);...

}

Hàm bool ReadConfig(std::string) có tác dụng đọc file config của chươngtrình, file này chứa một số tham số như tốc độ hình trên giây (fps), folder chứa ảnhchụp hoặc video ghi vào (output_folder) … Ban đầu các tham số này chứa giá trị

Page 98: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 98

mặc định, tuy nhiên khi người dùng điều chỉnh bằng cách nhấn vào button Settingthì các tham số này sẽ được thay đổi và lưu lại trong file config, những lần chạysau, các thông số đó sẽ được đọc và có giá trị là giá trị mà người dùng đã tủy chỉnhcho chúng, hàm này sẽ được gọi ngay khi chương trình được chạy, hàm cài đặt chủyếu dựa vào các hàm chuẩn trong C++ có dạng như sau:

bool CMyCamDlg::ReadConfig(std::string file_name){

std::ifstream ifs(file_name);if(!ifs) return false;std::vector<std::string> lines;std::string line;while(ifs >> line){

lines.push_back(line);}if(lines.size() < 2) return false;fps = atof(lines.at(0).c_str());output_folder = lines.at(1);ifs.close();return true;

}

Hàm std::string CreateFileName() có tác dụng tạo ra một chuỗi tên khác nhauở các thời điểm khác nhau nhằm mục đích đặt tên cho các file ảnh chụp hoặc filevideo quay. Để tạo ra được các chuỗi tên không bị trùng nhau, tốt nhất là ta lấytheo thời gian, như vậy ở mỗi thời điểm chỉ có duy nhất một tên được tạo ra. Nếunhư ta nhấn vào nut Snapshot (chụp ảnh) ở thời điểm là 11 giờ 30 phút 10 giây thứ6 ngày 28 tháng 12 năm 2012 thì tên của file ảnh lưu lại sẽ là Fri Dec 28 11_30_102012.jpg. Ý tưởng cho việc cài đặt hàm này là lấy thời gian của hệ thống, sau đóthay thế các kí tự hai chấm “:” của thời gian bằng kí tự gạch nối dưới “_” (vì kí tựhai chấm là không hợp lệ để đặt tên trong window):std::string CMyCamDlg::CreateFileName(){

CreateDirectoryA(output_folder.c_str(), NULL);time_t rawtime;time(&rawtime);std::string t = (std::string)ctime(&rawtime);int pos = t.find(":");if(pos != t.npos){

t.replace(pos, 1, "_");t.replace(pos + 3, 1, "_");t.pop_back();

}std::string path = output_folder + "/";return path + t;

}

Page 99: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 99

14. Hàm void Start() là hàm chứa vòng lặp của có chức năng xử lý ảnh thu được từwebcam và hiển thị ảnh đã qua xử lý. Ảnh được xử lý như thế nào thì tùy vào biếnsố type. Hàm này sẽ được gọi ngay từ đầu chương trình sau khi các giao diện vàcác thông số đã được khởi tạo hết, biến is_stoped sẽ dung để kết thúc vòng lặp củahàm này, biến này được đặt giá trị là true khi chương trình kết thúc trong hàmOnClose(). Hàm Start được cài đặt như sau:

void CMyCamDlg::Start(){

if(!capture.isOpened()) return;size = cv::Size((int) capture.get(CV_CAP_PROP_FRAME_WIDTH),

(int) capture.get(CV_CAP_PROP_FRAME_HEIGHT));

while(!is_stoped){

capture >> src;switch(type){case RGB :

dst = src;break;

case GRAY :mycam.Gray(src, dst);break;

case INVERT:mycam.Invert(src, dst);break;

case HSV :mycam.ToHSV(src, dst);break;

case ... :...break;

default:dst = src;break;

}cv::imshow("Video", dst);if(is_captured && writer.isOpened())

writer << dst;cv::waitKey(1);

}}

Page 100: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 100

15. Các hàm dùng để điều khiển quá trình xử lý video khi click vào các buttonđều có chung một dòng lệnh và có dạng như sau (giả sử hàm khi click vào buttonbtn_invert):void CMyCamDlg::OnBnClickedInvert(){

type = INVERT;

}16. Hàm khi click vào button Snapshot (chụp ảnh):

void CMyCamDlg::OnBnClickedSnapshot(){

img_file_name = CreateFileName() + ".jpg";std::vector<int> p(2);p.at(0) = CV_IMWRITE_JPEG_QUALITY;p.at(1) = 90;if(!dst.empty()){

cv::imwrite(img_file_name, dst, p);}

}Hàm này có tác dụng lưu ảnh đã qua xử lý với tên file ảnh là tên chuỗi được tạo ratừ hàm CreateFileName như đã nói ở trên và định dạng là jpg.

17. Hàm khi click vào button Capture (ghi hình)void CMyCamDlg::OnBnClickedCapture(){

is_captured = !is_captured;if(is_captured){

video_file_name = CreateFileName() + ".avi";if(size.height > 0 && size.width > 0)

writer = cv::VideoWriter(video_file_name,CV_FOURCC('X','V', 'I', 'D'),8, size, true);btn_capture.SetBitmap((HBITMAP)(LoadImageA(0,"../MyCam/res/stop.bmp",0, 0, 0, LR_LOADFROMFILE)));btn_snapshot.EnableWindow(false);btn_setting.EnableWindow(false);btn_folder.EnableWindow(false);

}

else{

btn_capture.SetBitmap((HBITMAP)(LoadImageA(0,"../MyCam/res/capture.bmp",0, 0, 0, LR_LOADFROMFILE)));btn_snapshot.EnableWindow(true);btn_setting.EnableWindow(true);btn_folder.EnableWindow(true);

Page 101: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 101

if(writer.isOpened())writer.release();

}}

Khi button Capture được nhấn, đối tượng writer sẽ được tạo mới với tên của filevideo được tạo ra từ hàm CreateFileName(), định dạng ta sử dụng ở đây là avi vớicodec là XVID, đồng thời trạng thái của chương trình cũng chuyển sang trạng tháighi hình, nghĩa là button Capture bây giờ được thay bởi ảnh có tên lá Stop, cácbutton khác như Snapshot, Open Folder, Setting sẽ bị vô hiệu hóa. Nếu button nàyđược click lần thứ hai (khi quá trình ghi hình đang diễn ra) thì ta sẽ ngừng việc ghivideo lại, hủy đối tượng writer và trạng thái chương trình trở về như ban đầu.

18. Hàm khi click vào button Open Folder (mở folder chứa file ảnh file video):void CMyCamDlg::OnBnClickedFolder(){

std::string str_command = "explorer.exe " + output_folder;const char *command = str_command.c_str();system(command);

}

Hàm này sẽ mở folder chứa chứa ảnh chụp và video được ghi lại bằng hàmsytem(command), trong đó command là một chuỗi kiểu char để gọi hàmexplorer.exe với đối số là đường dẫn tới folder cần mở.

19. Hàm khi click button Setting:void CMyCamDlg::OnBnClickedSetting(){

Setting setting;is_stoped = true;setting.DoModal();fps = setting.Fps;output_folder = setting.OutputPath;if(fps <= 0) fps = 8;if(output_folder == "") output_folder = "..\\Output";

std::ofstream ofs("config.txt");if(!ofs) return;ofs << fps <<std::endl;ofs << output_folder;ofs.flush();ofs.close();

is_stoped = false;Start();

}

Page 102: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 102

Hàm này sẽ mở ra một dialog để người dung đặt các giá trị cho chương trình nhưfps, output_folder (bạn đọc có thể để thêm vào một số thông số như điều chỉnh độsang cho ảnh trong quá trình xử lý chẳng hạn) …Kết quả sau phép cài đặt nàyđược ghi vào file config và được sử dụng cho các lần chạy chương trình tiếp theo.

Sau đây là kết quả chạy chương trình với trường hợp tạo ra các hình đối xứng và ảnh âmbản:

Page 103: Ung dung xu ly anh trong thuc te voi thu vien open cv

Nguyễ

n Văn

Lon

g

Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV

Tác giả: Nguyễn Văn Long – [email protected] Page 103

Chương trình trên đư ợc cài đặt ở dạng đơn giản để tạo ra một chương trình có thể làmviệc với các video stream mà cụ thể ở đây là thu từ webcam. Trên một khía cạnh nào đónó giống với ý tưởng của chương trình Cyberlink YouCam khá nổi tiếng và đã đượcthương mại hóa, tuy nhiên nó đơn giản hơn khá nhiều cả về chức năng lẫn giao diện. Bạnđọc có thể tự phát triển thêm nhiều hàm, nhiều modul để tự tạo ra cho mình một chươngtrình thực sự thú vị dựa trên những điều cơ bản nhất.

------------------------------Hết------------------------------Một số vấn đề dự kiến sẽ được thảo luận trong phần tới: Phát hiện - nhận dạngmặt người, nhận dạng chữ viết tay, theo dõi đối tượng (object tracking), cameracalibration - sterio vision và tái tạo không gian 3D, robot vision – một sô vấn đềvề việc áp dụng xử lý ảnh trong các kì thi robocon …