2017. 8. 8.
石立 喬

Visual C#とWin2DによるUWPでの画像処理(1)

――― Visual C#でプロジェクトを作成し、Win2Dの画像処理Effectクラスを使う ―――

 Visual Studio Community 2017でVisual C++を使用してUWPアプリを作成してきたが、画像処理の段階にきて困難に直面した。Canvasで使用して便利なWin2D APIには、画像処理用の多数の優れたクラスが用意されているが、C#でしか使用できないメソッドがあり、非同期処理キーワードもC#以外では使えない。したがって、画像処理に限って、UWPアプリをVisual C#で実現することにした。
 Win2Dのインストールと各種設定、XAMLコードの作成方法など、Visual C++とVisual C#に共通な部分の説明は、省略または簡略化されているので、従来の項目も随時参照されたい。

Win2Dに用意されている画像処理用Effectクラス群
 Win2Dの名前空間Microsoft.Graphics.Canvas.Effectsには、優れた画像処理用クラスが多数用意されている。IGraphicsEffectインターフェイスからの実装で、下記はその主要なものである(最期につくEffectを省略してある。たとえば、BrightnessはBrightnessEffectの意味)。
 ピクセル単独の処理 --- Brightness、Contrast、GammaTransfer、Grayscale、Invert
 周辺との演算処理 ----- ConvolveMatrix、EdgeDetection、Emboss、GaussianBlur、Morphology
 色変換処理 ---------- ColorMatrix、DiscreteTransfer、HueRotation、HueToRgb、RgbToHue、Saturation
 画像合成処理 -------- ArithmeticComposite、BlendComposite、CrossFade

Visual C#とWin2Dを用いてUWPアプリを作成するための準備
 Visual C#を始めるに当たっての新しい部分を説明する。すでに紹介したVisual C++と共通な部分は省略してある。
1)「スタートページ」で、右下の「新しいプロジェクト」蘭の「新しいプロジェクトを作成・・・」をクリックする。
2)「新しいプロジェクト」ウィンドウで、図1のように「Visual C#」→「Windowsユニバーサル」→「空白のアプリ」を選択し、名前(例えばCSharp1)を付けて「OK」をクリックする。


図1 「新しいプロジェクト」ウィンドウでVisual C#によるUWPを選択する


3)「新しいユニバーサルWindowsプロジェクト」ウィンドウで「OK」をクリックする。
4)「ソリューションエクスプローラー」で「MainPage.xaml」をダブルクリックしてXAMLコードを表示させると、図2のコードができている。


図2 できているスケルトンのXAMLコード


5)同様に、「MainPage.xaml」→「MainPage.xaml.cs」をダブルクリックすると図3のC#コードが確認できる。


図3 できているスケルトンのC#コード


6)Windows10を開発モードに設定する(Visual C++の場合と同じなので詳細省略)
7)Win2Dをインストールする(同上)
8)CanvasでWin2Dを使用するための準備をする
 ・XAMLコードに、xmls:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"を追加する。
 ・C#コードに、using Microsoft.Graphics.Canvas.UI.Xaml;と、using Microsoft.Graphics.Canvas;を追加する。

XAMLコードにCanvasControlを設定する
 図4は設定後のXAMLコードを示す。すでに述べたように、Canvasを使用するために、Microsoft.Graphics.Canvas.UI.Xamlが名前空間として記述されている。CanvasControlについては、まずCanvasを「ツールボックス」から持ってきてから、「Canvas」を「canvas:CanvasControl」に書き換え、DrawイベントとCreateResourcesイベントを追加する(詳細については、Visual C++の場合の説明を参照)。


図4 XAMLコードにxmlns:canvasとcanvas:CanvasContlrolを追加する


原画像の準備
 原画像はAssetsフォルダの中に入れておく。単純にコピーして入れるだけではだめで、下記の手順が必要である。
 1)「ソリューションエクスプローラー」ウィンドウで「Assets」フォルダを右クリックする
 2)「追加」→「既存の項目」をクリックする
 3)ウィンドウが開くので、そこから画像ファイルを選択して「追加」をクリックする
 4)「ソリューションエクスプローラー」の「Assets」の下に画像名が表示されるのを確認する

非同期処理の概要説明
 CanvasBitmapクラスとして画像を読み込むCanvasBitmap.LoadAsyncメソッドは非同期処理用なので、LoadAsyncメソッドの前にawaitキーワード(オペレータ)を記述する。awaitキーワードが含まれるメソッドは、非同期処理であることを明示するために、asyncキーワード(修飾子)と戻り値のTaskクラスを記述する(戻り値が無い場合、ある場合はTask<T>を使用)。非同期処理メソッドには、メソッド名の最後にAsyncを付ける習慣がある。
 CanvasControlのCreateResourcesイベントから発生するCanvas_CreateResourcesイベントハンドラーから非同期処理」メソッドを呼び出すには、TaskAsyncActionメソッドを用いて非同期処理の追跡をさせる。

