2013年8月14日水曜日

暑い!

うぁーー。今年の暑さはやばい!

温暖化が進めば夏の気温が50度になる?

うーん、なりそうだ・・・

2013年4月17日水曜日

ヘビーなメタルバンド

Compressorhead
さすがに、動きがカッチコチ www
ドラムの stickboy さんがカッコeeEEE


2013年2月20日水曜日

iOS 6.1の脱獄

iOS が使いにくいので脱獄

脱獄アプリのiFileを使用して
ファイルが見れるようになった

が・・・

テキストビューアで、テキストファイルを開くと

文字化けする


文字コードの判定処理が入っていないのかなー
文字化けするアプリはいらない

右上の「完了」ボタンでビューアが終了する
使いにくなーー

そもそも、
フォント変更機能が無い
ディレクトリの階層移動 左上のボタンのみ


iOSのファイラー iFileしかない??
Goodなんとかも使いにくそうなので放置

Androidだと ES ファイルエクスプローラー とかいろいろある

sharp Zaurus SL-A300、HP iPaqの方が使いやすかった気がする

2013年1月14日月曜日

C++ AMP 3 ヒストグラム

グレースケールのヒストグラムを画像ファイルに保存するテスト

ダイオウイカの目が怖かったじゃなイカ
atomic_fetch_add でアトミックに加算でゲソ
restrict(amp) がついてる処理がGPU上で実行されるはずじゃなイカ
デバッグ実行をGPUのみにしてもブレークしないじゃなイカ

main.cpp

#include <wincodec.h>

#include <iostream>
#include <tchar.h>
#include <assert.h>

#include <d3d11.h>
#include <DirectXTex.h>
#include <amp.h>
#include <amp_graphics.h>
#include <amp_math.h>

#pragma comment(lib, "d3d11.lib")


using namespace concurrency;
using namespace concurrency::graphics;
using namespace concurrency::graphics::direct3d;

ID3D11Device*    g_pd3dDevice = nullptr;
ID3D11DeviceContext*  g_pImmediateContext = nullptr ;

ID3D11Texture2D*   g_pInputTexture = nullptr;
texture<unorm4, 2>*   g_pAmpProcessedTexture = nullptr;

// ヒストグラムのサイズ (ビン数)
#define HIST_SIZE 256

void HistKernel(index<2> idx, array_view<int, 1> av, const texture<unorm_4, 2>& input_tex, graphics::writeonly_texture_view<unorm_4, 2> output_tex_view) restrict(amp)
{
 // 入力画像のピクセル値を取得
 float_4 pixel = static_cast<float_4>(input_tex[idx].rgba);

 // RGB値をグレースケールに変更 (0.0 ~ 1.0)
 float Y = pixel.r * 0.2126f + pixel.r * 0.7152f + pixel.b * 0.0722f;

 // 配列のインデックスの算出
 int index = static_cast<int>(Y * (HIST_SIZE - 1));
// int index = 8;

 // アトミックに1加算
 atomic_fetch_add( &av[index], 1 );  // av[index] += 1;

 output_tex_view.set(idx, unorm_4(Y, Y, Y, pixel.a));
}


