Giáo trình Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV
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.
Trang 1
Trang 2
Trang 3
Trang 4
Trang 5
Trang 6
Trang 7
Trang 8
Trang 9
Trang 10
Tải về để xem bản đầy đủ
Tóm tắt nội dung tài liệu: Giáo trình Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV
hư 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 class MyCamCore { public: MyCamCore(void); ~MyCamCore(void); void Gray(cv::Mat&, cv::Mat&); Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 92 voidỨngInvert(cv::Mat& dụng xử lý ,ả nhcv::Mat&); trong thực tế với thư viện OpenCV void ToHSV(cv::Mat&, cv::Mat&); void Blur(cv::Mat&, cv::Mat&); }; Và mycamcore.cpp: #include "MyCamCore.h" #include #include #include 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); else dst = src; } void MyCamCore::Invert(Mat &src, Mat &dst) { } void MyCamCore::ToHSV(Mat &src, Mat &dst) { } void MyCamCore::Blur(Mat &src, Mat &dst) { } } Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 93 Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV 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ên có 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ị video có 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ụng là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àm nà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ốn khá 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ên bị 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ên ma 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ép toá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ất nhiề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 src_bgr; std::vector 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 đầu tiê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ôn lẫ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: Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 94 Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV 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 đó được gá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ớp nà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 #include #include "MyCamCore.h" #include "afxwin.h" class CMyCamDlg : public CDialogEx { public: CMyCamDlg(CWnd* pParent = NULL); enum { IDD = IDD_MYCAM_DIALOG }; Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 95 enumỨng{RGB d ụ=ng 0, x GRAYử lý =ả nh1, trongINVERT th= 2,ực HSVtế v=ớ 3,i thư YCRCB vi ệ=n 4,OpenCV 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ình bool 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 override virtual 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 button CButton btn_snapshot; CButton btn_capture; CButton btn_setting; CButton btn_rgb; CButton btn_gray; ... // Các hàm khi click vào button afx_msg void OnBnClickedSnapshot(); afx_msg void OnBnClickedCapture(); afx_msg void OnBnClickedSetting(); afx_msg void OnBnClickedRgb(); afx_msg void OnBnClickedGray(); ... Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 96 Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV }; 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ấm chư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 file MyCamDlg.cpp như: Hàm void InitAppearance(): Hàm này có tác dụng cài đặt giao diện khởi tạo cho 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êm cá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ùng hàm SetBitmap với đối số của hàm là một HBITMAP, như vậy ta có thể cài đặt hà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ương trình, file này chứa một số tham số như tốc độ hình trên giây (fps), folder chứa ảnh chụp hoặc video ghi vào (output_folder) Ban đầu các tham số này chứa giá trị Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 97 mặỨc đngịnh, d ụtuyng nhiên xử lýkhiả ngưnh ờtrongi dùng thđiềựu cch tỉếnhv ớbằing thư cách vi nhệnấ nOpenCV vào button Setting thì các tham số này sẽ được thay đổi và lưu lại trong file config, những lần chạy sau, các thông số đó sẽ được đọc và có giá trị là giá trị mà người dùng đã tủy chỉnh cho 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 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 file video quay. Để tạo ra được các chuỗi tên không bị trùng nhau, tốt nhất là ta lấy theo 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ếu như 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_10 2012.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; } Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 98 Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV 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ến số 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ủa hàm này, biến này được đặt giá trị là true khi chương trình kết thúc trong hàm OnClose(). 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); } } Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 99 15. ỨngCác dụ hàmng xdùngử lý đảểnhđiề utrong khiển thquáự trìnhc tế xvửớlýi thư video vi khiện clickOpenCV 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 button btn_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 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 ra từ 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); Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 100 Ứng dụngif (writer.isOpened())xử lý ảnh trong thực tế với thư viện OpenCV writer.release(); } } Khi button Capture được nhấn, đối tượng writer sẽ được tạo mới với tên của file video được tạo ra từ hàm CreateFileName(), định dạng ta sử dụng ở đây là avi với codec là XVID, đồng thời trạng thái của chương trình cũng chuyển sang trạng thái ghi hình, nghĩa là button Capture bây giờ được thay bởi ảnh có tên lá Stop, các button 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 ghi video 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àm sytem(command), trong đó command là một chuỗi kiểu char để gọi hàm explorer.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(); } Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 101 Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV 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 âm bản: Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 102 Ứng dụng xử lý ảnh trong thực tế với thư viện OpenCV 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àm việ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à đã được thươ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ương trì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ạng mặt người, nhận dạng chữ viết tay, theo dõi đối tượng (object tracking), camera calibration - 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 Tác giả: Nguyễn Văn Long – long.06clc@gmail.com Page 103
File đính kèm:
- giao_trinh_ung_dung_xu_ly_anh_trong_thuc_te_voi_thu_vien_ope.pdf