使用するインターフェイス、クラス、プロパティ、メソッドなど
 ここでは多くのインターフェイスが用いられている。インターフェイスは、C++ではなじみが薄いが、JavaやC#、VBでは多用されている。抽象クラスのようなもので、ここから派生した(実装と呼ぶ)多くのクラスが共通的に用いられる。
 各画像処理用Effectは、IGraphicsEffectインターフェイスの実装で、各EffectのSourceプロパティはIGraphicsEffectSourceインターフェイスからのCanvasBitmapクラスを使用する。
 Canvasへの描画に用いるDrawImageメソッドは、引数としてICanvasImageインターフェイスのCanvasBitmapクラスを受け取る。ICanvasImageインターフェイスはIGraphicsEffectSourceインターフェイスから派生しているので、DrawImageの引数として使用できる。
◎CanvasBitmapクラス
 画像ファイルを読み込んでbitmapとするCanvasBitmap. LoadAsyncメソッドの引数は下記の通り。
   LoadAsync(
     ICanvasResourceCreater  resourceCreater, ----- 該当するCanvasControlを指定する、senderとして渡される
     String               filename ------------ 読み込む画像ファイル名であるが、Assetsフォルダから始める
   )
◎InvertEffectクラス
 画像反転に用いるクラスで、ネガ化に使用する。SourceプロパティはIGraphicsEffectSourceインターフェイスの画像を使用する。
◎GrayscaleEffectクラス
 グレイスケール化に用いるクラスで、白黒画像が得られる。SourceプロパティはIGraphicsEffectSourceである。
◎ContrastEffectクラス
 コントラストの強調(抑制も)に用いるクラスで、たとえば、曇天下での写真にメリハリをつけるのに使用する。主なプロパティは下記の通り。
   Contrast -------- 範囲は-1〜1、デフォルトは0(変化なし)
   ClampSource ---- 原画像の値を0〜1にクランプしてからコントラスト処理をする。trueまたはfalseで、デフォルトはfalse
   Source --------- IGraphicsEffectSource
◎CanvasDrawingSession.DrawImageメソッド
 Canvas上に画像を描画するのに使用する。DrawImageメソッドには多くのオーバーロードがあるが、ここでは
   DrawImage{
     ICanvasImage image, ---- CanvasBitmapクラス、各種画像処理Effectクラスが使用できる
     float      x, -------- Canvas上のX座標、ここへ画像の左辺が来る
     float      y, -------- Canvas上のY座標、ここへ画像の上辺が来る
   )
を使用する。
◎CanvasCreateResourcesEventArgs.TrackAsyncActionメソッド
 senderに非同期動作を追跡させて、CreateSoursesAsync動作が完了したことを確認する。引数はIAsyncActionインターフェイスである。CanvasBitmap.LoadAsyncメソッドのような非同期メソッドが含まれるCreateResourcesAsyncに使用する。
◎WindowsRuntimeSystemExtensions.AsAsyncActionメソッド
 戻り値はIAsyncActionインターフェイスなので、これを上述のTrackAsyncActionメソッドの引数とする。
◎asyncキーワード(修飾子)
 それに続くメソッドが非同期処理であることを示す修飾子である。メソッド内部にawaitキーワードが用いられる。awaitが来るまでは、同期的に動作する。メソッド名には、最後にAsyncを付ける慣習がある。
◎awaitキーワード(オペレーター)
 awaitが指定するTaskが完了するまで、asyncで指定された非同期処理メソッドを中断させる。awaitの後には、戻り値としてTaskクラスを指定する。
◎Taskクラス
 非同期処理メソッドの戻り値として用いる。Taskクラスは、非同期的な動作そのものである。メソッドが戻り値を持たない(return命令がない)場合もTaskと指定しておく。

プログラム
 図5は、プログラムの最初の名前空間設定の部分で、注釈の理由で名前空間を追加してある。図6は、MainPageクラスの最初の部分で、MainPage内のグローバル・オブジェクトを宣言し、タイトルバーにタイトルを追加した。
 図7は、Canvas_CreateResourcesイベントハンドラーと、そこから呼び出される非同期処理CreateResourcesAsyncメソッドの内容で、原画像を非同期的に読み込んでCanvasBitmapクラスのbitmapとし、各画像処理用クラスを生成、設定している。
 図8は、以上の結果として得られた各画像を描画するCanvas_Drawイベントハンドラーの内容である。


図5 必要な名前空間を追加する



図6 各種画像処理クラスをグローバルに宣言し、タイトルも設定する



図7 画像を非同期で読み込み各種画像処理クラスを設定する



図8 Canvas上に描画するイベントハンドラー


得られた結果
 図9は得られた結果で、左上が原画像、右上は反転画像(InvertEffect)、左下はグレイ化画像(GrayscaleEffect)、右下はコントラスト強調画像(ContrastEffect)である。


図9 得られた結果







「Visual C++の勉強部屋」(目次)へ