void RunImageProcessing(const texture<unorm_4, 2>& input_tex, graphics::writeonly_texture_view<unorm_4, 2> output_tex_view, std::vector<int> vHist, graphics::writeonly_texture_view<unorm_4, 2> hist_tex_view)
{
 array_view<int, 1> av(HIST_SIZE, vHist);
// array_view<int, 1> av(HIST_SIZE);  // ERROR
 av.discard_data();

    parallel_for_each(input_tex.accelerator_view, output_tex_view.extent, [=, &input_tex] (index<2> idx) restrict(amp) {
  HistKernel(idx, av, input_tex, output_tex_view);
 });

 av.synchronize();

 // ヒストグラムの最大値とインデックス
 int maxValue = 0;
 int maxIndex = 0;
 int totalPixel = 0;

 parallel_for(0, HIST_SIZE, [&maxValue, &maxIndex, &vHist, &av, &totalPixel](int i) {
  vHist[i] = av[i];
  totalPixel += av[i];
  if(maxValue < av[i]) {
   maxValue = av[i];
   maxIndex = i;
  }
 });

 std::wcout << "input texture width: " << input_tex.extent[1] << std::endl; 
 std::wcout << "input texture height: " << input_tex.extent[0] << std::endl; 
 std::wcout << "input texture pixel num: " << input_tex.extent[0] * input_tex.extent[1] << std::endl; 

 std::wcout << "histogram maxValue: " << maxValue << std::endl; 
 std::wcout << "histogram maxIndex: " << maxIndex << std::endl;
 std::wcout << "totalPixel: " << totalPixel << std::endl;


 // ヒストグラムのグラフ描画用
    parallel_for_each(hist_tex_view.extent, [=] (index<2> idx) restrict(amp) {
  
     const UINT x = idx[1];
  const UINT y = idx[0];

  // 背景色
  float c = 1.0f;

  // ヒストグラム値を 0.0 ~ 1.0 に変換
  float v = av[x] / float(maxValue);

  // Y座標の値を 0.0 ~ 1.0 に変換
  float fy = 1.0f - (y / float(HIST_SIZE - 1));

  // グラフを書く
  if( v > 0 ) {
   if(v >= fy) {
    c = 0.0f;
   }
  }
  hist_tex_view.set(idx, unorm_4(c, c, c, 1.0));
 });
}


// textureをjpeg形式で保存
void SaveImage(LPCWSTR fileName, texture<unorm4, 2>* pTex)
{
 HRESULT hr;

 ID3D11Texture2D* processedTexture = reinterpret_cast<ID3D11Texture2D*>(get_texture<unorm4, 2>(*pTex));

 // processedTextureをoutput_imageにキャプチャーする
 DirectX::ScratchImage output_image;

 hr = DirectX::CaptureTexture(g_pd3dDevice, g_pImmediateContext, reinterpret_cast<ID3D11Resource *>(processedTexture), output_image);
 assert( hr == S_OK );

 // キャプチャーした画像を保存する
 GUID containerFormat = GUID_ContainerFormatJpeg;
 DWORD flags = 0;
 const DirectX::Image* pImage = output_image.GetImages();
 size_t numImage = output_image.GetImageCount();

 hr = DirectX::SaveToWICFile(pImage, numImage, flags, containerFormat, fileName);
 assert( hr == S_OK );

 processedTexture->Release();
}


void TestAMP(_TCHAR* imgFilePath)
{
 HRESULT hr;

 // LoadFromWICFile 用にCOMを初期化
 hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);

 // DirectX11の初期化
    unsigned int createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
// createDeviceFlags |= D3D11_CREATE_DEVICE_SWITCH_TO_REF;
#endif

 D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_HARDWARE;
// D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_REFERENCE;
// D3D_DRIVER_TYPE driverType = D3D_DRIVER_TYPE_SOFTWARE;

    D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_0;
 hr = D3D11CreateDevice( nullptr, driverType, nullptr, createDeviceFlags,
    &FeatureLevel, 1, D3D11_SDK_VERSION, &g_pd3dDevice, nullptr, &g_pImmediateContext );
 assert( hr == S_OK );


 // 画像ファイルを読み込む
 DirectX::TexMetadata mdata;
 DirectX::ScratchImage image;

 hr = DirectX::LoadFromWICFile(imgFilePath, DirectX::DDS_FLAGS_NONE, &mdata, image);
 assert( hr == S_OK );

 // ID3D11Texture2Dを作成
 hr = DirectX::CreateTexture( g_pd3dDevice, image.GetImages(), image.GetImageCount(), mdata, reinterpret_cast<ID3D11Resource **>(&g_pInputTexture) );
 assert( hr == S_OK );


 // concurrency::accelerator_viewを作成 実行するとリソースが解放されない何か残っている?
 accelerator_view g_av = concurrency::direct3d::create_accelerator_view(reinterpret_cast<IUnknown*>(g_pd3dDevice));


 // 出力先の作成
 UINT img_width = mdata.width;
    UINT img_height = mdata.height;

    g_pAmpProcessedTexture = new texture<unorm4, 2>(static_cast<int>(img_height), static_cast<int>(img_width), 8U, g_av);

 // writeonly_texture_viewに書き込む
 writeonly_texture_view<unorm4, 2> output_tex_view(*g_pAmpProcessedTexture);

 // ヒストグラム用のテクスチャ
 texture<unorm4, 2>* pAmpHistTexture = new texture<unorm4, 2>(HIST_SIZE, HIST_SIZE, 8U, g_av);
 writeonly_texture_view<unorm4, 2> hist_tex_view(*pAmpHistTexture);


 // 入力データ
 // ID3D11Texture2D g_pInputTextureからConcurrency::graphics::textureを作成
 const texture<unorm4, 2> input_tex = make_texture<unorm4, 2>(g_av, reinterpret_cast<IUnknown*>(g_pInputTexture));

 std::vector<int> vHist_grayscale( HIST_SIZE );


 // 入出力データの用意ができたので、画像処理を行う
 RunImageProcessing(input_tex, output_tex_view, vHist_grayscale, hist_tex_view);


 // 処理結果を保存
 SaveImage(L"output.jpg", g_pAmpProcessedTexture);

 // ヒストグラムのtextureを保存
 SaveImage(L"histogram.jpg", pAmpHistTexture);

 // 解放
 if (g_pInputTexture) g_pInputTexture->Release();
 if (g_pAmpProcessedTexture) delete g_pAmpProcessedTexture;
 delete pAmpHistTexture;
 
 if (g_pImmediateContext) g_pImmediateContext->Release();
    if (g_pd3dDevice) g_pd3dDevice->Release();
 CoUninitialize();
}


int _tmain(int argc, _TCHAR* argv[])
{
 if(argc > 1) {
  TestAMP(argv[1]);
 }
 return 0;
}

2013年1月6日日曜日

C++ AMP 2 モザイク

C++ AMP の AMP は (Accelerated Massive Parallelism) の略
意訳すると、C++で加速される、ちょー大きい並列処理 のこと?
Massive が気になる

4GBのメモリを積んだウルトラハイエンドなグラボがあるらしい

あと、msdnのリファレンスの文章がおかしい、特に C++ AMP mad関数の説明なんかが変
「きちがい関数」www ではない x * y + z を返すだけ

モザイク処理のテスト
出力画像を拡大して確認 → 指定した領域にモザイクがかかっていた

何がどう動いているのかイメージしにくいので、
キーワード tile_static tiled_extent などを後で調査

ドメイン固有言語 なんだけど C++で書けることなのかなー

そういえば、タイルレンダリングのテストもしていた気がする
シェーダーとは、GPU 上で実行される、ドメインに固有の小容量プログラム なんですか?

main.cpp

#include <windows.h>
#include <wincodec.h>

#include <tchar.h>
#include <assert.h>

#include <d3d11.h>
#include <amp.h>
#include <amp_graphics.h>

#include <DirectXTex.h>

#pragma comment(lib, "d3d11.lib")

using namespace concurrency;
using namespace concurrency::graphics;
using namespace concurrency::direct3d;

#define DIMENSION   2

ID3D11Device*     g_pd3dDevice = nullptr;
ID3D11DeviceContext*  g_pImmediateContext = nullptr ;
ID3D11Texture2D*    g_pInputTexture = nullptr;

texture<unorm_4, 2>*  g_pAmpProcessedTexture = nullptr;

// タイルのサイズ
static const UINT sTileSize = 32;
//static const UINT sTileSize = 48; // compile ERROR

// 「C++ AMP でのタイリングの概要」で検索
// extentは範囲
tiled_extent<sTileSize, sTileSize> GetTiledExtent(const extent<DIMENSION>& ext)
{
  // タイルエクステント内の総数は1024以下ならOK
    tiled_extent<sTileSize, sTileSize> text(ext);
    return text.pad();
}

