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.

 

 

5 thoughts on “Lập trình OpenCV C++ Bài 2: Load ảnh bằng opencv

  • 14 November, 2020 at 1:42 am
    Permalink

    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 .

    Reply
    • 27 November, 2020 at 7:07 pm
      Permalink

      Nếu em còn cần source code thì em để lại email nhé, anh sẽ gửi cho em

      Reply
  • 23 April, 2021 at 5:40 am
    Permalink

    Liên hệ với zalo: 0932879928, cần người hổ trợ code, có phí ạ.

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.