2013年12月7日土曜日

Windows での画像ファイルの読み書き(2)

画像の読み込みで、IWICFormatConverter を使用して画素フォーマットを変換する例です。

画像を読み込んだ直後の IWICBitmapFrameDecode 上の画素フォーマット、たとえば色空間や画素のビット数などは、画像ファイル形式に依存します。前エントリの Chrysanthemum.jpg は BGR 各 8ビットのフォーマットでしたが、たとえば png などの画像では異なるフォーマットになり、画素値を取り出す方法も異なります。ですが多くの場合、画素の表現は画像ファイル形式によらず一定の表現を使いたい場合が多いと思います。

そのような場合、IWICFormatConverter で所望のフォーマットに変換できます。

コード例は以下になります。ポイントは、IWICBitmapFrameDecode から画素を取り出すのではなく、 さらにIWICFormatConverter を介する点です。IWICFormatConverter の Initialize() メソッドで所望のフォーマットを指定します。この際、さらにフォーマット変換処理内容についていくつかパラメタを指定できます。

#include <wincodec.h> // Windows Imaging Component。windowscodecs.lib もリンクが必要。
#include <iostream>

HRESULT GetPixelsFromImageFileWithConversionTo24bppBGR(LPCWSTR imageFileName, UINT *puiWidth, UINT *puiHeight, BYTE **ppcbBuffer)
{
 IWICImagingFactory *pFactory = NULL; // ファクトリ。デコーダを生成。
 IWICBitmapDecoder *pDecoder = NULL;  // デコーダ。画像ファイルからのフレーム取得に使用。
 IWICBitmapFrameDecode *pFrame = NULL; // フレーム。画像一枚を格納。
 IWICFormatConverter *pConverter = NULL; // コンバータ。目的のフォーマットに変換。
 
 HRESULT hr = S_OK;
 if(SUCCEEDED(hr))
 {
  // ファクトリの生成。
  hr = CoCreateInstance(
      CLSID_WICImagingFactory,
      NULL,
      CLSCTX_INPROC_SERVER,
      IID_IWICImagingFactory,
      (LPVOID*)&pFactory
    );
 }
 
 if(SUCCEEDED(hr))
 {
  // デコーダの生成。ファイル名からデコーダを判別して生成。
  hr = pFactory->CreateDecoderFromFilename(
      imageFileName,
      NULL,
      GENERIC_READ,
      WICDecodeMetadataCacheOnDemand,
      &pDecoder
    );
 }
 
 if(SUCCEEDED(hr))
 {
  // 一枚目のフレームを取得。
  hr = pDecoder->GetFrame(0, &pFrame);
 }
 
 if(SUCCEEDED(hr))
 {
  // コンバータの生成。ファイル名からデコーダを判別して生成。
  hr = pFactory->CreateFormatConverter(&pConverter);
 }
 
 if(SUCCEEDED(hr))
 {
  // コンバータの初期化。
  hr = pConverter->Initialize(
   pFrame,
   GUID_WICPixelFormat24bppBGR, // フォーマット指定
   WICBitmapDitherTypeNone,  // ディザリング指定
   NULL,       // パレット指定
   0.f,       // α スレッショルド
   WICBitmapPaletteTypeMedianCut // パレットトランスレーション
   );
 }
 
 WICPixelFormatGUID guidPixelFormat = GUID_WICPixelFormat24bppBGR;
 if(SUCCEEDED(hr))
 {
  // フォーマット情報を取得。
  hr = pConverter->GetPixelFormat(&guidPixelFormat);
 }
 
 if(SUCCEEDED(hr))
 {
  // フォーマットが 24ビット BGR (1色8ビット) か検査。
  if(guidPixelFormat != GUID_WICPixelFormat24bppBGR)
  {
   hr = E_FAIL;
  }
 }
 
 if(SUCCEEDED(hr))
 {
  // 画像の幅、高さを取得。
  hr = pConverter->GetSize(puiWidth, puiHeight);
 }
 
 if(SUCCEEDED(hr))
 {
  // 画素の領域を確保。
  *ppcbBuffer = new BYTE[*puiWidth * *puiHeight * 3];
  if(*ppcbBuffer == NULL)
  {
   hr = E_FAIL;
  }
 }
 
 if(SUCCEEDED(hr))
 {
  // 画素を取得。
  hr = pConverter->CopyPixels(NULL, (*puiWidth * 3), (*puiWidth * *puiHeight * 3), *ppcbBuffer);
 }
 
 if(FAILED(hr))
 {
  // エラーが発生した場合は、画素の領域を解放。
  if(*ppcbBuffer)
  {
   delete [] *ppcbBuffer;
  }
  *ppcbBuffer = NULL;
 }

 // ファクトリ、デコーダ、フレームの解放。
 if(pFactory)
 {
  pFactory->Release();
 }
 
 if(pDecoder)
 {
  pDecoder->Release();
 }

 if(pFrame)
 {
  pFrame->Release();
 }

 if(pConverter)
 {
  pFrame->Release();
 }
 
 return hr;
}

