画像処理画像処理
RGBグラフィックおよびその入出力; RGB Graphic

概要
様々な画像処理を施すために浮動小数点型のRGBで表現された画像クラスを用意する。
またその画像をファイルに入出力するための関数も用意する。

ピクセル情報のコピーについて
ピクセル情報のコピーを行うとき、GDIのSetPixel/GetPixelは遅い。(デバイスコンテキストを経由してピクセル情報が格納されているメモリにアクセスするため遅い。下記にくらべ50倍の時間を要するようです。)
そのため、Marshal.Copyを使ってピクセル情報の配列をマネージ配列にコピーしてから画像クラスに格納していく、またはその逆方向の手段を講じる。
なおピクセル情報の並びには気をつける必要があり、PixelFormat.Format32bppArgbではBGRAの順である。(ややこしい・・・)

RGBグラフィック ソースコード

namespace ColorField {

    /// <summary>RGBグラフィック</summary>
    public class GraphicRGB : Graphic<RGB, double>{

        /// <summary>コンストラクタ</summary>
        public GraphicRGB(int w, int h) : base(w, h) {
        }
        
        /// <summary>コンストラクタ</summary>
        public GraphicRGB(RGB[,] graph) : base(graph) {
        }

        /// <summary>コンストラクタ</summary>
        public GraphicRGB(Type[,] graph, Func<Type, RGB> convert_func) : base(graph, convert_func) {
        }

        /// <summary>クローン</summary>
        public override object Clone() {
            return new GraphicRGB(graph);
        }
    }
}

グラフィック基本クラス ソースコード

namespace ColorField {
    
    /// <summary>グラフィック基本クラス</summary>
    public class Graphic<Color, ChannelType> : ICloneable where Color : struct, IRGBChannelColor<ChannelType> where ChannelType : struct {
        protected int w, h;
        protected Color[,] graph;

        /// <summary>コンストラクタ</summary>
        protected Graphic() {
            this.w = this.h = 0;
            this.graph = null;
        }

        /// <summary>コンストラクタ</summary>
        public Graphic(int w, int h) {
            if(w <= 0 || h <= 0) {
                throw new ArgumentOutOfRangeException();
            }

            this.w = w;
            this.h = h;
            this.graph = new Color[w, h];
        }

        /// <summary>コンストラクタ</summary>
        public Graphic(Color[,] graph) {
            if(graph == null) {
                throw new ArgumentNullException();
            }

            this.w = graph.GetLength(0);
            this.h = graph.GetLength(1);
            this.graph = (Color[,])graph.Clone();
        }

        /// <summary>コンストラクタ</summary>
        public Graphic(Type[,] graph, Func<Type, Color> convert_func) {
            if(graph == null) {
                throw new ArgumentNullException();
            }

            this.w = graph.GetLength(0);
            this.h = graph.GetLength(1);
            this.graph = new Color[w, h];

            for(int x, y = 0; y < h; y++) {
                for(x = 0; x < w; x++) {
                    this.graph[x, y] = convert_func(graph[x, y]);
                }
            }
        }

        /// <summary>横幅</summary>
        public int Width {
            get {
                return w;
            }
        }

        /// <summary>縦幅</summary>
        public int Height {
            get {
                return h;
            }
        }

        /// <summary>インデクサ</summary>
        public Color[,] Graph {
            get {
                return graph;
            }
        }

        /// <summary>クローン</summary>
        public virtual object Clone() {
            return new Graphic<Color, ChannelType>((Color[,])graph.Clone());
        }
    }
}

RGBグラフィック入出力 ソースコード

namespace ColorField {

    /// <summary>入出力</summary>
    public static class ImageIO {

        /// <summary>GDIイメージからRGBグラフィックへコピー</summary>
        public static void Copy(out GraphicRGB graph, Bitmap image) {
            if(image == null) {
                throw new ArgumentNullException();
            }
            if(!CheckSize(image.Width, image.Height)){
                throw new ArgumentException("Image is too Large");
            }

            graph = new GraphicRGB(image.Width, image.Height);
            var bytes = new byte[image.Width * image.Height * 4];

            var bmp_data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
            Marshal.Copy(bmp_data.Scan0, bytes, 0, bytes.Length);
            image.UnlockBits(bmp_data);

            for(int x, y = 0, i = 0, w = image.Width, h = image.Height; y < h; y++) {
                for(x = 0; x < w; x++, i += 4) {
                    byte r = bytes[i + 2], g = bytes[i + 1], b = bytes[i], a = bytes[i + 3];
                    graph.Graph[x, y] = new RGB(r, g, b, a) / 255.0;
                }
            }
        }

        /// <summary>RGBグラフィックからGDIイメージへコピー</summary>
        public static void Copy(out Bitmap image, GraphicRGB graph) {
            if(graph == null) {
                throw new ArgumentNullException();
            }
            if(!CheckSize(graph.Width, graph.Height)){
                throw new ArgumentException("Image is too Large");
            }

            image = new Bitmap(graph.Width, graph.Height);

            var bytes = new byte[image.Width * image.Height * 4];

            Func<double, byte> clip = (v) => (byte)Math.Min(Math.Max((int)(v * 255), 0), 255);

            for(int x, y = 0, i = 0, w = image.Width, h = image.Height; y < h; y++) {
                for(x = 0; x < w; x++, i += 4) {
                    RGB c = graph.Graph[x, y];
                    bytes[i + 2] = clip(c.R);
                    bytes[i + 1] = clip(c.G);
                    bytes[i] = clip(c.B);
                    bytes[i + 3] = clip(c.A);
                }
            }

            var bmp_data = image.LockBits(new Rectangle(0, 0, image.Width, image.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
            Marshal.Copy(bytes, 0, bmp_data.Scan0, bytes.Length);
            image.UnlockBits(bmp_data);
        }

        /// <summary>ファイルから読み込み</summary>
        public static void Read(out GraphicRGB graph, string filepath) {
            Bitmap image = (Bitmap)Image.FromFile(filepath);
            Copy(out graph, image);
        }

        /// <summary>ファイルから書き込み</summary>
        public static void Write(GraphicRGB graph, string filepath) {
            Bitmap image;

            Copy(out image, graph);
            image.Save(filepath);
        }

        /// <summary>大きさが有効範囲内かチェック</summary>
        public static bool CheckSize(int width, int height) {
            try {
                checked {
                    int v = width * height * 4;
                }
                return true;
            }
            catch {
                return false;
            }
        }
    }
}

関連項目
RGB空間

ライブラリライブラリ
確率統計確率統計
線形代数線形代数
幾何学幾何学
最適化最適化
微分方程式微分方程式
画像処理画像処理
補間補間
機械学習機械学習
クラスタリングクラスタリング
パズルゲーム・パズル
未分類未分類