この記事では、さまざまな Windows ランタイム API で画像を表すために使用される SoftwareBitmap クラスを、さまざまな画像処理アルゴリズムを提供するオープン ソースのネイティブ コード ライブラリである Open Source Computer Vision Library (OpenCV) を使用して使用する方法について説明します。
この記事の例では、C# を使用して作成されたアプリを含め、UWP アプリから使用できるネイティブ コード Windows ランタイム コンポーネントを作成する手順について説明します。 このヘルパー コンポーネント は、OpenCV のぼかし画像処理関数を使用する 1 つのメソッド Blur を公開します。 このコンポーネントは、OpenCV ライブラリで直接使用できる基になるイメージ データ バッファーへのポインターを取得するプライベート メソッドを実装しているため、ヘルパー コンポーネントを簡単に拡張して他の OpenCV 処理機能を実装できます。
- SoftwareBitmap の使用の概要については、「ビットマップ イメージの作成、編集、保存」を参照してください。
- OpenCV ライブラリの使用方法については、 https://opencv.orgを参照してください。
- MediaFrameReader でこの記事に示されている OpenCV ヘルパー コンポーネントを使用して、カメラからフレームのリアルタイム画像処理を実装する方法については、「MediaFrameReader で OpenCV を使用する」を参照してください。
- いくつかの異なる効果を実装する完全なコード例については、Windows ユニバーサル サンプル GitHub リポジトリの カメラ フレーム + OpenCV サンプル を参照してください。
注
この記事で詳しく説明する OpenCVHelper コンポーネントで使用される手法では、処理するイメージ データが GPU メモリではなく CPU メモリに存在する必要があります。 そのため、 MediaCapture クラスなどのイメージのメモリ位置を要求できる API の場合は、CPU メモリを指定する必要があります。
OpenCV 相互運用のためのヘルパー Windows ランタイム コンポーネントを作成する
1. 新しいネイティブ コード Windows ランタイム コンポーネント プロジェクトをソリューションに追加する
- Visual Studio でソリューション エクスプローラー内のソリューションを右クリックし、[追加] ->[新しいプロジェクト]を選択して、新しいプロジェクトを追加します。
- [Visual C++] カテゴリで、[Windows ランタイム コンポーネント (ユニバーサル Windows)] を選択します。 この例では、プロジェクトに "OpenCVBridge" という名前を付け、[ OK] をクリックします。
- [新しい Windows ユニバーサル プロジェクト] ダイアログで、アプリのターゲットと最小 OS バージョンを選択し、[OK] をクリックします。
- ソリューション エクスプローラーで自動生成されたファイル Class1.cppを右クリックし、[ 削除] を選択します。確認ダイアログボックスが表示されたら、[削除] を選択 します。 次に、Class1.h ヘッダー ファイルを削除します。
- OpenCVBridge プロジェクト アイコンを右クリックし、[
Add- ] を選択します。[クラス の追加Class... ] ダイアログボックスで、[ クラス名 ] フィールドに「OpenCVHelper」と入力し、[OK ] をクリックします。 コードは、後の手順で作成されたクラス ファイルに追加されます。
2. OpenCV NuGet パッケージをコンポーネント プロジェクトに追加する
- ソリューション エクスプローラーで OpenCVBridge プロジェクト アイコンを右クリックし、[ NuGet パッケージの管理]を選択します。
- NuGet パッケージ マネージャー ダイアログが開いたら、[ 参照 ] タブを選択し、検索ボックスに「OpenCV.Win」と入力します。
- [OpenCV.Win.Core] を選択し、インストールをクリックします。 プレビュー ダイアログ
で、 [OK] をクリックします。 - 同じ手順を使用して、"OpenCV.Win.ImgProc" パッケージをインストールします。
注
OpenCV.Win.Core と OpenCV.Win.ImgProc は定期的に更新されず、Store コンプライアンス チェックに合格しないため、これらのパッケージは実験のみを目的としています。
3. OpenCVHelper クラスを実装する
OpenCVHelper.h ヘッダー ファイルに次のコードを貼り付けます。 このコードには、インストールした Core パッケージと ImgProc パッケージの OpenCV ヘッダー ファイルが含まれており、次の手順で示す 3 つのメソッドが宣言されています。
#pragma once
// OpenCVHelper.h
#include <opencv2\core\core.hpp>
#include <opencv2\imgproc\imgproc.hpp>
namespace OpenCVBridge
{
public ref class OpenCVHelper sealed
{
public:
OpenCVHelper() {}
void Blur(
Windows::Graphics::Imaging::SoftwareBitmap^ input,
Windows::Graphics::Imaging::SoftwareBitmap^ output);
private:
// helper functions for getting a cv::Mat from SoftwareBitmap
bool TryConvert(Windows::Graphics::Imaging::SoftwareBitmap^ from, cv::Mat& convertedMat);
bool GetPointerToPixelData(Windows::Graphics::Imaging::SoftwareBitmap^ bitmap,
unsigned char** pPixelData, unsigned int* capacity);
};
}
OpenCVHelper.cpp ファイルの既存の内容を削除し、次のインクルード ディレクティブを追加します。
#include "pch.h"
#include "OpenCVHelper.h"
#include "MemoryBuffer.h"
インクルード ディレクティブの後に、次の using ディレクティブを 追加します。
using namespace OpenCVBridge;
using namespace Platform;
using namespace Windows::Graphics::Imaging;
using namespace Windows::Foundation;
using namespace Microsoft::WRL;
using namespace cv;
次に、 GetPointerToPixelData メソッドをOpenCVHelper.cppに追加します。 このメソッドは SoftwareBitmap を 受け取り、一連の変換を通じて、基になるデータ バッファーへのポインターを char 配列として取得できるピクセル データの COM インターフェイス表現を取得します。
まず、ピクセル データを含む BitmapBuffer を取得するには、 LockBuffer を呼び出し、OpenCV ライブラリがそのピクセル データを変更できるように、読み取り/書き込みバッファーを要求します。
CreateReference は、 IMemoryBufferReference オブジェクトを取得するために呼び出されます。 次に、IMemoryBufferByteAccess インターフェイスは IInspectable としてキャストされ、すべての Windows ランタイム クラスの基本インターフェイスであり、QueryInterface を呼び出して、ピクセル データ バッファーを char 配列として取得できるようにする IMemoryBufferByteAccess COM インターフェイスを取得します。 最後に、IMemoryBufferByteAccess::GetBuffer
bool OpenCVHelper::GetPointerToPixelData(SoftwareBitmap^ bitmap, unsigned char** pPixelData, unsigned int* capacity)
{
BitmapBuffer^ bmpBuffer = bitmap->LockBuffer(BitmapBufferAccessMode::ReadWrite);
IMemoryBufferReference^ reference = bmpBuffer->CreateReference();
ComPtr<IMemoryBufferByteAccess> pBufferByteAccess;
if ((reinterpret_cast<IInspectable*>(reference)->QueryInterface(IID_PPV_ARGS(&pBufferByteAccess))) != S_OK)
{
return false;
}
if (pBufferByteAccess->GetBuffer(pPixelData, capacity) != S_OK)
{
return false;
}
return true;
}
次に、以下に示す TryConvert メソッドを追加します。 このメソッドは SoftwareBitmap を受け取り、画像データ バッファーを表すために OpenCV が使用するマトリックス オブジェクトである Mat オブジェクトへの変換を試みます。 このメソッドは、上記で定義
注
次の例では、CV_8UC4定数をピクセル形式として指定して、Matオブジェクトを作成します。 つまり、このメソッドに渡
作成された Mat オブジェクトの浅いコピーがメソッドから返されるため、このバッファーのコピーではなく、SoftwareBitmap によって参照されるのと同じデータ ピクセル データ バッファーに対して処理が実行されます。
bool OpenCVHelper::TryConvert(SoftwareBitmap^ from, Mat& convertedMat)
{
unsigned char* pPixels = nullptr;
unsigned int capacity = 0;
if (!GetPointerToPixelData(from, &pPixels, &capacity))
{
return false;
}
Mat mat(from->PixelHeight,
from->PixelWidth,
CV_8UC4, // assume input SoftwareBitmap is BGRA8
(void*)pPixels);
// shallow copy because we want convertedMat.data = pPixels
// don't use .copyTo or .clone
convertedMat = mat;
return true;
}
最後に、このヘルパー クラスの例 では、単に上記で定義した TryConvert メソッドを使用してぼかし操作のソース ビットマップとターゲット ビットマップを表す Mat オブジェクトを取得し、OpenCV ImgProc ライブラリから blur メソッドを呼び出す単一の画像処理メソッド Blur を実装します。 ぼかし を
void OpenCVHelper::Blur(SoftwareBitmap^ input, SoftwareBitmap^ output)
{
Mat inputMat, outputMat;
if (!(TryConvert(input, inputMat) && TryConvert(output, outputMat)))
{
return;
}
blur(inputMat, outputMat, cv::Size(15, 15));
}
ヘルパー コンポーネントを使用した単純な SoftwareBitmap OpenCV の例
OpenCVBridge コンポーネントが作成されたので、OpenCV ブラー メソッドを使用して SoftwareBitmap を変更する単純な C# アプリを作成できます。 UWP アプリから Windows ランタイム コンポーネントにアクセスするには、最初にコンポーネントへの参照を追加する必要があります。 ソリューション エクスプローラーで、UWP アプリ プロジェクトの下にある [ 参照 ] ノードを右クリックし、[ 参照の追加]...[参照マネージャ]ダイアログボックスで、[ プロジェクト]->[ソリューション]を選択します。 OpenCVBridge プロジェクトの横にあるチェック ボックスをオンにし、[OK]をクリックしてください。
次のコード例では、ユーザーがイメージ ファイルを選択し、 BitmapDecoder を使用してイメージの SoftwareBitmap 表現を作成できます。 SoftwareBitmap の操作の詳細については、「ビットマップ イメージの作成、編集、保存」を参照してください。
この記事で既に説明したように、 OpenCVHelper クラスでは、提供されるすべての SoftwareBitmap イメージを事前乗算されたアルファ値を持つ BGRA8 ピクセル形式を使用してエンコードする必要があるため、イメージがまだこの形式でない場合は、 Convert を呼び出してイメージを予期された形式に変換します。
次に、ぼかし操作のターゲットとして使用する SoftwareBitmap が作成されます。 入力イメージのプロパティは、一致する形式のビットマップを作成するコンストラクターの引数として使用されます。
OpenCVHelper の新しいインスタンスが作成され、Blur メソッドが呼び出され、ソース ビットマップとターゲット ビットマップが渡されます。 最後に、出力イメージを XAML イメージ コントロールに割り当てるために SoftwareBitmapSource が作成されます。
このサンプル コードでは、既定のプロジェクト テンプレートに含まれる名前空間に加えて、次の名前空間の API を使用します。
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Pickers;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media.Imaging;
FileOpenPicker fileOpenPicker = new FileOpenPicker();
fileOpenPicker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
fileOpenPicker.FileTypeFilter.Add(".jpg");
fileOpenPicker.ViewMode = PickerViewMode.Thumbnail;
var inputFile = await fileOpenPicker.PickSingleFileAsync();
if (inputFile == null)
{
// The user cancelled the picking operation
return;
}
SoftwareBitmap inputBitmap;
using (IRandomAccessStream stream = await inputFile.OpenAsync(FileAccessMode.Read))
{
// Create the decoder from the stream
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
// Get the SoftwareBitmap representation of the file
inputBitmap = await decoder.GetSoftwareBitmapAsync();
}
if (inputBitmap.BitmapPixelFormat != BitmapPixelFormat.Bgra8
|| inputBitmap.BitmapAlphaMode != BitmapAlphaMode.Premultiplied)
{
inputBitmap = SoftwareBitmap.Convert(inputBitmap, BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
}
SoftwareBitmap outputBitmap = new SoftwareBitmap(inputBitmap.BitmapPixelFormat, inputBitmap.PixelWidth, inputBitmap.PixelHeight, BitmapAlphaMode.Premultiplied);
var helper = new OpenCVBridge.OpenCVHelper();
helper.Blur(inputBitmap, outputBitmap);
var bitmapSource = new SoftwareBitmapSource();
await bitmapSource.SetBitmapAsync(outputBitmap);
imageControl.Source = bitmapSource;
関連トピック