Code c xử lý ảnh

Tài liệu Ứng dụng xử lý ảnh trong thực thế với thư viện opencv c/c++: Ng uy ễn V ăn L on 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 – Page 1 Ứng dụng xử lý ảnh trong thực thế với thư viện OpenCV C/C++ Nguyễn Văn Long Ng uy ễn V ăn L on 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 – 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ộng rã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à khoa họ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ực như 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ân tay 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ô...

Code c xử lý ảnh
103 trang | Chia sẻ: Khủng Long | Lượt xem: 2052 | Lượt tải: 3
Code c xử lý ảnh

Bạn đang xem trước 20 trang mẫu tài liệu Ứng dụng xử lý ảnh trong thực thế với thư viện opencv c/c++, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Ng uy ễn V ăn L on 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 – Page 1 Ứng dụng xử lý ảnh trong thực thế với thư viện OpenCV C/C++ Nguyễn Văn Long Ng uy ễn V ăn L on 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 – 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ộng rã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à khoa họ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ực như 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ân tay 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 Tuy nhiê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âm mộ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 mang nặ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ắm bắ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ễn củ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ông nhiề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 cho ra đờ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ễn giả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ẫn là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ái niệ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ôn ngữ C++ với sự giúp đỡ của thư viện OpenCV, một thư viện mã nguông mở được đánh giá là mạnh mẽ về tốc độ xử lý đáp ứng được các ứng dụng trong thời gian thực. 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ác bộ 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 sinh viê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ùn 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ốt tốt đẹp và lời cảm ơn sâu sắc tới độc giả Ng uy ễn V ăn L on 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 – Page 3 Hướng dẫn sử dụng sách Cuố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ẫu chươ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 OpenCV khá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 Studio 2010. 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ịch khá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á chi tiết và giải thích đầy đủ, 3 project được tác giả mô tả chung chung hơn. Do đó bạn đọc nế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ới hạ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ác nhau để 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ảm thấy là thỏa đáng nhất Ng uy ễn V ăn L on 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 – 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 5 2. Phiên bản OpenCV 1 hay OpenCV 2 5 3. 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 12 2. Không gian màu, chuyển đổi không gian màu 13 3. Điều chỉnh độ sang, độ tương phản 17 4. Ảnh nhị phân, nhị phân hóa với ngưỡng động 19 5. Histogram, cân bằng histogram 23 6. Phóng to, thu nhỏ, xoay ảnh 27 7. Lọc số trong ảnh 30 8. Các phép toán hình thái học trong ảnh 37 9. Tìm biên ảnh với bộ lọc Canny 43 10. 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 51 2. Khởi tạo project MFC 51 3. Làm việc với các điều khiển (Control) 54 4. Chuyển đổi các kiểu dữ liệu trong MFC 59 5. 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 64 2. Nhận dạng biển số xe 73 3. MyCam, một số hiệu ứng ảnh trong video 90 Ng uy ễn V ăn L on 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 – 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áy với hơn 500 hàm và hơn 2500 các thuật toán đã đư ợc tối ưu về xử lý ảnh, và các vấn đề liên quan tới thị giác máy. OpenCV được thiết kế một cách tối ưu, sử dụng tối đa sức mạnh của các dòng chip đa lõi để thực hiện các phép tính toán trong thời gian thực, nghĩa là tốc độ đáp ứng của nó có thể đủ nhanh cho các ứng dụng thông thường. OpenCV là thư viện được thiết kế để chạy trên nhiều nền tảng khác nhau (cross-patform), nghĩa là nó có thể chạy trên hệ điều hành Window, Linux, Mac, iOS Việc sử dụng thư viện OpenCV tuân theo các quy định về sử dụng phầ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ương mại. Dự án về OpenCV được khởi động từ những năm 1999, đến năm 2000 nó được giới thiệu trong một hội nghị của IEEE về các vấn đề trong thị giác máy và nhận dạng, tuy nhiên bản OpenCV 1.0 mãi tới tậ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ản trước có giao diện của C) và có khá nhiều điểm khác biệt so với phiện bản thứ nhất. Thư viện OpenCV ban đầu được sự hỗ trợ từ Intel, sau đó được hỗ trợ bở Willow Garage , một phòng thí nghiệm chuyên nghiên cứu về công nghệ robot. Cho đến nay, OpenCV vẫn là thư viện mở, được phát triển bởi nguồn quỹ không lợ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ản chí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òn gọ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 hai phiên bản này. - OpenCV 1.x (bao gồm bản 1.0 và bản pre-release 1.1) dựa trên giao diện C, cấu trú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ựa trê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 đối tượng, nghĩa là khi một đối tượng mới được tạo ra, ta phải luôn chú ý để giải phó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. Ng uy ễn V ăn L on 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 – 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ểu hơn so với C, một phần là các hàm trong OpenCV 2.x đã được tối ưu hóa nhiều bướ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ãy xét ví dụ về việc phát hiện đường tròn trong ảnh mầu dựa vào thuật toán Hough, các bước để thực hiện là load một ảnh mầu, chuyển sang ảnh nhị phân, tìm biên dựa trên bộ lọc canny và phát hiện đường tròn dựa trên thuật toán Hough. OpenCV 1.x thực hiện như sau: // Phát hiện đường tròn trong ảnh OpenCV 1.x IplImage* src = cvLoadImage(“image.jpg”); IplImage* gray = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1); cvCvtColor(src, gray, CV_BGR2GRAY); cvCanny(gray, gray, 10, 30, 3); CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 1, 50, 100, 50); Trong khi đó, OpenCV 2.x thực hiện như sau: // Phát hiện đường tròn trong ảnh OpenCV 1.x Mat src = imread(“image.jpg”); Mat gray; CvtColor(src, gray, CV_BGR2GRAY); Canny(gray, gray, 10, 30, 3); Vector 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, đối tượng storage (đối tượng trung gian, không có ý nghĩa về mặt sử dụng) cũng không cần phải khởi tạo (và do đó không cần giải phóng). - Thư viện OpenCV 1.x tuy chứa một lượng lớn hàm xử lý và thuật toán, tuy nhiên nó 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 đối tượ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ột phần giao diện C để tương thích với các phiên bản của OpenCV 1.x Từ một số đặc điểm trên 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ẫn cò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ành cho 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 Ng uy ễn V ăn L on 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 – 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ản mới nhất tại địa chỉ . Chọn bản đã build sẵn phù hợp với hệ điều hành đang dùng, bản OpenCV được sử dụng trong 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 download về 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ới OpenCV 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ản trướ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++, Win32 console 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ọn Application type là Console application và Additional option là Empty project, nhấn Finish để kết thúc quá trình khởi tạo Ng uy ễn V ăn L on 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 – 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 file nguồn để chương trình có thể chạy được, trong Solution Explorer ta click chuột phải vào Source Files, chọn Add -> New Item Hộp thoại Add New Item hiện ra, ta chọn kiểu cần thêm vào là C++ File(.cpp) đồng thời trong ô Name ta đặt tên cho file thêm vào, giả sử là FirstApp.cpp. Bây giở trong file này ta có thể thêm vào các #include và gọi hàm main() để chạy chương trình. Để chương trình có thể chạy được vớ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ọn VC++ Directories, tương ứng bên phải, ta tìm mục Include Directories và Library Directories. Ta sẽ chỉ đường dẫn hai thư mục này đến các phần tương ứng của thư viện OpenCV. Mục Include Directories, ta tùy chỉnh ở ô bên phải tới C:\opencv\build\include Mục Library Directories trỏ đến thư mục C:\opencv\build\x86\vc10\lib nếu như 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. Ng uy ễn V ăn L on 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 – Page 9 Tiếp theo, trong hộp thoại opencv Property Pages -> Configuration Properties -> Linker chọn Input, tương ứng ở ô bên phải, thêm vào các giá trị cho mục Additional Dependencies 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ác header 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ần sử dụng tới các hàm về video, khi đó ta thêm header #include thì trong phần Additional Dependencies ta phải khai báo thêm opencv_video243d.lib. Chữ d đứng cuối các file trên thể hiện ta đang hoạt động ở chế độ debug, ta có thể thêm các lib khô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ác file *.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ương trình (file *.exe). Các file *.dll này nằm trong mục C:\opencv\build\x86\bin với win 32 bit hoặc C:\opencv\build\x64\bin với win 64 bit. Với các phiên bản OpenCV cũ hơn, ta cần copy luôn file tbb_debug.dll (trong chế độ debug) hoặc tbb.dll (trong chế độ release) vào thư mục chứa file *.exe. tbb.dll (Thread building block) là file khá quan trọng, thiếu nó chương trình sẽ báo lỗi. Sau khi đã hoàn tất việc chỉ dẫn thư mục chứa header, library và link tới cá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àm việc của OpenCV. #include #include #include #include using namespace std; using namespace cv; void main() { ... } Ng uy ễn V ăn L on 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 – 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++ Project xuất hiện, trong hộp thoại ta chọn Project name là opencv, Project type là Hello World C++ Project (Có thể chọn là Empty Project), 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ọn Include rồi dẫn đường dẫn tới mục Include của OpenCV là C:\opencv\build\include. Trong phần MinGW C++ Linker chọn Library và chọn các mục như sau: click vào dấu cộng ở Library search path (-L) và dẫn tới thư mục lib: C:\opencv\build\x86\mingw\lib đối với Windows 32 bit hoặc C:\opencv\build\x64\mingw\lib đối với Windows 64 bit. Tiếp đó click vào dấu "cộng" để thêm Library(-I) vào, các library cần thêm lần lượt là: opencv_core243, opencv_highgui243, opencv_imgproc243 ... nói chung là tùy vào nhu cầu sử dụng có thể thêm một hoặc nhiều lib vào. Ng uy ễn V ăn L on 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 – 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ương trình để chương trình có thể chạy thành công. Ng uy ễn V ăn L on 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 – 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 để load và hiển thị một ảnh. Chương trình như sau: #include "stdafx.h" #include #include #include using namespace std; using namespace cv; int main() { cout<<"Chuong trinh dau tien"< T Hình sau mô tả một ảnh nhị phân với ngưỡng nhị phân T = 100 Ng uy ễn V ăn L on 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 – 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àm như sau: threshold(cv::InputArray src, cv::OutputArray dst, double thresh, int maxval, int type) Trong đó, src là ảnh đầu vào một kênh màu (ảnh xám ), dst là ảnh sau khi được nhị phân hóa, thresh là ngưỡng nhị phân, maxval là giá trị lớn nhất trong ảnh (maxval = 255 đối với ảnh xám), type là kiểu nhị phân có thể là CV_THRESH_BINARY, CV_THRESH_BINARY_INV, CV_THRESH_OTSU lần lượt là nhị phân hóa thông thường, nhị phân hóa ngược và nhị phân hóa 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ỗi ngưỡng T khác nhau thì ta có những ảnh nhị phân khác nhau. Hình sau mô tả 3 ảnh nhị phân tương ứng với ngưỡng T = 50, T = 100 và T = 150. 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áng khác nhau (không cần quan tâm tới ngưỡng T), người ta dùng một kĩ thuật sao cho với mọi ngưỡng xám khác nhau ta luôn thu được một ảnh nhi phân tốt. Kĩ thuật đó gọi là kĩ Ng uy ễn V ăn L on 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 – 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ựa trê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ác vù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ử ta tính toán ngưỡng cho một vùng nào đó dựa trên độ trung bình của các pixel trong vùng đó (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 trong mộ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ình cộng của tất cả các ô trong cửa sổ T = (55 + 10 + 100 + )/25 = 65.6. Chương trình nhị phân hóa với ngưỡng động như sau // Adaptive Threshold #include #include #include #include using namespace std; using namespace cv; int main() { cout<<"Nhi phan anh voi nguong dong"< 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); else rectangle(src1, r, Scalar(0, 255, 0), 1, 8, 0); } imshow("Ket qua phat hien truoc phep gian no", src1); vector > 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); else rectangle(src2, r, Scalar(0, 255, 0), 1, 8, 0); } Ng uy ễn V ăn L on 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 – 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àm findContours sẽ tìm đường bao quanh của các đối tượng đã được nhị nhâp hóa và lưu đường bao này vào một vector là các điểm nằm trên đường bao đó, hàm boundingRect sẽ tìm ra một hình chữ nhật bao một tập điểm của một đường bao được tìm ra từ hàm findContours. Dựa vào tỉ lệ kích thước chiều dài/rộng của hình chữ nhật này ta có thể xem xét liệu đường bao mà ta tìm được có phải là một vùng của biển số hay không, nếu phải ta sẽ vẽ hình chữ nhật này thông qua hàm rectangle với một màu khác (Scalar(0, 0, 255) : màu đỏ) và nét đậm hơn. Kết quả chạy chương trình như sau 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 ta cũ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ó xu hướ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ó đinh vit ở 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 ra các kí tự này. Ng uy ễn V ăn L on 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 – 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ết quả 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ương phá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 +1 Sau đó 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ẽ được là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ỏ đi nhữ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ới giá 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ốn khả năng sau: Ng uy ễn V ăn L on 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 – 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 A lớ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 A lớ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 A lớ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ớn gradient 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ơn ngưỡ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ưỡng dưới thì đó không phải điểm biên. Trong trường hợp giá trị gradient nằm giữa ngưỡng trê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ạnh củ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ên mẫ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: Ng uy ễn V ăn L on 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 – Page 45 #include "stdafx.h" #include #include #include 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ác tiểu tiết không mong muốn trong đường biên sau này). Về nguyên tắc, bộ lọc GaussianBlur 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ấu trú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 đầu và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 y sẽ được lấy theo sigmaX. BorderType là cách nội suy biên đối với những điểm ảnh tràn ra ngoà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ác nhau như hình sau. Ng uy ễn V ăn L on 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 – 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 trong phâ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 gian củ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 Hough cho đường thẳng và đường tròn. Ng uy ễn V ăn L on 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 – 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ưới dạ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 đường thẳ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ểu diễn đường thẳng trong hệ tọa độ cực. Phương trình đường thẳng trong hệ tọa độ cực có 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 (n pixel) 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ủa thuậ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ương tự như với đường thẳng, phương trình đường trò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ới 360 độ. 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= −= − Ng uy ễn V ăn L on 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 – 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ất hiệ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 R từ 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ước giá 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ết quả ở 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 đường thẳ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 ra củ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ới 360 độ), 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ứa tọ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ủa hà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ương trình sẽ tự động dò biên bằng phương pháp canny), circles là vector chứa kết quả phát Ng uy ễn V ăn L on 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 – 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ưỡng trê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ần phá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ương trì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 #include #include #include 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 thang Mat canny; Canny(gray, canny, 100, 200, 3, false); vector lines; HoughLinesP(canny, lines, 1, CV_PI/180, 50, 60, 10); // Tim duong tron vector circles; HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 100, 200, 100, 0, 0); // Ve duong thang, duong tron len anh for(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); } Ng uy ễn V ăn L on 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 – Page 50 for(int i = 0; i < circles.size(); i++ ) { Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); // Tam int radius = cvRound(circles[i][2]); // Ban kinh circle(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 đổi qua ả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àm canny để 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 đường trò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. Ng uy ễn V ăn L on 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 – 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ác cô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ản hơ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ện Dialog 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ột phầ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 Other langguge trước khi hiển ra Visual C++) sau đó chọn MFC Application. Ta đặt tên cho project trong trường Name (giả sử tên là xyz) sau đó click OK để đến bước tiếp theo. Ở bước tiếp theo, tiếp tục chọn Next ta được hộp thoại sau Ng uy ễn V ăn L on 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 – Page 52 Application type chọn Dialog based, Project type chọn MFC standard Chú ý rằng việc tick vào chọn Use Unicode libraries sẽ có những ý nghĩa và cách dùng khác nhau, ta sẽ xét trường hợp này sau. Ta nhấn next để đi tới hộp thoại tiếp theo, hộp thoại này cho phép ta tùy chỉnh một số chức năng của cửa sổ như có thêm nút phóng to, thu nhỏ, menu có thể để mặc định và chọn next Ng uy ễn V ăn L on 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 – 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ởi tạo lớp. Ta chọn là CxyzDlg và nhấn Finish Ng uy ễn V ăn L on 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 – 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 control Khi muốn sử dụng một Control nào, ta kéo control đó từ Toolbox và cho vào dialog. Để làm việc với các control một cách dễ dàng, ta nê đặt tên biến cho các control. Để đặt tên biến cho một control, ta click chuột phải vào control, sau đó chọn Add Variable. Một hộp thoại Add Member Variable Wizard hiện ra và trong mục variable name ta đặt tên cho control đó. Chú ý là đối với các control mà ID của nó có dạng IDC_STATIC (như Static Text) thì ta chỉ có thể đặt tên biến được khi đổi Ng uy ễn V ăn L on 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 – 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 ô Edit Control như sau: CString text; text_box.GetWindowTextW(text); Giá trị lấy được sẽ được lưu trong một chuỗi CString text. Một điểm cần chú ý từ giờ về sau là các hàm trong MFC khi được build ở chế độ Unicode và chế độ Multi-byte thì các gọi các hàm, cách sử dụng các hàm cũng có những điểm khác nhau nho nhỏ, chẳng hạn như cũng là lấy giá trị trong một ô Edit Control nhưng nếu ở chế độ Multi-byte ta sẽ dùng lệ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ách vào Project -> Properties (hoặc nhấn Alt + F7), hộp thoại Properties hiện ra, chọn Configuration 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àm SetWindowText(CString text) text_box.SetWindowTextW(_T("text")); // Unicode text_box.SetWindowTextA("text"); // Multi-byte Ng uy ễn V ăn L on 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 – 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 ảnh bitmap 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 control Ta sử dụng hàm EnableWindow để cho phép control có được phép sử dụng hay không text_box.EnableWindow(flase); // Vo hieu hoa edit control text_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 thanh trượ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 Box Combo 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 trong properties 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ảng properties ở mục Data ta điền vào HaNoi;ThanhHoa;DaNang các lựa chọn được phân cá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ục Data của Combo box Dialog mở file và lưu file Mụ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 file CFileDialog dlg(FALSE, _T("*.jpg"), NULL, OFN_FILEMUSTEXIST|OFN_PATHMUSTEXIST|OFN_HIDEREADONLY,Filter,NULL);// Luu file Filter 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ợp trê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ếu muố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 Ng uy ễn V ăn L on 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 – 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ưu file 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 button Hầ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ác vớ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 slider control Để 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, ta chọn BN_CLICKED rồi chọn OnBnClickedButton1, 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 button1 void CxyzDlg::OnBnClickedButton1() { MessageBoxA(NULL, "Button1 duoc click", "Thong bao", 0); } Xử lý sự kiện khi thay đổi lựa chọn Combo Box Sự 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ột Message 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; Ng uy ễn V ăn L on 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 – 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 menu Menu trong MFC được xem là resource của chương trình. Việc thêm menu vào dialog đòi hỏ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ác resource của chương trình bằng cách từ menu của Visual Studio chọn View -> Resource View (hoặc nhận tổ hợp phìm Ctrl + Shift + E). Cửa sổ Resource View hiện ra, ta click chuột phải vào đó, chọn Add->Resource Cửa sổ Add Resource hiện ra, ta chọn Menu và click vào button New. Ngay sau đó ta sẽ có một resource chứa menu chống, ta tiến hành điền tên các menu mà ta muốn chương trình thực hiện. Hình bê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ủa dilog, trong mục Menu chọn IDR_MENU1, với IDR_MENU1 chính là ID của menu ta vừ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 Add Event Handler Ng uy ễn V ăn L on 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 – Page 59 Ở hộp thoại Event Handler Wizard ta chọn lớp mà ta muốn thêm menu vào sau đó click vào button Add and Edit để đi tới hàm xử lý sự kiện khi click vào menu, ví dụ sau là ta gọ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 đọc tham 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 đầu vào, đầu ra của một việc nào đó, chẳng hạn khi ta dùng CFileDialog để mở một đường dẫn sau đó đọc ảnh từ đường dẫn này, kết quả trả về đường dẫn của CFileDialog là một chuỗi CString tuy nhiên hàm cv::imread lại đọc ảnh từ một chuỗi string, do đó ta phải Ng uy ễn V ăn L on 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 – 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ại Vớ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 string CString 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 string CString 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ại Cá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 Unicode int num1 = _wtoi(cstr1); // Chuyen sang so nguyen su dung Unicode CString cstr2 = "10.5"; float num2 = atof(cstr2) // Chuyen sang so thuc khong su dung Unicode float num2 = _wtof(cstr2) // Chuyen sang so thuc su dung Unicode 13. Chuyển số sang CString char s1[20]; int num = 10; sprintf(s1, "%d", num); CString cstr1 = s1; // chuyen so nguyen sang cstring - khong unicode CString cstr2; cstr2.Format(_T("%d"), num); // chuyen so nguyen sang cstring unicode Tương tự với số thực ta có thể chuyển đổi tương tự và thay "%d" bằng "%f". Ng uy ễn V ăn L on 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 – 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ện như 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ủa chúng được đổi và chúng được đựt tên biến là l_width, l_height, l_channels, l_path (đối với các static text dùng để hiển thị kết quả). ID của Picture Control được đổi thành IDC_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|All Files(*.*)|*.*||"); 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 Picture control Labelabels Group Box Ng uy ễn V ăn L on 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 – Page 62 ::ShowWindow(hParent, SW_HIDE); // resize va hien thi anh cv::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ị ảnh trong OpenCV, sau đó hàm SetParent() sẽ đặt cửa sổ này vào điều khiển Picture Control củ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 MFC thay 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ích thướ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 anh if(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ưa thì 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 Ng uy ễn V ăn L on 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 – 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ậy các giá trị như int, float, double .. muốn hiển thị được trên các điều khiển của MFC ta phả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 trinh this->OnCancel(); } Kết quả chạy chương trình như sau: Ng uy ễn V ăn L on 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 – 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án xử 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ại giữ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ằng histogram 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ỉnh sử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ần chuyên về các hàm xử lý ảnh được lưu trong một lớp và đặt tên là ImgProcessing. Phần cho 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ều chỉ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 định nghĩ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 Photo EditorDlg.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 Ng uy ễn V ăn L on 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 – 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ùy thuộ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ĩa header của lớp ImgProcessing như sau: // heade file #pragma once #include using namespace cv; class ImgProcessing { public: ImgProcessing(void); ~ImgProcessing(void); // Ham xu ly void 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): // Brightness void 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(i,j) = saturate_cast(src.at(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(i,j)[k] = saturate_cast(src.at(i,j)[k] + value); } } Ng uy ễn V ăn L on 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 – 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ớp nà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ãy click 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ộp thoạ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ành quá 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 click chuộ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ọn tham số có tên là silder1, slider2, slider3 , các biến là nhãn (Static text) dung để đặt tên hiể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ến hiể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 ra số 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 Ng uy ễn V ăn L on 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 – 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ý trong file 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ào yê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 phan this->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àm xử 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_; Ng uy ễn V ăn L on 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 – 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(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ến thà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 OK this->is_ok = true; ImgPreview::OnCancel(); } Ng uy ễn V ăn L on 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 – 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ác mở 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ác nhau 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ột phải vào Project sau đó chọn Add -> Resource-> Menu. Sau khi đã hoàn thành hệ thống menu ta hãy vào dialog chính, ở mục Properties của dialog này chọn Menu và chọn ID củ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ột phải vào menu cần xử lý sau đó chọn Add Event Handler. Sự kiện click chuột vào menu Open có dạng như sau: void CMyPhotoEditorDlg::OnUpdateFileOpen1(CCmdUI *pCmdUI) { // Open Image static CString Filter=_T("image files (*.bmp; *.jpg) |*.bmp;*.jpg|All Files (*.*)|*.*||"); 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 Ng uy ễn V ăn L on 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 – 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ện click chuột vào menu đó được sử lý có dạng như sau: void CMyPhotoEditorDlg::OnUpdateAdjustmentsInvert(CCmdUI *pCmdUI) { // Invert Image process.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ền tham số cho dialog này và nhận tham số để xử lý. Sự kiện của những menu đó có dạng như sau: void CMyPhotoEditorDlg::OnUpdateAdjustmentsThreshold(CCmdUI *pCmdUI) { // Threshold ImgPreview 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 để cho lớp này biết kiểu cần x

Các file đính kèm theo tài liệu này:

  • Code c xử lý ảnh
    tailieu.pdf