// カーネル関数
void ApplyEffectKernelFunc001(const texture<unorm_4, 2>& input_tex, writeonly_texture_view<unorm_4, 2> output_tex_view, tiled_index<sTileSize, sTileSize> idx) restrict(amp)
{
    // タイル毎の共有メモリ
    tile_static float_4 local_pixels[sTileSize][sTileSize];

    const UINT globalY = idx.global[0];
    const UINT globalX = idx.global[1];
    const UINT localY = idx.local[0];
    const UINT localX = idx.local[1];

  local_pixels[localY][localX] = static_cast<float_4>(input_tex[idx.global].rgba);
//  local_pixels[idx.local] = static_cast<float_4>(input_tex[idx.global].rgba);   // ERROR

  idx.barrier.wait();

  if((globalY >= 100 && globalY < 250) && (globalX > 500 && globalX < 720)) {
    // 特定の領域にモザイクをかける
    output_tex_view.set(idx.global, unorm_4(local_pixels[0][0].r, local_pixels[0][0].g, local_pixels[0][0].b, 1.0));
  }
  else
  {
    // 入力値をコピー
    float_4 pixel = static_cast<float_4>(input_tex[idx.global].rgba);
    output_tex_view.set(idx.global, unorm_4(pixel.r, pixel.g, pixel.b, 1.0));
  }
}


void ApplyEffect(const texture<unorm_4, DIMENSION> & input_tex,  writeonly_texture_view<unorm_4, DIMENSION>& output_tex_view)
{
  // タイルのエクステント
  tiled_extent<sTileSize, sTileSize> computeDomain = GetTiledExtent(input_tex.extent);

  // 並列計算を起動
  parallel_for_each(computeDomain, [=, &input_tex](tiled_index<sTileSize, sTileSize> idx) restrict(amp)
  {
    ApplyEffectKernelFunc001(input_tex, output_tex_view, idx);
  });
}


void TestAMP(_TCHAR* filePath)
{
  HRESULT hr;

  hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);

    unsigned int createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    // DirectX11の初期化
    D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_0;
  hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags, &FeatureLevel, 1, D3D11_SDK_VERSION, &g_pd3dDevice, nullptr, &g_pImmediateContext );
  assert( hr == S_OK );

  // リソースが解放されない? 実行すると何かが残る
  accelerator_view g_av = create_accelerator_view(reinterpret_cast<IUnknown *>(g_pd3dDevice));

  std::wstring desc = g_av.get_accelerator().get_description();
  bool is_debug = g_av.get_is_debug();
  queuing_mode qmode = g_av.get_queuing_mode();


  // 画像ファイルを読み込む
  DirectX::TexMetadata mdata;
  DirectX::ScratchImage image;

  hr = DirectX::LoadFromWICFile(filePath, DirectX::DDS_FLAGS_NONE, &mdata, image);
  assert( hr == S_OK );

  // ID3D11Texture2Dを作成
  hr = DirectX::CreateTexture( g_pd3dDevice, image.GetImages(), image.GetImageCount(), mdata, reinterpret_cast<ID3D11Resource **>(&g_pInputTexture) );
  assert( hr == S_OK );


  // Concurrency::graphics::textureを作成
  UINT img_width = mdata.width;
    UINT img_height = mdata.height;
#if 1
  // OK
    g_pAmpProcessedTexture = new texture<unorm_4, DIMENSION>(static_cast<int>(img_height), static_cast<int>(img_width), 8U, g_av);
#else
  // NG ApplyEffect内でエラー発生する
//    g_pAmpProcessedTexture = new texture<unorm_4, DIMENSION>(static_cast<int>(img_height), static_cast<int>(img_width), 8U);
#endif

  // 画像データを加工する

  // 出力先
  writeonly_texture_view<unorm_4, DIMENSION> output_tex_view(*g_pAmpProcessedTexture);

  // 入力データ
  // ID3D11Texture2D g_pInputTextureからConcurrency::graphics::textureを作成
  const texture<unorm_4, DIMENSION> input_tex = graphics::direct3d::make_texture<unorm_4, DIMENSION>(g_av, reinterpret_cast<IUnknown *>(g_pInputTexture));

  // 加工処理
  ApplyEffect(input_tex, output_tex_view);

  // 加工結果を取り出す

  // Concurrency::graphics::texture の g_pAmpProcessedTexture から ID3D11Texture2Dを取得
    ID3D11Texture2D* processedTexture = reinterpret_cast<ID3D11Texture2D *>(graphics::direct3d::get_texture<unorm_4, DIMENSION>(*g_pAmpProcessedTexture));

  DirectX::ScratchImage output_image;

  // processedTextureをoutput_imageにキャプチャーする
  hr = DirectX::CaptureTexture(g_pd3dDevice, g_pImmediateContext, reinterpret_cast<ID3D11Resource *>(processedTexture), output_image);
  assert( hr == S_OK );

  // Jpeg形式で画像を保存
  GUID containerFormat = GUID_ContainerFormatJpeg;
  DWORD flags = 0;
  const DirectX::Image* pImage = output_image.GetImages();
  size_t numImage = output_image.GetImageCount();

  hr = DirectX::SaveToWICFile(pImage, numImage, flags, containerFormat, L"output.jpg");
  assert( hr == S_OK );

  // 解放
    processedTexture->Release();

    if (g_pInputTexture) g_pInputTexture->Release();
  if (g_pAmpProcessedTexture) delete g_pAmpProcessedTexture;

    if (g_pImmediateContext) g_pImmediateContext->Release();
    if (g_pd3dDevice) g_pd3dDevice->Release();
  CoUninitialize();
}

int _tmain(int argc, _TCHAR* argv[])
{
  if(argc > 1) {
    TestAMP(argv[1]);
  }
  return 0;
}

2013年1月3日木曜日

C++ AMP 1 グレースケール

C++ AMPで画像処理のテスト
VC 2012 Express版では不可?みたいなので、
テスト用のPCをWindows8 にUpgrade & Visual Studio 2012のPro版をInstall
ようやくテストすることができた。

AMPについて詳しくないので、
「C++ AMP の概要」 でググって読む
なんやらかんやらが、裏でいろいろ動いているはず

C++ AMPの本の付属サンプルを参考に作成したはず
http://ampbook.codeplex.com/

画像の読み込みにDirectXTex ライブラリを使用
ライブラリをDL & ビルドしてリンクに追加
http://directxtex.codeplex.com/

concurrency::indexクラス N次元のインデックス
concurrency::extentクラス N次元の範囲

RunImageProcessing()の parallel_for_each が肝 そういえばラムダ式

concurrency::accelerator_viewが怪しい
concurrency::direct3d::create_accelerator_view() で作成した
リソースが残っていたはず

8K解像度の画像でもサクサク処理できるかテスト中

namespaceを使わないとナガイ & < >が抜けていた?

main.cpp

#include <wincodec.h>

#include <iostream>
#include <tchar.h>
#include <assert.h>

#include <d3d11.h>
#include <DirectXTex.h>
#include <amp.h>
#include <amp_graphics.h>

#pragma comment(lib, "d3d11.lib")

// 今回は使用しない
//using namespace concurrency;
//using namespace concurrency::graphics;
//using namespace concurrency::direct3d;

ID3D11Device*       g_pd3dDevice = nullptr;
ID3D11DeviceContext*    g_pImmediateContext = nullptr ;

ID3D11Texture2D*      g_pInputTexture = nullptr;
Concurrency::graphics::texture<Concurrency::graphics::direct3d::unorm4, 2>*                 g_pAmpProcessedTexture = nullptr;

// 簡易タイマー
class Timer
{
  LARGE_INTEGER start, end;
public:
  void Start() {
    QueryPerformanceCounter(&start);
  }
  void Stop() {
    QueryPerformanceCounter(&end);
  }

  double ElapsedTime() {
    LARGE_INTEGER freq;
    QueryPerformanceFrequency(&freq);
    return (double(end.QuadPart) - double(start.QuadPart)) * 1000.0 / double(freq.QuadPart);
  }
};


// ↓今回のテスト対象 画像処理を行う
void RunImageProcessing(const Concurrency::graphics::texture<Concurrency::graphics::unorm_4, 2> & input_tex,  const Concurrency::graphics::writeonly_texture_view<Concurrency::graphics::unorm_4, 2> output_tex_view)
{
    parallel_for_each(input_tex.accelerator_view, output_tex_view.extent, [=, &input_tex] (Concurrency::index<2> idx) restrict(amp) {

    Concurrency::graphics::float_4 pixel = static_cast<Concurrency::graphics::float_4>(input_tex[idx].rgba);

    // RGB値をグレースケールに変更
    float Y = pixel.r * 0.2126f + pixel.r * 0.7152f + pixel.b * 0.0722f;

    output_tex_view.set(idx, Concurrency::graphics::unorm_4(Y, Y, Y, pixel.a));
  });
}

