Lập trình OpenCV C++ Bài 2: Load ảnh bằng opencv
Hello.
Chúng ta tiếp tục bài số 2 trong Chủ đề lập trình OpenCV C++
Bài số 2 chúng ta sẽ học các load một tấm ảnh lên trên view của project đã tạo ở bài 1.
Step 1: Định nghĩa hàm OpenDocument
Các bạn hãy mở file : MFCOpenCV2Doc.h và chúng ta thêm vào một hàm :
BOOL OnOpenDocument(LPCTSTR lpszPathName);
class OpenCVGUIDoc : public CDocument { protected: // create from serialization only OpenCVGUIDoc(); DECLARE_DYNCREATE(OpenCVGUIDoc) // Attributes public: // Operations public: // Overrides public: virtual BOOL OnNewDocument(); virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); virtual void Serialize(CArchive& ar); #ifdef SHARED_HANDLERS virtual void InitializeSearchContent(); virtual void OnDrawThumbnail(CDC& dc, LPRECT lprcBounds); #endif // SHARED_HANDLERS
Các bạn thêm nó như đoạn code ở trên
Sau đó sang file cpp chúng ta bắt đầu định nghĩa nó
BOOL OpenCVGUIDoc::OnOpenDocument(LPCTSTR lpszPathName) { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; return TRUE; }
Chúng ta định nghĩa bước đầu đơn giản như trên
Step 2: Include các file header của OpenCV
Trở lại file header của lớp Doc
các bạn thêm đoạn include như sau
#include <opencv2/opencv.hpp> #include <opencv2/core/core.hpp> #include <opencv2/highgui.hpp> #include <opencv2/videoio/videoio_c.h> #include <opencv2/imgproc/imgproc.hpp> class MFCOpenCV2Doc : public CDocument { protected: // create from serialization only MFCOpenCV2Doc(); DECLARE_DYNCREATE(MFCOpenCV2Doc)
Các bạn gọi include 5 file như vậy
Step 3: Bắt đầu xử lý việc đọc một file ảnh
Các bạn khai báo các biến thành viên sau cho lớp doc
+ biến m_Video; // Quản lý xem file đọc vào là ảnh hay video
+ BITMAPINFO* m_pBmi; // Quản lý dữ liệu ảnh xuất ra file
cv::Mat m_Mat; // Quản lý việc đọc ảnh từ opencv
class MFCOpenCV2Doc : public CDocument { protected: // create from serialization only MFCOpenCV2Doc(); DECLARE_DYNCREATE(MFCOpenCV2Doc) protected: cv::VideoCapture m_Video; // Attributes public: // Operations public: BOOL m_bErase; BITMAPINFO* m_pBmi; cv::Mat m_Mat; // Overrides public:
gán con trỏ m_pBmi = NULL bên hàm khởi tạo của lớp doc
OpenCVGUIDoc::OpenCVGUIDoc() { // TODO: add one-time construction code here m_pBmi = NULL; }
Định nghĩa 1 hàm : void SetupBitmapInfo(cv::Mat& mat, const int& bitCount = 24); như minh họa dưới đây
public: virtual BOOL OnNewDocument(); virtual BOOL OnOpenDocument(LPCTSTR lpszPathName); virtual void Serialize(CArchive& ar); #ifdef SHARED_HANDLERS virtual void InitializeSearchContent(); virtual void OnDrawThumbnail(CDC& dc, LPRECT lprcBounds); #endif // SHARED_HANDLERS void SetupBitmapInfo(cv::Mat& mat, const int& bitCount = 24); // dinh nghia ham setupBitmapInfo // Implementation
Định nghĩa bên file cpp
void OpenCVGUIDoc::SetupBitmapInfo(cv::Mat& mat, const int& bitCount /*=24*/) { if (NULL != m_pBmi) { delete m_pBmi; m_pBmi = NULL; } m_pBmi = new BITMAPINFO; BITMAPINFOHEADER* pHeader = &m_pBmi->bmiHeader; pHeader->biSize = sizeof(BITMAPINFOHEADER); pHeader->biPlanes = 1; pHeader->biCompression = BI_RGB; pHeader->biXPelsPerMeter = 100; pHeader->biYPelsPerMeter = 100; pHeader->biClrUsed = 0; pHeader->biClrImportant = 0; pHeader->biWidth = m_Mat.cols; pHeader->biHeight = -m_Mat.rows; pHeader->biBitCount = bitCount; m_pBmi->bmiHeader.biSizeImage = 0; }
Quay trở lại hàm OnOpenDocument()
các bạn xử lý code như dưới đây, bắt đầu từ dòng //Start load image by opencv
BOOL OpenCVGUIDoc::OnOpenDocument(LPCTSTR lpszPathName) { if (!CDocument::OnOpenDocument(lpszPathName)) return FALSE; //Start load image by opencv cv::String nameStr = CT2A(lpszPathName); if (!m_Video.open(nameStr)) return FALSE; //Check image or video if (m_Video.get(CV_CAP_PROP_FRAME_COUNT) < 2) { //::PostMessage(pView->GetSafeHwnd(), WMU_SETFPS, 0, 0); m_Mat.release(); m_Mat = cv::imread(nameStr); SetupBitmapInfo(m_Mat); } return TRUE; }
Thực hiện build lần 1 để xem có lỗi gì không ?
Step 4: Xử lý hiển thị hình ảnh trên view
Các bạn khai báo một hàm : void RenderImage(OpenCVGUIDoc* pDoc, CDC* pDC); bên lớp view như dưới đây
class OpenCVGUIView : public CView { protected: // create from serialization only OpenCVGUIView(); DECLARE_DYNCREATE(OpenCVGUIView) // Attributes public: OpenCVGUIDoc* GetDocument() const; void RenderImage(OpenCVGUIDoc* pDoc, CDC* pDC); // Operations
Sau đó định nghĩa hàm này bên file cpp như sau:
void OpenCVGUIView::RenderImage(OpenCVGUIDoc* pDoc, CDC* pDC) { if (pDoc) { //......................block 1.............. CRect rectClient; GetClientRect(&rectClient); CDC memDC; memDC.CreateCompatibleDC(pDC); CBitmap bmpMem; bmpMem.CreateCompatibleBitmap(pDC, rectClient.Width(), rectClient.Height()); CBitmap* pOldBitmap = memDC.SelectObject(&bmpMem); ///////////////////////////////////////////////////////// //......................block 2.............. //Insert image into document const int cx = rectClient.right; // view client area width; const int cy = rectClient.bottom; // view client area height; const int bx = pDoc->m_Mat.cols; // source bitmap width; const int by = pDoc->m_Mat.rows; // source bitmap height; const int vx = (int)((double)bx);// virtual document width; const int vy = (int)((double)by);// virtual document height; int xSrc = 0; int ySrc = 0; int nSrcWidth = bx; int nSrcHeight = by; int xDst = 0; int yDst = 0; int nDstWidth = vx; int nDstHeight = vy; ///////////////////////////////////////////////////// //..............................block3.................... memDC.SetStretchBltMode(COLORONCOLOR); StretchDIBits(memDC.m_hDC, xDst, yDst, nDstWidth, nDstHeight, 0, 0, nSrcWidth, nSrcHeight, pDoc->m_Mat.data, pDoc->m_pBmi, DIB_RGB_COLORS, SRCCOPY); pDC->BitBlt(0, 0, rectClient.Width(), rectClient.Height(), &memDC, 0, 0, SRCCOPY); memDC.SelectObject(pOldBitmap); bmpMem.DeleteObject(); ///////////////////////////////////////////////////////////////// }
Block1 là khối xử lý thiết lập môi trường bitmap cho view, để hiện thị được hình ảnh cho view
Block2 là lấy liệu hình ảnh đọc được từ opencv thông qua biến : pDoc->m_Mat
Block3 là thực hiện việc đẩy dữ liệu trong m_Mat vào môi trường đồ họa của MFC thông qua hàm StretchDIBits
Về cơ bản chúng ta ko đi sâu vào phần xử lý của hàm render vì đó là cơ chế của MFC, các bạn có thể tự tìm hiểu riêng.
Sau đó gọi hàm renderImage này trong hàm onDraw và bỏ comment đối số pDc của hàm OnDraw đi
void OpenCVGUIView::OnDraw(CDC* pDC) //nếu có comment đối số pDC thì bỏ đi { OpenCVGUIDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); if (!pDoc) return; RenderImage(pDoc, pDC); // gọi hàm RenderImage tại đây // TODO: add draw code for native data here }
Thực hiện build và test chương trình
Các bạn chạy chương trình lên, sẽ thấy view sẵn có màu đen
Các bạn chọn vào icon open và sau đó chọn một tấm ảnh, rồi ok để kiểm tra
Kết quả thành công sẽ là như sau:
Các bạn sẽ thấy xuất hiện một view thứ 2 và view này hiển thị được hình ảnh chúng ta cần load
Ok đến đây là chúng ta đã hoàn thành được bài 1 về cách load ảnh bằng opencv
Các bạn đừng vội thắc mắc như là:
vì sao ảnh bị che, vì sao lại có 2 view, vì sao em load lên bị đen trắng.
Tất cả những vấn đề trên chúng ta sẽ dần xử lý trong các bài tiếp theo.
Đến bước này các bạn đã thành công trong việc đọc ảnh lên view bằng opencv.
Anh ơi ! anh có thể chia sẻ source được không ạ ?
Nhiều đoạn hương dẫn em tìm không thấy : vd :class MFCOpenCV2Doc : public CDocument thằng này .
Em cảm ơn .
Nếu em còn cần source code thì em để lại email nhé, anh sẽ gửi cho em
Anh ơi ! anh có thể chia sẻ source được không ạ ? Em cảm ơn a nhiều ạ, email em đây ạ: nguyenthetrungk69@gmail.com
Liên hệ với zalo: 0932879928, cần người hổ trợ code, có phí ạ.
opencv a