2013年9月8日日曜日

Windows での画像ファイルの読み書き(1)

Windows Imaging Component を使った画像ファイルの読み書きをしてみたので、備忘として記します。

Windows Imaging Component は Windows SDK に含まれており、静止画や動画を読み書きするための API が提供されています。画像処理をするようなプログラムを書くときに有用です。

Windows での画像の読み書きでは win32api や GDI+ もありますが、GUI はないプログラムだと Windows Imaging Component がもっとも使い勝手がよいのでは、と思います。


まずは画像ファイルからの読み込みです。

画像ファイルから画素を取得するまでの簡単な流れは以下となります。

  1. IWICImaginFactory オブジェクトの生成
  2. IWICImaginFactory オブジェクトからのデコーダの生成
  3. デコーダからのフレームの生成
  4. フレームからの画素の取得

コードを以下に記載します。本コードで便利なところは、ファイル名から画像フォーマットを判別して適宜ファイルを読んでくれるところです。これで、大体どんな画像ファイルでも一律に読み出せます。

本コードでは、ファイルから読みだした画素は 24ビット BGR を前提としていますが、IWICFormatConverter を使用して適宜画素フォーマットを変換することができます。その例ものちほど載せたいと思います。

#include<wincodec.h> // Windows Imaging Component。要windowscodecs.lib。
#include<iostream>

// Windows Imaging Component を使用した画素の取得。
HRESULT GetPixelsFromImageFile(LPCWSTR imageFileName, UINT *puiWidth, UINT *puiHeight, BYTE **ppcbBuffer)
{
 // ファクトリ。デコーダの生成に使用。
 IWICImagingFactory *pFactory = NULL;
 // デコーダ。画像ファイルからのフレーム取得に使用。
 IWICBitmapDecoder *pDecoder = NULL;
 // フレーム。画像一枚を格納。画素の取得に使用。
 IWICBitmapFrameDecode *pFrame = NULL;
 
 HRESULT hr = S_OK;
 if(SUCCEEDED(hr))
 {
  // ファクトリの生成。
  hr = CoCreateInstance(
      CLSID_WICImagingFactory,
      NULL,
      CLSCTX_INPROC_SERVER,
      IID_IWICImagingFactory,
      (LPVOID*)&pFactory
    );
 }
 
 if(SUCCEEDED(hr))
 {
  // デコーダの生成。ファイル名からデコーダを判別して生成。
  hr = pFactory->CreateDecoderFromFilename(
      imageFileName,
      NULL,
      GENERIC_READ,
      WICDecodeMetadataCacheOnDemand,
      &pDecoder
    );
 }
 
 if(SUCCEEDED(hr))
 {
  // 一枚目のフレームを取得。
  hr = pDecoder->GetFrame(0, &pFrame);
 }
  
 WICPixelFormatGUID guidPixelFormat;
 if(SUCCEEDED(hr))
 {
  // フォーマット情報を取得。
  hr = pFrame->GetPixelFormat(&guidPixelFormat);
 }
 
 if(SUCCEEDED(hr))
 {
  // フォーマットが 24ビット BGR (1色8ビット) か検査。
  if(guidPixelFormat != GUID_WICPixelFormat24bppBGR)
  {
   hr = E_FAIL;
  }
 }
 
 if(SUCCEEDED(hr))
 {
  // 画像の幅、高さを取得。
  hr = pFrame->GetSize(puiWidth, puiHeight);
 }
 
 if(SUCCEEDED(hr))
 {
  // 画素の領域を確保。
  *ppcbBuffer = new BYTE[*puiWidth * *puiHeight * 3];
  if(*ppcbBuffer == NULL)
  {
   hr = E_FAIL;
  }
 }
 
 if(SUCCEEDED(hr))
 {
  // 画素を取得。
  hr = pFrame->CopyPixels(
     NULL,
     (*puiWidth * 3),
     (*puiWidth * *puiHeight * 3),
     *ppcbBuffer);
 }
 
 if(FAILED(hr))
 {
  // エラーが発生した場合は、画素の領域を解放。
  if(*ppcbBuffer)
  {
   delete [] *ppcbBuffer;
  }
  *ppcbBuffer = NULL;
 }

 // ファクトリ、デコーダ、フレームの解放。
 if(pFactory)
 {
  pFactory->Release();
 }
 
 if(pDecoder)
 {
  pDecoder->Release();
 }

 if(pFrame)
 {
  pFrame->Release();
 }
 
 return hr;
}