// AMPのテスト
// 画像ファイルを読み込んだ後に、グレースケールに変換して保存する
void TestAMP(_TCHAR* imgFilePath)
{
  Timer timer;
  HRESULT hr;

  // LoadFromWICFile 用にCOMを初期化
  hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);

  // DirectX11の初期化
    unsigned int createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_FEATURE_LEVEL FeatureLevel = D3D_FEATURE_LEVEL_11_0;
  hr = D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, createDeviceFlags,
        &FeatureLevel, 1, D3D11_SDK_VERSION, &g_pd3dDevice, nullptr, &g_pImmediateContext );
  assert( hr == S_OK );


  // 画像ファイルを読み込む
  DirectX::TexMetadata mdata;
  DirectX::ScratchImage image;

  hr = DirectX::LoadFromWICFile(imgFilePath, DirectX::DDS_FLAGS_NONE, &mdata, image);
  assert( hr == S_OK );

  // ID3D11Texture2Dを作成
  hr = DirectX::CreateTexture( g_pd3dDevice, image.GetImages(), image.GetImageCount(), mdata, reinterpret_cast<ID3D11Resource **>(&g_pInputTexture) );
  assert( hr == S_OK );


  // concurrency::accelerator_viewを作成  実行するとリソースが解放されない何か残っている?
  concurrency::accelerator_view g_av = concurrency::direct3d::create_accelerator_view(reinterpret_cast<IUnknown *>(g_pd3dDevice));


  // 出力先の作成
  UINT img_width = mdata.width;
    UINT img_height = mdata.height;

    g_pAmpProcessedTexture = new Concurrency::graphics::texture<Concurrency::graphics::direct3d::unorm4, 2>(static_cast<int>(img_height), static_cast<int>(img_width), 8U, g_av);

  // writeonly_texture_viewに書き込む
  Concurrency::graphics::writeonly_texture_view<Concurrency::graphics::direct3d::unorm4, 2> output_tex_view(*g_pAmpProcessedTexture);

  // 入力データ
  // ID3D11Texture2D g_pInputTextureからConcurrency::graphics::textureを作成
  const Concurrency::graphics::texture<Concurrency::graphics::direct3d::unorm4, 2> input_tex = Concurrency::graphics::direct3d::make_texture<Concurrency::graphics::direct3d::unorm4, 2>(g_av, reinterpret_cast<IUnknown *>(g_pInputTexture));

  timer.Start();

  // 入出力データの用意ができたので、画像処理を行う
  RunImageProcessing(input_tex, output_tex_view);

  timer.Stop();

  std::wcout << "ElapsedTime: " << timer.ElapsedTime() << " (ms)" << std::endl;

  // 処理結果を取り出す

  // Concurrency::graphics::texture の g_pAmpProcessedTexture から ID3D11Texture2Dを取得
    ID3D11Texture2D* processedTexture = reinterpret_cast<ID3D11Texture2D *>(Concurrency::graphics::direct3d::get_texture<Concurrency::graphics::direct3d::unorm4, 2>(*g_pAmpProcessedTexture));

  // processedTextureをoutput_imageにキャプチャーする
  DirectX::ScratchImage output_image;

  hr = DirectX::CaptureTexture(g_pd3dDevice, g_pImmediateContext, reinterpret_cast<ID3D11Resource *>(processedTexture), output_image);
  assert( hr == S_OK );

  // キャプチャーした画像を保存する
  GUID containerFormat = GUID_ContainerFormatJpeg;  // Jpeg
  DWORD flags = 0;
  const DirectX::Image* pImage = output_image.GetImages();
  size_t numImage = output_image.GetImageCount();

  hr = DirectX::SaveToWICFile(pImage, numImage, flags, containerFormat, L"output.jpg");
  assert( hr == S_OK );

  // 解放
    processedTexture->Release();

  if (g_pInputTexture) g_pInputTexture->Release();
  if (g_pAmpProcessedTexture) delete g_pAmpProcessedTexture;
  
  if (g_pImmediateContext) g_pImmediateContext->Release();
  if (g_pd3dDevice) g_pd3dDevice->Release();
  CoUninitialize();
}

int _tmain(int argc, _TCHAR* argv[])
{
  if(argc > 1) {
    TestAMP(argv[1]);
  }
  return 0;
}