酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

Windowsストアアプリで口径食(Vignetting)調の画像処理を実装する

本記事では「Windowsストアアプリで画像処理をおこなう」で紹介したIEffectインターフェースをベースにして、口径食変換処理を実装しています。まだIEffectでの実装についてご覧になっていない方は先にこちらの記事をお読みください。

口径食(Vignetting)を表現してみました。中心から離れるに従って暗くなっている状態を「口径食」と呼びます。口径食による周辺光量の低下は良い現象ではないのですが、プロのカメラマンがクリエイティブの一環として、味わいのある写真になるためわざとトイカメラ風に口径食を発生させた撮影をする場合もあるようです。

本記事では、Metro スタイル アプリでの口径食調整処理を実装します。

口径食にあたる部分が既に暗くなっているグラデーション画像をクラスライブラリ内に用意しておき、エフェクト対象となる画像上にマッピングします。Effectsフォルダー配下にVignettingEffect.cs という名前のファイルを用意しておきましょう。

// VignettingEffect.cs

using System;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.UI.Xaml.Media.Imaging;

namespace Softbuild.Media.Effects
{
    /// <summary>
    /// 周辺光の低出力
    /// </summary>
    public class VignettingEffect : IEffect
    {
        /// <summary>
        /// 周辺光を表現するマスク画像
        /// </summary>
        private WriteableBitmap MaskBitamp { get; set; }

        /// <summary>
        /// マスク画像の不透明度
        /// </summary>
        private double Opacity { get; set; }

        /// <summary>
        /// VignettingEffect クラスの新しいインスタンスを初期化します。
        /// </summary>
        /// <param name="maskBitamp">周辺光を表現するマスク画像</param>
        /// <param name="opacity">マスク画像の不透明度を表現する(0.0~1.0 不透明:1.0)</param>
        public VignettingEffect(WriteableBitmap maskBitamp, double opacity)
        {
            MaskBitamp = maskBitamp;
            Opacity = opacity;
        }
        
        // ↓に続きます

ピクセルデータに対するエフェクト処理も実装してしまいましょう。

        /// <summary>
        /// 口径食調整処理をおこなう
        /// </summary>
        /// <param name="width">ビットマップの幅</param>
        /// <param name="height">ビットマップの高さ</param>
        /// <param name="source">処理前のピクセルデータ</param>
        /// <returns>処理後のピクセルデータ</returns>
        public byte[] Effect(int width, int height, byte[] source)
        {
            // マスク画像のピクセルデータを取得する
            var mask = MaskBitamp.PixelBuffer.ToArray();

            int pixelCount = width * height;
            var dest = new byte[source language=".Length"][/source];

            for (int i = 0; i < pixelCount; i++)
            {
                var index = i * 4;

                // 処理前のピクセルの各ARGB要素を取得する
                double b = source[index + 0];
                double g = source[index + 1];
                double r = source[index + 2];
                double a = source[index + 3];

                // マスク画像のピクセルの各ARGB要素を取得する
                double mb = mask[index + 0];
                double mg = mask[index + 1];
                double mr = mask[index + 2];
                double ma = mask[index + 3];

                // 処理後のピクセルの各ARGB要素を取得する
                double db, dg, dr, da;

                // マスク画像に適用する透明率を算出する
                double ax = (ma / 255) * Opacity;

                // 指定値画像のピクセルのアルファ値をチェック
                if (ax == 0)
                {
                    // マスク画像が透明なので元画像のARGB値をそのまま代入
                    db = b;
                    dg = g;
                    dr = r;
                    da = a;
                }
                else
                {
                    // アルファ値を元に合成後のARGB値を算出
                    db = (b * (1.0 - ax)) + (mb * ax);
                    dg = (g * (1.0 - ax)) + (mg * ax);
                    dr = (r * (1.0 - ax)) + (mr * ax);
                    da = a;
                }

                // 処理後のバッファへピクセル情報を保存する
                dest[index + 0] = (byte)Math.Min(255, Math.Max(0, db));
                dest[index + 1] = (byte)Math.Min(255, Math.Max(0, dg));
                dest[index + 2] = (byte)Math.Min(255, Math.Max(0, dr));
                dest[index + 3] = (byte)Math.Min(255, Math.Max(0, da));
            }

            return dest;
        }
    }
}

SoftbuildLibraryプロジェクトに含まれるWriteableBitmapの拡張メソッド群であるWriteableBitmapExtensionsクラスに、クラスライブラリにEffectVignettingAsyncメソッドを定義します。

namespace Softbuild.Media
{
    static public class WriteableBitmapExtensions
    {
        /// <summary>
        /// 口径食調整処理をしたWriteableBitampオブジェクトを返す
        /// </summary>
        /// <param name="bitmap">元になるWriteableBitampオブジェクト</param>
        /// <returns>WriteableBitampオブジェクト</returns>
        public static async Task<WriteableBitmap> EffectVignettingAsync(this WriteableBitmap bmp, double vignetting)
        {
            // クラスライブラリ内の画像をリソースを読み出す
            var maskFile = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///SoftbuildLibrary/Images/vignetting_gradation.png"));
            // StorageFileからWriteableBitampを生成する
            var maskBitamp = await WriteableBitmapExtensions.FromStreamAsync(await maskFile.OpenReadAsync());
            // 元画像とサイズと合わせる
            var resizedBmp = maskBitamp.Resize(bmp.PixelWidth, bmp.PixelHeight);
            // 幕末画像を作成する
            return Effect(bmp, new VignettingEffect(resizedBmp, vignetting));
        }
    }
}

上記のコードを実行すると、下図のように口径食調整処理した画像を得ることができます。左が元画像、右が口径食調整処理をした画像になります

f:id:ch3cooh393:20150905214536p:plain