// main 関数
int main(int argc, char* argv[])
{
 CoInitialize(NULL); // COM コンポーネントの初期化

 LPCWSTR imageFile =
  L"C:\\Users\\Public\\Pictures\\Sample Pictures\\Chrysanthemum.jpg";
 UINT uiWidth = 0, uiHeight = 0;
 BYTE *pcbBuffer = NULL;
 HRESULT hr = GetPixelsFromImageFile(
        imageFile,
        &uiWidth,
        &uiHeight,
        &pcbBuffer);

 if(pcbBuffer == NULL)
 {
  std::wcerr << L"failed to read " << imageFile << std::endl;
 }
 else
 {
  // 画素を表示。
  for(UINT y = 0; y < uiHeight; y++)
  {
   for(UINT x = 0; x < uiWidth; x++)
   {
    UCHAR r = pcbBuffer[(y * uiWidth + x) * 3 + 2];
    UCHAR g = pcbBuffer[(y * uiWidth + x) * 3 + 1];
    UCHAR b = pcbBuffer[(y * uiWidth + x) * 3 + 0];
    std::cout << "(" << x << ", " << y << ")" << " : "
        << "r=" << (int)r << ", "
        << "g=" << (int)g << ", "
        << "b=" << (int)b << std::endl;
   }
  }

 }

 if(pcbBuffer)
 {
  delete [] pcbBuffer;
 }

 CoUninitialize(); // COM コンポーネントの終了

 return 0;
}


少し補足です。

Windows Imaging Component の使用には wincodec.h をインクルードし、windowscodecs.lib をリンクする必要があります。

Windows Imaging Component は COM コンポーネントなので、ファクトリ生成前に CoInitialize() を、プログラム終了前に CoUninitialize() を実行する必要があります。

試していないのですが、gif など複数枚の画像を格納できるフォーマットの場合、IWICBitmapDecoder::GetFrameCount() で枚数を取得でき、IWICBitmapDecoder::GetFrame() の1番目の引数で取得するフレーム番号を指定して任意の画像を取得できると思います。

2013年8月25日日曜日

SyntaxHighlighter on blogger

本ブログ(blogger)で,ソースコードを見やすく色付けする SyntaxHighliter を,下記サイトを参考にして設定しました.
how to setup syntax highlighter on blogger

手順は以下になります.
  1. blogger テンプレートを "HTMLで編集" で開きます.
  2. テンプレートの <b:skin> タグの内容の末尾(CDATA内)に下記サイトのコードを追加します.http://syntaxhighlighter.googlecode.com/svn/trunk/Styles/SyntaxHighlighter.css
  3. 下記の <script> タグを, テンプレートの <head> タグの内容の末尾に追加します.
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shCore.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushCpp.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushCSharp.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushCss.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushDelphi.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushJava.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushJScript.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushPhp.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushPython.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushRuby.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushSql.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushVb.js' type='text/javascript'></script>
    <script src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushXml.js' type='text/javascript'></script>
    
  4. 下記のコードをテンプレートの </body> タグの内容の末尾に追加します.
    <script language='javascript'>
    dp.SyntaxHighlighter.BloggerMode();
    dp.SyntaxHighlighter.HighlightAll('code');
    </script>
    
  5. テンプレートの編集はここまでです.テンプレートを保存します.
  6. <pre> タグの, <name> 属性に "code" を,<class> 属性にハイライトを適用したいプログラミング言語を指定することで,SyntaxHighligher を使用できます. たとえば,php の場合は class に "php" を指定します.

    <pre name="code" class="php">     echo "I like PHP"; </pre>
    SyntaxHighligher を適用すると以下のように表示されます.文字列が青にハイライトされています.
    echo "I like PHP";
    
  7. <pre> タグ内のコードはエスケープする必要がありますが,エスケープ前のテキストからエスケープしたテキストを得るには下記のサイトが有用です.
    Quick escape
  8. サポートする言語の一覧は下記の URL で参照できます.
    http://code.google.com/p/syntaxhighlighter/wiki/Languages

設定は以上です.
本内容の設定後に別のテンプレートに変更すると本変更内容は消えてしまうようですのでご注意ください.
SyntaxHighligher を使用すると,とても見やすくなりますね!