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

コメント