読者です 読者をやめる 読者になる 読者になる

酢ろぐ!

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

ネガティブ(ネガポジ反転)変換

分割した記事で書かれていたのを「Windowsストアアプリで画像処理をおこなう - 酢ろぐ!」とひとつのエントリにまとめました。以下のエントリをご覧ください。




同じC#というプログラミング言語を使用しているのですが、WinForms、Windows Phone、Metro スタイル アプリと異なるプラットフォーム毎に同じ処理を書いているので、アルゴリズムは一緒だけど実装方法が異なるというとても残念なことを繰り返しています。

何度も画像を処理させるのに、UIスレッドでなければ実行できないWriteableBitmapの生成をわざわざおこなうのはあまり賢い方法ではありません。ピクデルデータであるbyte配列とその幅と高さをパラメータに取るEffectメソッドをinterfaceで定義し、それぞれ専用のエフェクトクラスにて実装をおこなう形にしたいと思います。

本記事では、Metro スタイル アプリでのネガティブ(白黒反転)処理を実装します。

IEffect.csでのinterfaceの定義

さて最初にIEffect.csにてinterfaceの定義をおこないます。

// IEffect.cs

using System;

namespace Softbuild.Media.Effects
{
    public interface IEffect
    {
        byte[] Effect(int width, int height, byte[] source);
    }
}

IEffect.csの実装をおこなう専用のエフェクトクラスの実装

ネガティブ処理は、ネガポジ反転処理と呼ばれたりもします。

撮影した写真を画像処理によって、ネガフィルムのように色調を反転させることを「ネガティブ処理」といいます。処理としては非常にシンプルで色調を反転させるには、255からピクセルの各RGB値を引くだけです。

// NegativeEffect.cs.cs

using System;

namespace Softbuild.Media.Effects
{
    /// <summary>
    /// ネガティブ処理をおこなうクラス
    /// </summary>
    public class NegativeEffect : IEffect
    {
        public byte[] Effect(int width, int height, byte[] source)
        {
            int pixelCount = width * height;
            var dest = new byte[source.Length];

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

                var b = 255 - source[index + 0];
                var g = 255 - source[index + 1];
                var r = 255 - source[index + 2];
                var a = source[index + 3];

                dest[index + 0] = (byte)Math.Min(255, Math.Max(0, b));
                dest[index + 1] = (byte)Math.Min(255, Math.Max(0, g));
                dest[index + 2] = (byte)Math.Min(255, Math.Max(0, r));
                dest[index + 3] = a;
            }

            return dest;
        }
    }
}

WindowsストアアプリでIRandomAccessStream型のストリームからWriteableBitmapオブジェクトへ変換する - 酢ろぐ!」にて紹介したWriteableBitmap用の拡張メソッド集である「WriteableBitmapExtensions」で、エフェクトを掛けた後の画像を取得できるようにしましょう。

WriteableBitmapオブジェクトからピクセルデータを取得し、ピクセルデータに対して各種専用のエフェクト処理を実行します。次に加工後のピクセルデータからWriteableBitmapオブジェクトを生成するには、WriteableBitmapExtensions.FromArrayメソッドを使用します。

// WriteableBitmapExtensions.cs

using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Softbuild.Media.Effects;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI.Xaml.Media.Imaging;

namespace Softbuild.Media
{
    static public class WriteableBitmapExtensions
    {
		〜〜 FromArrayメソッド等の記載 省略 〜〜
	
        /// <summary>
        /// パラメータ無しの画像処理をおこなう
        /// </summary>
        /// <param name="bitmap">元になるWriteableBitampオブジェクト</param>
        /// <param name="effecter">エフェクト処理オブジェクト</param>
        /// <returns>WriteableBitmapオブジェクト</returns>
        private static WriteableBitmap Effect(WriteableBitmap bmp, IEffect effecter)
        {
            // WriteableBitampのピクセルデータをバイト配列に変換する
            var srcPixels = bmp.PixelBuffer.ToArray();

            // パラメータ無しの画像処理をおこなう
            var dstPixels = effecter.Effect(bmp.PixelWidth, bmp.PixelHeight, srcPixels);

            // バイト配列からピクセルを作成する
            return WriteableBitmapExtensions.FromArray(bmp.PixelWidth, bmp.PixelHeight, dstPixels);
        }

IEffectを実装したエフェクトクラスを実装し、元画像のWriteableBitmapオブジェクトと実装したエフェクトクラスのオブジェクトをパラメータにするだけで、以下のように非常にシンプルにエフェクト処理を実行するメソッドを作成することができました。

        /// <summary>
        /// ネガポジ反転処理をしたWriteableBitampオブジェクトを返す
        /// </summary>
        /// <param name="bitmap">元になるWriteableBitampオブジェクト</param>
        /// <returns>WriteableBitampオブジェクト</returns>
        public static WriteableBitmap EffectNegative(this WriteableBitmap bmp)
        {
            return Effect(bmp, new NegativeEffect());
        }
    }
}