Skip to content

Instantly share code, notes, and snippets.

@matarillo
Last active March 22, 2025 13:25
Show Gist options
  • Save matarillo/462f1c44cd37fe5d2476ec35b4c380b5 to your computer and use it in GitHub Desktop.
Save matarillo/462f1c44cd37fe5d2476ec35b4c380b5 to your computer and use it in GitHub Desktop.
using NetVips;
if (ModuleInitializer.VipsInitialized)
{
Console.WriteLine(
$"Inited libvips {NetVips.NetVips.Version(0)}.{NetVips.NetVips.Version(1)}.{NetVips.NetVips.Version(2)}");
}
else
{
Console.WriteLine(ModuleInitializer.Exception.Message);
Environment.Exit(1);
}
using var http = new HttpClient();
var data = http.GetByteArrayAsync("https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png").Result;
File.WriteAllBytes("lena.png", data);
using var image = Image.PngloadBuffer(data);
// グレースケールに変換
using var greyImageWithAlpha = image.Colourspace(Enums.Interpretation.Bw);
using var greyImage = greyImageWithAlpha.ExtractBand(0);
// 画像データをRAWフォーマットでバイト配列として取得
// libvipsの内部形式でバッファに書き出す (バンドインターリーブ形式のため、あらかじめ1バンドのみ取り出してある)
var rawData = greyImage.WriteToBuffer(".raw");
ApplyFloydSteinbergDithering(rawData, greyImage.Width, greyImage.Height);
var bwImage = Image.NewFromMemory(rawData, greyImage.Width, greyImage.Height, 1, Enums.BandFormat.Uchar);
bwImage.WriteToFile("lena.bw.png");
static void ApplyFloydSteinbergDithering(Span<byte> rawData, int width, int height)
{
// エラー拡散用の配列
var errorBuffer = new double[height, width];
// 画像を左上から右下に走査して誤差拡散処理
for (var y = 0; y < height; y++)
for (var x = 0; x < width; x++)
{
var index = y * width + x;
// 現在のピクセル値を取得(エラーバッファの値を加算)
var oldPixel = rawData[index] + errorBuffer[y, x];
oldPixel = Math.Max(0, Math.Min(255, oldPixel)); // 0-255の範囲に収める
// 二値化(閾値以上なら255、そうでなければ0)
var newPixel = (byte)(oldPixel >= 128 ? 255 : 0);
// 処理結果を配列に書き込む
rawData[index] = newPixel;
// 量子化誤差を計算
var error = oldPixel - newPixel;
// Floyd-Steinbergパターンで誤差を分散
// 7/16を右のピクセルに
if (x + 1 < width) errorBuffer[y, x + 1] += error * 7.0 / 16.0;
// 次の行の処理
if (y + 1 < height)
{
// 3/16を左下のピクセルに
if (x - 1 >= 0) errorBuffer[y + 1, x - 1] += error * 3.0 / 16.0;
// 5/16を真下のピクセルに
errorBuffer[y + 1, x] += error * 5.0 / 16.0;
// 1/16を右下のピクセルに
if (x + 1 < width) errorBuffer[y + 1, x + 1] += error * 1.0 / 16.0;
}
}
}
using SkiaSharp;
using var http = new HttpClient();
var data = http.GetByteArrayAsync("https://upload.wikimedia.org/wikipedia/en/7/7d/Lenna_%28test_image%29.png").Result;
File.WriteAllBytes("lena.png", data);
using var bitmap = SKBitmap.Decode(data);
// グレースケールカラータイプで新しいビットマップを作成
var info = new SKImageInfo(
bitmap.Width,
bitmap.Height,
SKColorType.Gray8,
SKAlphaType.Opaque
);
using var grayBitmap = new SKBitmap(info);
using (var canvas = new SKCanvas(grayBitmap))
{
canvas.DrawBitmap(bitmap, 0, 0);
}
ApplyFloydSteinbergDithering(grayBitmap.GetPixelSpan(), grayBitmap.Width, grayBitmap.Height);
using (var file = File.OpenWrite("lena.bw.png"))
{
grayBitmap.Encode(SKEncodedImageFormat.Png, 100).SaveTo(file);
}
static void ApplyFloydSteinbergDithering(Span<byte> rawData, int width, int height)
{
// エラー拡散用の配列
var errorBuffer = new double[height, width];
// 画像を左上から右下に走査して誤差拡散処理
for (var y = 0; y < height; y++)
for (var x = 0; x < width; x++)
{
var index = y * width + x;
// 現在のピクセル値を取得(エラーバッファの値を加算)
var oldPixel = rawData[index] + errorBuffer[y, x];
oldPixel = Math.Max(0, Math.Min(255, oldPixel)); // 0-255の範囲に収める
// 二値化(閾値以上なら255、そうでなければ0)
var newPixel = (byte)(oldPixel >= 128 ? 255 : 0);
// 処理結果を配列に書き込む
rawData[index] = newPixel;
// 量子化誤差を計算
var error = oldPixel - newPixel;
// Floyd-Steinbergパターンで誤差を分散
// 7/16を右のピクセルに
if (x + 1 < width) errorBuffer[y, x + 1] += error * 7.0 / 16.0;
// 次の行の処理
if (y + 1 < height)
{
// 3/16を左下のピクセルに
if (x - 1 >= 0) errorBuffer[y + 1, x - 1] += error * 3.0 / 16.0;
// 5/16を真下のピクセルに
errorBuffer[y + 1, x] += error * 5.0 / 16.0;
// 1/16を右下のピクセルに
if (x + 1 < width) errorBuffer[y + 1, x + 1] += error * 1.0 / 16.0;
}
}
}
@matarillo
Copy link
Author

libvips, Skia, ともにネイティブバイナリも必要 (ネイティブバイナリもnugetで配布されている)

win-x64におけるサイズは

  • libvips-42.dll 18,432 KB
  • libSkiaSharp.dll 9,381 KB

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment