2013年3月5日火曜日

icc profileを使った色変換



ここではicc profileを使った色変換をWPFのGraphicsを使ってやってみようと思う。
まず、WPFのGraphicsについて簡単に
GDIやGDI+と比較してWPFのGraphics(System.Windows.Media.Imaging)は割りとよくできている。

特徴


・RGBだけではなく、様々なピクセルフォーマットに対応している
 →RGB(A)/CMYK/Gray
・icc profileを使用した色変換もサポートしているよ
・Regionではなくパスの論理演算ができるよ
・ビットマップのエフェクト周りがいろいろ捗るよ
・2DGraphicだけではなくて3DGraphicもサポートしているよ

やりたいこと


  • icc profileを使った画像の色変換
  • ビットマップの画素を弄る
  • icc profileを埋め込んだ画像の生成

icc profileを埋め込んだ画像の生成


JPEGファイルを生成する場合

JpegBitmapEncoderというクラスを使用する。
こいつは任意のピクセルフォーマットのビットマップからJpegを生成するクラスだ。

元のビットマップデータはWritableBitmapというクラスで予め設定しておく。

var w = 640; var h = 480;
var bmp = WriatableBitmap(w, h, 72, 72, PixcelFormats.Cmyk32, null);

的な感じでやると幅/高さ = 640/480ピクセルの1画素がCMYK(4byte)のビットマップ
バッファが生成される。
こいつの偉いところはピクセルフォーマットによって内部の構造を隠蔽しているところだ。

こいつに予め用意しておいたCMYKのbyte配列・・・buffでFillする場合、

bmp.WritePixcels(new Int32Rect(0, 0, w, h), buff, w * 4, 0);

というように書くとCMYKのビットマップがWritableBitmapに設定される。
後は、icc profileを埋め込んだJPEGファイルの生成だ。


WriteableBitmapをJpegBitmapEncoderに渡す場合、BitmapFrameというクラスを介在
させる必要があるらしい。

つまり、BitmapFrameにWriteableBitmapを設定して、そのBitmapFrameをJpegBitmapEncoder
に渡すような感じだ。

icc profileの表現


WPFのGraphicにおけるicc profileはColorContextクラスで表現される。

var cc = new ColorContext(new Uri(@"c:\spool\JapanColor2001Coated.icc"));

といった感じだ。

icc profileの埋め込み指定


生成するJpegファイルにicc profileを埋め込む場合、

JpegBitmapEncoder.ColorContext

というプロパティがあるのでこれに設定すればうまくいきそうだが、そうは行かない。
実行時にエラーが発生してしまう。

icc profileを埋め込んだJPEGを生成する場合、BitmapFrameをCreateする際にColorContextを渡す必要がある。
※なんでわざわざこんな実装にするんだろう?

以下のようなコードになる。


var bmpframe = BitmapFrame.Create(bmp, null, new BitmapMetadata("jpg"),
new ReadOnlyCollection<ColorContext>(new List<ColorContext>() { cc }));


後は、普通にjpegを保存するコードを書く

var enc = new JpegBitmapEncoder();
enc.Frames.Add(bmpframe);
using(var fs = new FileStream(@"c:\aa.jpeg", FileMode.Create, FileAccess.Write)) {
  enc.Save(fs);
}

うーん、実に簡単だ。



ちなみに、同じCMYKのビットマップからprofileの埋め込みあり、なしでJPEGを比較した。随分違うことがわかる。

0 件のコメント: