C#(GDI+) C#(others)  Delphi(Graphics) Delphi(others)  BBS Blog   Link Misc. Index

C#(GDI+) : Contour

C#(GDI+) : Contour


R, G, B をそれぞれ一次独立な直交座標とみたてて、RGB 空間のなかでの「色の距離」をはかって、輪郭を描画する Contour() をつくった。


photo by 0 W8ing


Contour() では HistoStretch() の有無を設定できる。さらに b24bitfasle のときは、返される画像はグレースケールであるが、b24bittrue のときは PixelFormat.Format24bppRgb のフォーマットで返す。

全コードをしめす。

最新の ImageUtils.csここからダウンロードできる。

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using System.Drawing.Imaging;
using ImageUtils;

namespace Contour
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        public static bool Contour(ref Bitmap bmp, bool bStretch, bool b24bit)
        {
            if (bmp.PixelFormat != PixelFormat.Format24bppRgb)
                return false;

            int w = bmp.Width;
            int h = bmp.Height;

            Rectangle rct = new Rectangle(0, 0, w - 1, h - 1);

            double[] dis = new double[4];

            int[] ix = new int[4];
            int[] iy = new int[4];
            double max;
            double dd = 1.732d;
            byte r, g, b;

            Bitmap tmp = new Bitmap(w, h, PixelFormat.Format8bppIndexed);
            ImgUtils.SetGrayPalette(tmp);

            BmpProc24 src = new BmpProc24(bmp);
            BmpProc8 dst = new BmpProc8(tmp);

            for (int y = 0; y < h; y++)
                for (int x = 0; x < w; x++)
                {
                    src.SetXY(x, y);
                    r = src.R; g = src.G; b = src.B;

                    ix[0] = x + 1; iy[0] = y - 1;   // upper-right
                    ix[1] = x + 1; iy[1] = y;       // right
                    ix[2] = x + 1; iy[2] = y + 1;   // lower-right
                    ix[3] = x; iy[3] = y + 1;   // lower

                    for (int i = 0; i < 4; i++)
                        if (rct.Contains(ix[i], iy[i]))
                        {
                            src.SetXY(ix[i], iy[i]);
                            dis[i] = (src.R - r) * (src.R - r) +
                                     (src.G - g) * (src.G - g) +
                                     (src.B - b) * (src.B - b);
                        }
                        else
                            dis[i] = 0d;

                    max = 0;

                    for (int i = 0; i < 4; i++)
                        if (dis[i] > max) max = dis[i];

                    dst[x, y] = ImgUtils.AdjustByte(255d - Math.Sqrt(max) / dd);
                }

            ImgUtils.CallDispose(dst, src);

            if (bStretch) ImgUtils.HistoStretch(ref tmp);

            if (b24bit)
            {
                Graphics gr = Graphics.FromImage(bmp);
                gr.DrawImageUnscaled(tmp, 0, 0);
                gr.Dispose();
                tmp.Dispose();
            }
            else
            {
                bmp.Dispose();
                bmp = tmp;
            }

            return true;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Bitmap bmp = new Bitmap(@"C:\Home\img\camellia.png");

            int w = bmp.Width;
            int h = bmp.Height;

            Graphics g = this.CreateGraphics();
            g.DrawImageUnscaled(bmp, 5, 5);

            Bitmap tmp = bmp.Clone() as Bitmap;
            if (Contour(ref tmp, false, false))
            {
                g.DrawImageUnscaled(tmp, 5, h + 10);
                Clipboard.SetImage(tmp);
            }
            tmp.Dispose();

            tmp = bmp.Clone() as Bitmap;
            if (Contour(ref tmp, true, false))
                g.DrawImageUnscaled(tmp, w + 10, h + 10);
            tmp.Dispose();

            g.Dispose();
            bmp.Dispose();
        }

    }
}


まず、カレントピクセルに対して、その右上、右、右下、下のピクセルの色の距離の二乗を計算して、配列に入れる。

            double[] dis = new double[4];

            int[] ix = new int[4];
            int[] iy = new int[4];

...

                    src.SetXY(x, y);
                    r = src.R; g = src.G; b = src.B;

                    ix[0] = x + 1; iy[0] = y - 1;   // upper-right
                    ix[1] = x + 1; iy[1] = y;       // right
                    ix[2] = x + 1; iy[2] = y + 1;   // lower-right
                    ix[3] = x;     iy[3] = y + 1;   // lower

                    for (int i = 0; i < 4; i++)
                        if (rct.Contains(ix[i], iy[i]))
                        {
                            src.SetXY(ix[i], iy[i]);
                            dis[i] = (src.R - r) * (src.R - r) +
                                     (src.G - g) * (src.G - g) +
                                     (src.B - b) * (src.B - b);
                        }
                        else
                            dis[i] = 0d;

そして、その最大値をもとめ、平方根を 255 に規格化してから、255 から引いた値をカレントの色とする。

                    max = 0;

                    for (int i = 0; i < 4; i++)
                        if (dis[i] > max) max = dis[i];


                    dst[x, y] = ImgUtils.AdjustByte(255d - Math.Sqrt(max) / dd);

以上を全ピクセルについて行った後、b24bit に応じた処理をして終わり。

            if (bStretch) ImgUtils.HistoStretch(ref tmp);

            if (b24bit)
            {
                Graphics gr = Graphics.FromImage(bmp);
                gr.DrawImageUnscaled(tmp, 0, 0);
                gr.Dispose();
                tmp.Dispose();
            }
            else
            {
                bmp.Dispose();
                bmp = tmp;
            }

            return true;


このフィルタは、KuwaharaAlpha() と組み合わせるとおもしろい。



KuwaharaAlpha() block = 6 では、



となり、なかなか雰囲気がある。








■ 参考

対応するブログ記事

▲top