Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/samples/directx/d3d11_interop.cpp
16337 views
1
/*
2
// A sample program demonstrating interoperability of OpenCV cv::UMat with Direct X surface
3
// At first, the data obtained from video file or camera and placed onto Direct X surface,
4
// following mapping of this Direct X surface to OpenCV cv::UMat and call cv::Blur function.
5
// The result is mapped back to Direct X surface and rendered through Direct X API.
6
*/
7
8
#define WIN32_LEAN_AND_MEAN
9
#include <windows.h>
10
#include <d3d11.h>
11
12
#include "opencv2/core.hpp"
13
#include "opencv2/core/directx.hpp"
14
#include "opencv2/core/ocl.hpp"
15
#include "opencv2/imgproc.hpp"
16
#include "opencv2/videoio.hpp"
17
18
#include "d3dsample.hpp"
19
20
#pragma comment (lib, "d3d11.lib")
21
22
class D3D11WinApp : public D3DSample
23
{
24
public:
25
D3D11WinApp(int width, int height, std::string& window_name, cv::VideoCapture& cap)
26
: D3DSample(width, height, window_name, cap),
27
m_nv12_available(false)
28
{}
29
30
~D3D11WinApp() {}
31
32
33
int create(void)
34
{
35
// base initialization
36
D3DSample::create();
37
38
// initialize DirectX
39
HRESULT r;
40
41
DXGI_SWAP_CHAIN_DESC scd;
42
43
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
44
45
scd.BufferCount = 1; // one back buffer
46
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; // use 32-bit color
47
scd.BufferDesc.Width = m_width; // set the back buffer width
48
scd.BufferDesc.Height = m_height; // set the back buffer height
49
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; // how swap chain is to be used
50
scd.OutputWindow = m_hWnd; // the window to be used
51
scd.SampleDesc.Count = 1; // how many multisamples
52
scd.Windowed = TRUE; // windowed/full-screen mode
53
scd.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
54
scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH; // allow full-screen switching
55
56
r = ::D3D11CreateDeviceAndSwapChain(
57
NULL,
58
D3D_DRIVER_TYPE_HARDWARE,
59
NULL,
60
0,
61
NULL,
62
0,
63
D3D11_SDK_VERSION,
64
&scd,
65
&m_pD3D11SwapChain,
66
&m_pD3D11Dev,
67
NULL,
68
&m_pD3D11Ctx);
69
if (FAILED(r))
70
{
71
throw std::runtime_error("D3D11CreateDeviceAndSwapChain() failed!");
72
}
73
74
#if defined(_WIN32_WINNT_WIN8) && _WIN32_WINNT >= _WIN32_WINNT_WIN8
75
UINT fmt = 0;
76
r = m_pD3D11Dev->CheckFormatSupport(DXGI_FORMAT_NV12, &fmt);
77
if (SUCCEEDED(r))
78
{
79
m_nv12_available = true;
80
}
81
#endif
82
83
r = m_pD3D11SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&m_pBackBuffer);
84
if (FAILED(r))
85
{
86
throw std::runtime_error("GetBufer() failed!");
87
}
88
89
r = m_pD3D11Dev->CreateRenderTargetView(m_pBackBuffer, NULL, &m_pRenderTarget);
90
if (FAILED(r))
91
{
92
throw std::runtime_error("CreateRenderTargetView() failed!");
93
}
94
95
m_pD3D11Ctx->OMSetRenderTargets(1, &m_pRenderTarget, NULL);
96
97
D3D11_VIEWPORT viewport;
98
ZeroMemory(&viewport, sizeof(D3D11_VIEWPORT));
99
100
viewport.Width = (float)m_width;
101
viewport.Height = (float)m_height;
102
viewport.MinDepth = 0.0f;
103
viewport.MaxDepth = 0.0f;
104
105
m_pD3D11Ctx->RSSetViewports(1, &viewport);
106
107
m_pSurfaceRGBA = 0;
108
m_pSurfaceNV12 = 0;
109
m_pSurfaceNV12_cpu_copy = 0;
110
111
D3D11_TEXTURE2D_DESC desc_rgba;
112
113
desc_rgba.Width = m_width;
114
desc_rgba.Height = m_height;
115
desc_rgba.MipLevels = 1;
116
desc_rgba.ArraySize = 1;
117
desc_rgba.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
118
desc_rgba.SampleDesc.Count = 1;
119
desc_rgba.SampleDesc.Quality = 0;
120
desc_rgba.BindFlags = D3D11_BIND_SHADER_RESOURCE;
121
desc_rgba.Usage = D3D11_USAGE_DYNAMIC;
122
desc_rgba.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
123
desc_rgba.MiscFlags = 0;
124
125
r = m_pD3D11Dev->CreateTexture2D(&desc_rgba, 0, &m_pSurfaceRGBA);
126
if (FAILED(r))
127
{
128
throw std::runtime_error("Can't create DX texture");
129
}
130
131
#if defined(_WIN32_WINNT_WIN8) && _WIN32_WINNT >= _WIN32_WINNT_WIN8
132
if(m_nv12_available)
133
{
134
D3D11_TEXTURE2D_DESC desc_nv12;
135
136
desc_nv12.Width = m_width;
137
desc_nv12.Height = m_height;
138
desc_nv12.MipLevels = 1;
139
desc_nv12.ArraySize = 1;
140
desc_nv12.Format = DXGI_FORMAT_NV12;
141
desc_nv12.SampleDesc.Count = 1;
142
desc_nv12.SampleDesc.Quality = 0;
143
desc_nv12.BindFlags = D3D11_BIND_SHADER_RESOURCE;
144
desc_nv12.Usage = D3D11_USAGE_DEFAULT;
145
desc_nv12.CPUAccessFlags = 0;
146
desc_nv12.MiscFlags = D3D11_RESOURCE_MISC_SHARED;
147
148
r = m_pD3D11Dev->CreateTexture2D(&desc_nv12, 0, &m_pSurfaceNV12);
149
if (FAILED(r))
150
{
151
throw std::runtime_error("Can't create DX NV12 texture");
152
}
153
154
D3D11_TEXTURE2D_DESC desc_nv12_cpu_copy;
155
156
desc_nv12_cpu_copy.Width = m_width;
157
desc_nv12_cpu_copy.Height = m_height;
158
desc_nv12_cpu_copy.MipLevels = 1;
159
desc_nv12_cpu_copy.ArraySize = 1;
160
desc_nv12_cpu_copy.Format = DXGI_FORMAT_NV12;
161
desc_nv12_cpu_copy.SampleDesc.Count = 1;
162
desc_nv12_cpu_copy.SampleDesc.Quality = 0;
163
desc_nv12_cpu_copy.BindFlags = 0;
164
desc_nv12_cpu_copy.Usage = D3D11_USAGE_STAGING;
165
desc_nv12_cpu_copy.CPUAccessFlags = /*D3D11_CPU_ACCESS_WRITE | */D3D11_CPU_ACCESS_READ;
166
desc_nv12_cpu_copy.MiscFlags = 0;
167
168
r = m_pD3D11Dev->CreateTexture2D(&desc_nv12_cpu_copy, 0, &m_pSurfaceNV12_cpu_copy);
169
if (FAILED(r))
170
{
171
throw std::runtime_error("Can't create DX NV12 texture");
172
}
173
}
174
#endif
175
176
// initialize OpenCL context of OpenCV lib from DirectX
177
if (cv::ocl::haveOpenCL())
178
{
179
m_oclCtx = cv::directx::ocl::initializeContextFromD3D11Device(m_pD3D11Dev);
180
}
181
182
m_oclDevName = cv::ocl::useOpenCL() ?
183
cv::ocl::Context::getDefault().device(0).name() :
184
"No OpenCL device";
185
186
return EXIT_SUCCESS;
187
} // create()
188
189
190
// get media data on DX surface for further processing
191
int get_surface(ID3D11Texture2D** ppSurface, bool use_nv12)
192
{
193
HRESULT r;
194
195
if (!m_cap.read(m_frame_bgr))
196
return EXIT_FAILURE;
197
198
if (use_nv12)
199
{
200
cv::cvtColor(m_frame_bgr, m_frame_i420, cv::COLOR_BGR2YUV_I420);
201
202
convert_I420_to_NV12(m_frame_i420, m_frame_nv12, m_width, m_height);
203
204
m_pD3D11Ctx->UpdateSubresource(m_pSurfaceNV12, 0, 0, m_frame_nv12.data, (UINT)m_frame_nv12.step[0], (UINT)m_frame_nv12.total());
205
}
206
else
207
{
208
cv::cvtColor(m_frame_bgr, m_frame_rgba, cv::COLOR_BGR2RGBA);
209
210
// process video frame on CPU
211
UINT subResource = ::D3D11CalcSubresource(0, 0, 1);
212
213
D3D11_MAPPED_SUBRESOURCE mappedTex;
214
r = m_pD3D11Ctx->Map(m_pSurfaceRGBA, subResource, D3D11_MAP_WRITE_DISCARD, 0, &mappedTex);
215
if (FAILED(r))
216
{
217
throw std::runtime_error("surface mapping failed!");
218
}
219
220
cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, mappedTex.RowPitch);
221
m_frame_rgba.copyTo(m);
222
223
m_pD3D11Ctx->Unmap(m_pSurfaceRGBA, subResource);
224
}
225
226
*ppSurface = use_nv12 ? m_pSurfaceNV12 : m_pSurfaceRGBA;
227
228
return EXIT_SUCCESS;
229
} // get_surface()
230
231
232
// process and render media data
233
int render()
234
{
235
try
236
{
237
if (m_shutdown)
238
return EXIT_SUCCESS;
239
240
// capture user input once
241
MODE mode = (m_mode == MODE_GPU_NV12 && !m_nv12_available) ? MODE_GPU_RGBA : m_mode;
242
243
HRESULT r;
244
ID3D11Texture2D* pSurface = 0;
245
246
r = get_surface(&pSurface, mode == MODE_GPU_NV12);
247
if (FAILED(r))
248
{
249
throw std::runtime_error("get_surface() failed!");
250
}
251
252
m_timer.reset();
253
m_timer.start();
254
255
switch (mode)
256
{
257
case MODE_CPU:
258
{
259
// process video frame on CPU
260
UINT subResource = ::D3D11CalcSubresource(0, 0, 1);
261
262
D3D11_MAPPED_SUBRESOURCE mappedTex;
263
r = m_pD3D11Ctx->Map(pSurface, subResource, D3D11_MAP_WRITE_DISCARD, 0, &mappedTex);
264
if (FAILED(r))
265
{
266
throw std::runtime_error("surface mapping failed!");
267
}
268
269
cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, (int)mappedTex.RowPitch);
270
271
if (m_demo_processing)
272
{
273
// blur data from D3D11 surface with OpenCV on CPU
274
cv::blur(m, m, cv::Size(15, 15));
275
}
276
277
m_timer.stop();
278
279
cv::String strMode = cv::format("mode: %s", m_modeStr[MODE_CPU].c_str());
280
cv::String strProcessing = m_demo_processing ? "blur frame" : "copy frame";
281
cv::String strTime = cv::format("time: %4.3f msec", m_timer.getTimeMilli());
282
cv::String strDevName = cv::format("OpenCL device: %s", m_oclDevName.c_str());
283
284
cv::putText(m, strMode, cv::Point(0, 20), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 200), 2);
285
cv::putText(m, strProcessing, cv::Point(0, 40), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 200), 2);
286
cv::putText(m, strTime, cv::Point(0, 60), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 200), 2);
287
cv::putText(m, strDevName, cv::Point(0, 80), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 200), 2);
288
289
m_pD3D11Ctx->Unmap(pSurface, subResource);
290
291
break;
292
}
293
294
case MODE_GPU_RGBA:
295
case MODE_GPU_NV12:
296
{
297
// process video frame on GPU
298
cv::UMat u;
299
300
cv::directx::convertFromD3D11Texture2D(pSurface, u);
301
302
if (m_demo_processing)
303
{
304
// blur data from D3D11 surface with OpenCV on GPU with OpenCL
305
cv::blur(u, u, cv::Size(15, 15));
306
}
307
308
m_timer.stop();
309
310
cv::String strMode = cv::format("mode: %s", m_modeStr[mode].c_str());
311
cv::String strProcessing = m_demo_processing ? "blur frame" : "copy frame";
312
cv::String strTime = cv::format("time: %4.3f msec", m_timer.getTimeMilli());
313
cv::String strDevName = cv::format("OpenCL device: %s", m_oclDevName.c_str());
314
315
cv::putText(u, strMode, cv::Point(0, 20), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 200), 2);
316
cv::putText(u, strProcessing, cv::Point(0, 40), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 200), 2);
317
cv::putText(u, strTime, cv::Point(0, 60), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 200), 2);
318
cv::putText(u, strDevName, cv::Point(0, 80), cv::FONT_HERSHEY_SIMPLEX, 0.8, cv::Scalar(0, 0, 200), 2);
319
320
cv::directx::convertToD3D11Texture2D(u, pSurface);
321
322
if (mode == MODE_GPU_NV12)
323
{
324
// just for rendering, we need to convert NV12 to RGBA.
325
m_pD3D11Ctx->CopyResource(m_pSurfaceNV12_cpu_copy, m_pSurfaceNV12);
326
327
// process video frame on CPU
328
{
329
UINT subResource = ::D3D11CalcSubresource(0, 0, 1);
330
331
D3D11_MAPPED_SUBRESOURCE mappedTex;
332
r = m_pD3D11Ctx->Map(m_pSurfaceNV12_cpu_copy, subResource, D3D11_MAP_READ, 0, &mappedTex);
333
if (FAILED(r))
334
{
335
throw std::runtime_error("surface mapping failed!");
336
}
337
338
cv::Mat frame_nv12(m_height + (m_height / 2), m_width, CV_8UC1, mappedTex.pData, mappedTex.RowPitch);
339
cv::cvtColor(frame_nv12, m_frame_rgba, cv::COLOR_YUV2RGBA_NV12);
340
341
m_pD3D11Ctx->Unmap(m_pSurfaceNV12_cpu_copy, subResource);
342
}
343
344
{
345
UINT subResource = ::D3D11CalcSubresource(0, 0, 1);
346
347
D3D11_MAPPED_SUBRESOURCE mappedTex;
348
r = m_pD3D11Ctx->Map(m_pSurfaceRGBA, subResource, D3D11_MAP_WRITE_DISCARD, 0, &mappedTex);
349
if (FAILED(r))
350
{
351
throw std::runtime_error("surface mapping failed!");
352
}
353
354
cv::Mat m(m_height, m_width, CV_8UC4, mappedTex.pData, mappedTex.RowPitch);
355
m_frame_rgba.copyTo(m);
356
357
m_pD3D11Ctx->Unmap(m_pSurfaceRGBA, subResource);
358
}
359
360
pSurface = m_pSurfaceRGBA;
361
}
362
363
break;
364
}
365
366
} // switch
367
368
// traditional DX render pipeline:
369
// BitBlt surface to backBuffer and flip backBuffer to frontBuffer
370
m_pD3D11Ctx->CopyResource(m_pBackBuffer, pSurface);
371
372
// present the back buffer contents to the display
373
// switch the back buffer and the front buffer
374
r = m_pD3D11SwapChain->Present(0, 0);
375
if (FAILED(r))
376
{
377
throw std::runtime_error("switch betweem fronat and back buffers failed!");
378
}
379
} // try
380
381
catch (cv::Exception& e)
382
{
383
std::cerr << "Exception: " << e.what() << std::endl;
384
cleanup();
385
return 10;
386
}
387
388
catch (const std::exception& e)
389
{
390
std::cerr << "Exception: " << e.what() << std::endl;
391
cleanup();
392
return 11;
393
}
394
395
return EXIT_SUCCESS;
396
} // render()
397
398
399
int cleanup(void)
400
{
401
SAFE_RELEASE(m_pSurfaceRGBA);
402
SAFE_RELEASE(m_pSurfaceNV12);
403
SAFE_RELEASE(m_pSurfaceNV12_cpu_copy);
404
SAFE_RELEASE(m_pBackBuffer);
405
SAFE_RELEASE(m_pD3D11SwapChain);
406
SAFE_RELEASE(m_pRenderTarget);
407
SAFE_RELEASE(m_pD3D11Dev);
408
SAFE_RELEASE(m_pD3D11Ctx);
409
D3DSample::cleanup();
410
return EXIT_SUCCESS;
411
} // cleanup()
412
413
protected:
414
void convert_I420_to_NV12(cv::Mat& i420, cv::Mat& nv12, int width, int height)
415
{
416
nv12.create(i420.rows, i420.cols, CV_8UC1);
417
418
unsigned char* pSrcY = i420.data;
419
unsigned char* pDstY = nv12.data;
420
size_t srcStep = i420.step[0];
421
size_t dstStep = nv12.step[0];
422
423
{
424
unsigned char* src;
425
unsigned char* dst;
426
427
// copy Y plane
428
for (int i = 0; i < height; i++)
429
{
430
src = pSrcY + i*srcStep;
431
dst = pDstY + i*dstStep;
432
433
for (int j = 0; j < width; j++)
434
{
435
dst[j] = src[j];
436
}
437
}
438
}
439
440
{
441
// copy U/V planes to UV plane
442
unsigned char* pSrcU;
443
unsigned char* pSrcV;
444
unsigned char* pDstUV;
445
446
size_t uv_offset = height * dstStep;
447
448
for (int i = 0; i < height / 2; i++)
449
{
450
pSrcU = pSrcY + height*width + i*(width / 2);
451
pSrcV = pSrcY + height*width + (height / 2) * (width / 2) + i*(width / 2);
452
453
pDstUV = pDstY + uv_offset + i*dstStep;
454
455
for (int j = 0; j < width / 2; j++)
456
{
457
pDstUV[j*2 + 0] = pSrcU[j];
458
pDstUV[j*2 + 1] = pSrcV[j];
459
}
460
}
461
}
462
463
return;
464
}
465
466
private:
467
ID3D11Device* m_pD3D11Dev;
468
IDXGISwapChain* m_pD3D11SwapChain;
469
ID3D11DeviceContext* m_pD3D11Ctx;
470
ID3D11Texture2D* m_pBackBuffer;
471
ID3D11Texture2D* m_pSurfaceRGBA;
472
ID3D11Texture2D* m_pSurfaceNV12;
473
ID3D11Texture2D* m_pSurfaceNV12_cpu_copy;
474
ID3D11RenderTargetView* m_pRenderTarget;
475
cv::ocl::Context m_oclCtx;
476
cv::String m_oclPlatformName;
477
cv::String m_oclDevName;
478
bool m_nv12_available;
479
cv::Mat m_frame_i420;
480
cv::Mat m_frame_nv12;
481
};
482
483
484
// main func
485
int main(int argc, char** argv)
486
{
487
std::string title = "D3D11 interop sample";
488
return d3d_app<D3D11WinApp>(argc, argv, title);
489
}
490
491