opencv Mat显示到指定窗体 Direct2D实现

对于opencv的imshow函数,恐怕我们是再熟悉不过了,它会创建一个窗体,然后将mat显示到窗体中。

然而,实际开发程序中,会有这样一种需求:上层将创建好的窗体,传递给下层dll,下层dll将得到的一张张图像直接显示到窗体中去。

本文主要记录利用direct2d的实现方法。

1、在项目中添加 BaseFactory.h文件

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF  
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO  
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A  
// PARTICULAR PURPOSE.  
//  
// Copyright (c) Microsoft Corporation. All rights reserved  
//  
#pragma once  

// Modify the following defines if you have to target a platform prior to the ones specified below.  
// Refer to MSDN for the latest info on corresponding values for different platforms.  
#ifndef WINVER              // Allow use of features specific to Windows 7 or later.  
#define WINVER 0x0700       // Change this to the appropriate value to target other versions of Windows.  
#endif  

#ifndef _WIN32_WINNT        // Allow use of features specific to Windows 7 or later.  
#define _WIN32_WINNT 0x0700 // Change this to the appropriate value to target other versions of Windows.  
#endif  

#ifndef UNICODE  
#define UNICODE  
#endif  

// Exclude rarely-used items from Windows headers.  
#include <d2d1.h>  
//#include <d2d1helper.h>  


/******************************************************************
*                                                                 *
*  Macros                                                         *
*                                                                 *
******************************************************************/
template<class Interface>
inline void SafeRelease(
	Interface **ppInterfaceToRelease
	)
{
	if (*ppInterfaceToRelease != NULL)
	{
		(*ppInterfaceToRelease)->Release();

		(*ppInterfaceToRelease) = NULL;
	}
}

#ifndef Assert  
#if defined( DEBUG ) || defined( _DEBUG )  
#define Assert(b) do {if (!(b)) {OutputDebugStringA("Assert: " #b "\n");}} while(0)  
#else  
#define Assert(b)  
#endif //DEBUG || _DEBUG  
#endif  

#ifndef HINST_THISCOMPONENT  
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ((HINSTANCE)&__ImageBase)  
#endif  

class BaseFactory
{
public:
	BaseFactory(void);
	~BaseFactory(void);
	bool Initialize(HWND hwnd, int width, int height);
private:
	// Initialize device-dependent resources.  
	HRESULT CreateDeviceResources();

	// Release device-dependent resource.  

public:
	//绘图  
	void OnRender(char *data);
	void DiscardDeviceResources();
	//图片信息  
	int imgwidth;
	int imgheight;
	D2D1_RECT_U imgrect;
	char *imgdata;

private:
	RECT rc;
	HWND m_hwnd;
	ID2D1Factory* m_pDirect2dFactory;
	ID2D1HwndRenderTarget* m_pRenderTarget;

	ID2D1Bitmap* m_pBitmap;
};


2、添加BaseFactory.cpp文件


#include "BaseFactory.h"  

BaseFactory::BaseFactory(void)
{
}

bool BaseFactory::Initialize(HWND hwnd, int width, int height)
{
	imgheight = height;
	imgwidth = width;
	m_hwnd = hwnd;
	m_pRenderTarget = nullptr;
	m_pBitmap = nullptr;
	CreateDeviceResources();
	return true;
}

BaseFactory::~BaseFactory(void)
{
}

