Bridgeパターンの3回目。前回はCocoaで画像を取り扱うクラスNSImageについて議論した訳だが、今回も画像に関するクラスを取り上げよう。ただし、Cocoaのクラスではない。TigerことMac OS X 10.4から追加された、高機能画像処理フレームワークであるCore Imageだ。

Core Imageの二面性

Core Imageは、画像処理を行うためのフレームワークだ。非常に高機能で、画像のクロップ、色の補正、エフェクトの適用、画像の合成、トランジションエフェクトの作成など、画像処理として思いつくものはほとんどサポートされている。言ってみれば、Photoshopを作ることができるフレームワークだ。

画像処理は、非常に重い処理だ。画像サイズが大きくなるほど、その影響は顕著になる。そこで、Core Imageでは近年性能向上が著しいGPUを利用している。Core ImageはGPUへのアクセスをラップしてくれるもの、ととらえることもできるだろう。Macの機種によって搭載しているGPUは異なるが、その差も吸収してくれる。

つまりCore Imageは、高機能な画像処理をプログラマに提供しながら、GPUのようなハードウェアへアクセスするという、二面性を持っている訳だ。ここを、Bridgeパターン使って眺めてみよう、というのが今回の主題だ。

Core Imageの詳しいドキュメントは、『Core Imageプログラミングガイド概論』にある。また、Core Imageを利用したプログラミングは、過去の記事『Core Imageで体験 - Mac OS Xの高速画像処理』でも紹介したので、そちらも参考にしてほしい。

プログラマが利用する高レベルAPI

プログラマがCore Imageを使うときの手順を紹介しよう。

まずは、サンプルコードを見てほしい。これは、上述の『Core Imageプログラミング概論』に出てくる、画像の色相の調整を行うコードだ。

List 1. (『Core Imageプログラミング概論』より引用)

// フィルタを取得する。
hueAdjust = [CIFilter filterWithName:@"CIHueAdjust"];

// フィルタの設定を行う。
[hueAdjust setDefaults];
[hueAdjust setValue:myCIImage forKey:@"inputImage"];  
[hueAdjust setValue:[NSNumber numberWithFloat:2.094]  
                    forKey:@"inputAngle"];

// 処理された画像を取得する。
result = [hueAdjust valueForKey:@"outputImage"];

まず、フィルタを取得する。これには、CIFilterのfilterWithName:を使う。名前を使ってフィルタを指定する。取得できるのは、CIFilterのサブクラスとなる。

次にフィルタの設定を行うのだが、これに使うのは先ほど取得したサブクラス特有のメソッドではない。さらに、CIFilterのメソッドですらない。すべての設定は、キー値コーディングのメソッドであるsetValue:forKey:を使うことになる。設定に使うキーは、ドキュメントを見て確認する。

キー値コーディングを使う事の利点は、サブクラスごとにヘッダファイルを用意する必要が無くなる事だろうか。Core Imageのフィルタは100を超える数があるので、ヘッダを作成する労力を嫌ったのかもしれない。逆に欠点は、設定項目をいちいちドキュメントで調べる必要があり、エディタのコード補完機能に頼れない事だ。

最後に、エフェクトのかけられた画像を取得するのだが、これにもキー値コーディングのメソッドを使う。valueForKey:だ。

このように、プログラマから見たCore ImageのAPIは、ほぼキー値コーディングのsetValue:forKey:とvalueForKey:である、という事になる。これが高レベルAPIだ。

GPUにアクセスする低レベルAPI

次に、プログラマによって指定された設定に従って、GPUを使って画像処理を行う部分を見てみよう。この処理の記述には、Core Image Kernel Languageと呼ばれる言語を使う。Open GLのシェーダを記述するOpen GL Shading Languageのサブセットでもあり、C言語に似た使用感覚となる。

次のサンプルは、先ほどと同様に、『Core Imageプログラミング概論』からの引用だ。かすみの除去を行っている。

List 2. (『Core Imageプログラミング概論』より引用)

kernel vec4 myHazeRemovalKernel(
                    sampler src,
                    __color color,
                    float distance,
                    float slope)
{
    vec4   t;
    float  d;

    d = destCoord().y * slope  +  distance;
    t = unpremultiply(sample(src, samplerCoord(src)));
    t = (t - d*color) / (1.0-d);
    return premultiply(t); 
}

destCoord、unpremultiply、sample、samplerCoord、premultiplyといった関数がある。これらが、Core Image Kernel Languageが提供するものとなる。この言語のリファレンスは、『Core Image Kernel Language Reference』を参照してほしい。

これを使って、Core ImageからGPUへとアクセスすることになる。

BridgeパターンとしてのCore Image

このような構造のCore Imageを、Bridgeパターンとして表してみよう。

Bridgeパターンの主要登場人物は、AbstractionとImplementorの二人だ。ClientへのAPIを提供するAbstractionは、CIFilterとなるだろう。Concrete Abstractionとして、たくさんのCIFilterのサブクラスが相当する。Implementorにあたるものとしては、CIKernelというクラスがある。CIFilterからGPUへのアクセスするためにある。

次のような図で表すことができるだろう。

Clientは、AbstractionであるCIFilterを使う。ここでは、高レベルAPIとしてキー値コーディングを使うことになる。CIFilterからは、ImplmentorであるCIKernelにアクセスする。ここでは、低レベルAPIであるCore Image Kernel Languageが使われる。

CIKernelでは、現在動作しているMacのGPUをチェックしている。Core Image Kernel Languageがサポートされているようならば、それをGPUへ送る。サポートされていない場合は、CPUで処理をさせることになる。この場合、負荷の高すぎる処理は自動的に間引かれることになるが。このように、実装に応じた処理を行うことになる。

これで、BridgeパターンとしてのCore Imageが明確になっただろう。Abstractionは、キー値コーディングを使って、実装やフィルタの種類に依存しないAPIを提供する。ImplementorへはCore Image Kernel Languageを使ってアクセスし、GPUの有無といった実装に依存した処理を行わせる。

Bridgeパターンの例として、前回のNSImage、今回のCore Imageと、画像に関するクラスが続いた。ハードウェアに強く依存する事の多い画像処理は、Bridgeパターンの適用が向いていると言えるかもしれない。

提供: 毎日キャリアバンク

毎日キャリアバンクではITエンジニア出身のキャリアコンサルタントで形成する IT専門のチームを編成し、キャリアに応じた専任コンサルタントがご相談を承り ます。キャリアチェンジから市場価値の可能性、ご収入などの相談から面接のア ドバイスまでお気軽にご相談ください。求人情報誌や転職情報サイトなどで一般 に公開されていないような「急募求人案件」も随時ご紹介が可能です。まずはご 登録ください!