Image Effects Sample in C++ AMP

We have walked you through the C++ AMP texture feature with this series on concurrency::graphics, including how to interop with DirectX11 textures in C++ AMP. In this blog post, I will use a sample that implements some image effects to show you how to use the Interop APIs in real code.

The sample is a Win32 application. The main window is shown below.

image

It loads an image file and renders the image to the window. There is an “Effects” menu that allows you to choose different effects that you want to apply to the image. In the sample, there are a few effects implemented, such as sobel edge detection (left) and embossing (right) as shown below:

clip_image002

clip_image004

Load image to texture

To load image files to texture, this sample uses the WICTextureLoader from the DirectX Tool Kit. WICTextureLoader is a WIC-based image file texture loader. In this sample, I only try to load simple 2D image files, so WICTextureLoader is a good fit. For loading more complex resource, you may want to use the DDSTextureLoader also from DirectX Tool Kit. For a full-featured DDS file reader, writer, and texture processing pipeline, please check out the DirectXTex Library. Back to this sample, I use the WICTextureLoader to load the image file, and create an ID3D11Texture2D object and its corresponding ID3D11ShaderResrouceView object. The code I use is very simple, in LoadImage() function:

 ID3D11Texture2D*          g_pInputTexture = nullptr;
 ID3D11ShaderResourceView* g_pInputTextureSRV = nullptr;
  
 //...
  
 // Create an ID3D11Texture2D object and an ID3D11ShaderResourceView object 
 // from the image file
 CreateWICTextureFromFile(g_pd3dDevice, g_pImmediateContext, g_file.c_str(), 
     reinterpret_cast<ID3D11Resource **>(&g_pInputTexture), &g_pInputTextureSRV);

The above code snippet allows me to get an ID3D11Texture2D object directly from an image file. The texture object created from the image file normally has a format as DXGI_FORMAT_R8G8B8A8_UNORM, which is assumed by this sample. Now, I have a texture on GPU, and it contains the pixels of the source image. I also have a corresponding ID3D11ShaderResourceView object, which could be bound to the graphics pipeline when I need to render the original image to the window. The code for rendering can be found in function ShowImage().

Interop between C++ AMP and DirectX

Since we have explained how to create an accelerator_view from an ID3D11Device object, this post will focus on the Interop APIs related to textures. In this sample, C++ AMP code deals with two textures:

  1. The texture that contains the original image. This is the one created via the WICTextureLoader as shown in the previous section.
  2. The texture that contains the image after an effect being applied. Note that the modified image is of the same size as the original image.

We created the second texture using C++ AMP API directly, also in LoadImage() function:

 texture<unorm4, 2>*  g_pAmpProcessedTexture = nullptr;
  
 //...
  
 // Create a texture the same size as g_pInputTexture
 g_pAmpProcessedTexture = new texture<unorm4, 2>(
     static_cast<int>(img_height), static_cast<int>(img_width), 8U, g_av);

I used C++ AMP to author the computation code that applies the effect. In this case, I want to use C++ AMP textures as input and output, so I use InterOp API to derive one from “g_pInputTexture”. For example, in function ApplyEffects(), I have:

 const texture<unorm4, 2> input_tex = make_texture<unorm4, 2>(g_av, 
     reinterpret_cast<IUnknown *>(g_pInputTexture));

Then I can use “input_tex” for the computation using C++ AMP.

On the other hand, sometimes I need to render the C++ AMP texture “g_pAmpProcessedTexture” to the window using DirectX graphics pipeline. I need to read it from the pixel shader. In order to do so, I use Interop API to get an ID3D11Texture2D object, and then create an ID3D11ShaderResourceView, which can be bound to the graphics pipeline. Again, see the code in LoadImage() function:

 ID3D11ShaderResourceView*  g_pProcessedTextureSRV = nullptr;
  
 //...
  
 // Get the D3D texture from interop, and create 
 // the corresponding SRV that could be later bound to 
 // graphics pipeline
 ID3D11Texture2D * processedTexture = 
     reinterpret_cast<ID3D11Texture2D *>(get_texture<unorm4, 2>(*g_pAmpProcessedTexture));
 g_pd3dDevice->CreateShaderResourceView(processedTexture, nullptr, &g_pProcessedTextureSRV);
 processedTexture->Release();

After this, whenever I need to render the modified image, I just bind “g_pProcessedTextureSRV” to the graphics pipeline. The code for rendering can be found in function ShowImage().

Apply effects to the image

In this sample, I implemented a few convolution based effects using C++ AMP. Please take a look on functions ApplyEdgeDetection() and ApplyOneConvolutionFilter(). The code is almost a direct translation from the algorithms in any image processing textbook to C++ AMP. There should still be room for performance optimization on the convolution. You are welcome to try it out.

Download the sample

Please download the attached package, which is released under Apache 2.0 (other than the portions coming from the DirectX Tool Kit, which is licensed under MS-PL). Look at the code, run it, play with it. Of course, you will need, Visual Studio 2012.

ImageEffects.zip