HRESULT BaseFactory::CreateDeviceResources()
{
	D2D1CreateFactory(D2D1_FACTORY_TYPE_SINGLE_THREADED, &m_pDirect2dFactory);
	HRESULT hr = S_OK;

	if (!m_pRenderTarget)
	{
		GetClientRect(m_hwnd, &rc);

		D2D1_SIZE_U size = D2D1::SizeU
			(
			rc.right - rc.left,
			rc.bottom - rc.top
			);
		// Create a Direct2D render target.  
		hr = m_pDirect2dFactory->CreateHwndRenderTarget(
			D2D1::RenderTargetProperties(),
			D2D1::HwndRenderTargetProperties(m_hwnd, size, D2D1_PRESENT_OPTIONS_IMMEDIATELY),//第三个参数设置不等待垂直同步,默认垂直同步时最高刷新频率为显卡刷新频率,一般60FPS  
			&m_pRenderTarget
			);
		//创建位图  
		D2D1_SIZE_U imgsize = D2D1::SizeU(imgwidth, imgheight);
		D2D1_PIXEL_FORMAT pixelFormat =  //位图像素格式描述  
		{
			DXGI_FORMAT_B8G8R8A8_UNORM, //该参数设置图像数据区的像素格式,现为RGBA,可根据需要改为别的格式,只是后面的数据拷贝要做相应的调整  
			D2D1_ALPHA_MODE_IGNORE
		};
		D2D1_BITMAP_PROPERTIES prop =  //位图具体信息描述  
		{
			pixelFormat,
			imgsize.width,
			imgsize.height
		};
		long pitch = imgsize.width * 4;
		imgdata = new char[imgsize.width * imgsize.height * 4];
		memset(imgdata, 0, imgsize.width * imgsize.height * 4);
		m_pRenderTarget->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);//设置图像为抗锯齿模式  
		m_pRenderTarget->CreateBitmap(imgsize, imgdata, pitch, &prop, &m_pBitmap);

		imgrect.left = 0;
		imgrect.right = imgwidth;
		imgrect.top = 0;
		imgrect.bottom = imgheight;
	}
	return hr;
}

void BaseFactory::DiscardDeviceResources()
{
	delete[] imgdata;
	SafeRelease(&m_pRenderTarget);
	m_pDirect2dFactory->Release();
	m_pBitmap->Release();
}

void BaseFactory::OnRender(char *data)//绘制图形到指定控件中  
{
	m_pRenderTarget->BeginDraw();//跟显示刷新频率有关系  
	m_pBitmap->CopyFromMemory(&imgrect, data, imgwidth * 4);
	m_pRenderTarget->DrawBitmap(m_pBitmap, D2D1::RectF(0, 0, rc.right - rc.left, rc.bottom - rc.top));//该矩形大小会受到"更改文本、应用和其他项目的大小:xxx%"的影响  
	m_pRenderTarget->EndDraw();
}

3、添加附加依赖项 d2d1.lib  和 程序运行所需要的d2d1.dll,d2d1.dll应该在电脑中能够搜索到。

4、使用示例

void test(HWND handle)//需要传入的窗体句柄
{
	HWND m_hWnd;
	m_hWnd = HWND(handle);

	string filename = "test.avi";//打开的视频文件  
	VideoCapture capture;
	capture.open(filename);
	
	int image_width = 672;
	int image_height = 378;
	
	BaseFactory* m_BaseFactory = new BaseFactory();//创建Direct2D工厂对象   
	m_BaseFactory->Initialize(m_hWnd, image_width, image_height);//初始化 
	char *data = new char[image_width*image_height * 4];

	while (true)
	{
		Mat image;
		capture >> image;
		if (image.empty()) break;
		for (size_t row = 0; row < image_height; row++)
			for (size_t col = 0; col < image_width; col++)
			{
				int point = col * 3 + row*image_width * 3;
				int pointrbga = col * 4 + row*image_width * 4;
				data[pointrbga + 0] = image.data[point + 0];
				data[pointrbga + 1] = image.data[point + 1];
				data[pointrbga + 2] = image.data[point + 2];
			}
		m_BaseFactory->OnRender(data);
		Sleep(30);
	}

	//将rgb转为rgba 

	//释放对象,回收内存  
	if (m_BaseFactory != NULL)
	{
		m_BaseFactory->DiscardDeviceResources();
		delete m_BaseFactory;
		m_BaseFactory = NULL;
	}
	delete[]data;
}

5、以上四个步骤,就可以用起来了。可以在上层传入窗体的句柄,创建一个线程,下层就可以实时的把获取的照片显示到指定窗体了,同时不影响上层的任何操作。

但direct2d的方法,博主亲自使用后,发现d2d1.dll会在有的系统上报错,导致程序崩溃性错误。在win7系统上,即使有serve pack1,也会无法使用,安装VS2013之后,就可以正常使用了,然而总不能够要求所有使用者的电脑都安装VS吧。

希望看到此博文的读者,有什么好的办法解决d2d1.dll报错问题,可以告诉我下,不胜感激。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值