Last active
March 22, 2025 13:25
-
-
Save matarillo/462f1c44cd37fe5d2476ec35b4c380b5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
libvips, Skia, ともにネイティブバイナリも必要 (ネイティブバイナリもnugetで配布されている)
win-x64におけるサイズは