WPFヘルパー:ImageFilters.cs ー 画像フィルターライブラリ(メソッドチェーン対応)

コンピュータ

ImageBufferを対象にした画像フィルター

ImageFilters.cs

using ImageBuffer = Maywork.WPF.Helpers.ImageBufferHelper.ImageBuffer;

namespace Maywork.WPF.Helpers;

public static class ImageFilters
{

    // グレースケール化
    public static ImageBuffer ToGray(this ImageBuffer src)
    {
        int width = src.Width;
        int height = src.Height;

        if (src.Channels == 1)
        {
            return src;
        }

        var dst = ImageBufferHelper.Create(width, height, 1);

        byte[] sp = src.Pixels;
        byte[] dp = dst.Pixels;

        Parallel.For(0, height, y =>
        {
            int si = y * src.Stride;
            int di = y * width;

            for (int x = 0; x < width; x++)
            {
                byte b = sp[si];
                byte g = sp[si + 1];
                byte r = sp[si + 2];

                int gray = (299 * r + 587 * g + 114 * b) / 1000;

                dp[di] = (byte)gray;

                si += src.Channels;
                di++;
            }
        });

        return dst;
    }    

    // 2値化
    public static ImageBuffer ToBinary(this ImageBuffer src, byte threshold = 128)
    {
        int width = src.Width;
        int height = src.Height;

        var dst = ImageBufferHelper.Create(width, height, 1);

        byte[] sp = src.Pixels;
        byte[] dp = dst.Pixels;

        Parallel.For(0, height, y =>
        {
            int si = y * src.Stride;
            int di = y * width;

            for (int x = 0; x < width; x++)
            {
                int gray;

                if (src.Channels == 1)
                {
                    gray = sp[si];
                }
                else
                {
                    byte b = sp[si];
                    byte g = sp[si + 1];
                    byte r = sp[si + 2];

                    gray = (299 * r + 587 * g + 114 * b) / 1000;
                }

                dp[di] = (byte)(gray >= threshold ? 255 : 0);

                si += src.Channels;
                di++;
            }
        });

        return dst;
    }
    // 細線化
    public static ImageBuffer Thinning(this ImageBuffer src)
    {
        int width = src.Width;
        int height = src.Height;

        if (src.Channels != 1)
            throw new Exception("Binary image required");
        
        var dst = src.Clone();

        byte[] p = dst.Pixels;

        bool changed;

        do
        {
            changed = false;

            List<int> remove = new();

            // step 1
            for (int y = 1; y < height - 1; y++)
            {
                int row = y * width;

                for (int x = 1; x < width - 1; x++)
                {
                    int i = row + x;

                    if (p[i] == 0) continue;

                    int p2 = p[i - width] > 0 ? 1 : 0;
                    int p3 = p[i - width + 1] > 0 ? 1 : 0;
                    int p4 = p[i + 1] > 0 ? 1 : 0;
                    int p5 = p[i + width + 1] > 0 ? 1 : 0;
                    int p6 = p[i + width] > 0 ? 1 : 0;
                    int p7 = p[i + width - 1] > 0 ? 1 : 0;
                    int p8 = p[i - 1] > 0 ? 1 : 0;
                    int p9 = p[i - width - 1] > 0 ? 1 : 0;

                    int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;

                    if (B < 2 || B > 6) continue;

                    int A =
                        (p2 == 0 && p3 == 1 ? 1 : 0) +
                        (p3 == 0 && p4 == 1 ? 1 : 0) +
                        (p4 == 0 && p5 == 1 ? 1 : 0) +
                        (p5 == 0 && p6 == 1 ? 1 : 0) +
                        (p6 == 0 && p7 == 1 ? 1 : 0) +
                        (p7 == 0 && p8 == 1 ? 1 : 0) +
                        (p8 == 0 && p9 == 1 ? 1 : 0) +
                        (p9 == 0 && p2 == 1 ? 1 : 0);

                    if (A != 1) continue;

                    if (p2 * p4 * p6 != 0) continue;
                    if (p4 * p6 * p8 != 0) continue;

                    remove.Add(i);
                }
            }

            foreach (var i in remove)
            {
                p[i] = 0;
                changed = true;
            }

            remove.Clear();

            // step 2
            for (int y = 1; y < height - 1; y++)
            {
                int row = y * width;

                for (int x = 1; x < width - 1; x++)
                {
                    int i = row + x;

                    if (p[i] == 0) continue;

                    int p2 = p[i - width] > 0 ? 1 : 0;
                    int p3 = p[i - width + 1] > 0 ? 1 : 0;
                    int p4 = p[i + 1] > 0 ? 1 : 0;
                    int p5 = p[i + width + 1] > 0 ? 1 : 0;
                    int p6 = p[i + width] > 0 ? 1 : 0;
                    int p7 = p[i + width - 1] > 0 ? 1 : 0;
                    int p8 = p[i - 1] > 0 ? 1 : 0;
                    int p9 = p[i - width - 1] > 0 ? 1 : 0;

                    int B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;

                    if (B < 2 || B > 6) continue;

                    int A =
                        (p2 == 0 && p3 == 1 ? 1 : 0) +
                        (p3 == 0 && p4 == 1 ? 1 : 0) +
                        (p4 == 0 && p5 == 1 ? 1 : 0) +
                        (p5 == 0 && p6 == 1 ? 1 : 0) +
                        (p6 == 0 && p7 == 1 ? 1 : 0) +
                        (p7 == 0 && p8 == 1 ? 1 : 0) +
                        (p8 == 0 && p9 == 1 ? 1 : 0) +
                        (p9 == 0 && p2 == 1 ? 1 : 0);

                    if (A != 1) continue;

                    if (p2 * p4 * p8 != 0) continue;
                    if (p2 * p6 * p8 != 0) continue;

                    remove.Add(i);
                }
            }

            foreach (var i in remove)
            {
                p[i] = 0;
                changed = true;
            }

        } while (changed);

        return dst;
    }
    // 反転
    public static ImageBuffer Invert(this ImageBuffer src)
    {
        int width = src.Width;
        int height = src.Height;

        var dst = src.Clone();
        byte[] p = dst.Pixels;

        Parallel.For(0, height, y =>
        {
            int i = y * src.Stride;

            for (int x = 0; x < width; x++)
            {
                p[i] = (byte)(255 - p[i]);
                i++;
            }
        });

        return dst;
    }
}
/*
// 使い方

string file = @"sample.png";

BitmapSource dst = ImageHelper.Load(file)
    .ToBuffer()
    .ToBinary()
    .Invert()
    .Thinning()
    .Invert()
    .ToBitmapSource();

Image1.Source = dst;

*/
Download

コメント