10

画像をモノクロ(白黒、1ビット深度)で保存しようとしていますが、その方法がわかりません。

私はpngから始めて、印刷用のビットマップに変換しています(これはサーマルプリンターであり、とにかく黒のみをサポートします-さらに、カラー/グレースケールとして送信しようとすると、大きな画像の場合は非常に遅くなります)。

これまでの私のコードは、ビットマップに変換するのは非常に簡単ですが、元の色深度を保持しています。

Image image = Image.FromFile("C:\\test.png");

byte[] bitmapFileData = null;
int bitsPerPixel = 1;
int bitmapDataLength;

using (MemoryStream str = new MemoryStream())
{
    image.Save(str, ImageFormat.Bmp);
    bitmapFileData = str.ToArray();
}
4

2 に答える 2

13

フルカラー (24 ビット/ピクセル) の画像を取得し、それを 1 ビット/ピクセルの出力ビットマップに変換し、標準の RGB からグレースケールへの変換を適用してから、Floyd-Steinberg を使用してグレースケールを1 ビット/ピクセル出力。

これは決して「理想的な」実装と見なすべきではありませんが、機能することに注意してください。必要に応じて適用できる多くの改善点があります。たとえば、入力画像全体をdata配列にコピーしますが、実際には、エラー データを蓄積するためにメモリに保持する必要があるのは 2 行 (「現在」と「次」の行) だけです。それにもかかわらず、パフォーマンスは許容できるようです。

public static Bitmap ConvertTo1Bit(Bitmap input)
{
    var masks = new byte[] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
    var output = new Bitmap(input.Width, input.Height, PixelFormat.Format1bppIndexed);
    var data = new sbyte[input.Width, input.Height];
    var inputData = input.LockBits(new Rectangle(0, 0, input.Width, input.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
    try
    {
        var scanLine = inputData.Scan0;
        var line = new byte[inputData.Stride];
        for (var y = 0; y < inputData.Height; y++, scanLine += inputData.Stride)
        {
            Marshal.Copy(scanLine, line, 0, line.Length);
            for (var x = 0; x < input.Width; x++)
            {
                data[x, y] = (sbyte)(64 * (GetGreyLevel(line[x * 3 + 2], line[x * 3 + 1], line[x * 3 + 0]) - 0.5));
            }
        }
    }
    finally
    {
        input.UnlockBits(inputData);
    }
    var outputData = output.LockBits(new Rectangle(0, 0, output.Width, output.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
    try
    {
        var scanLine = outputData.Scan0;
        for (var y = 0; y < outputData.Height; y++, scanLine += outputData.Stride)
        {
            var line = new byte[outputData.Stride];
            for (var x = 0; x < input.Width; x++)
            {
                var j = data[x, y] > 0;
                if (j) line[x / 8] |= masks[x % 8];
                var error = (sbyte)(data[x, y] - (j ? 32 : -32));
                if (x < input.Width - 1) data[x + 1, y] += (sbyte)(7 * error / 16);
                if (y < input.Height - 1)
                {
                    if (x > 0) data[x - 1, y + 1] += (sbyte)(3 * error / 16);
                    data[x, y + 1] += (sbyte)(5 * error / 16);
                    if (x < input.Width - 1) data[x + 1, y + 1] += (sbyte)(1 * error / 16);
                }
            }
            Marshal.Copy(line, 0, scanLine, outputData.Stride);
        }
    }
    finally
    {
        output.UnlockBits(outputData);
    }
    return output;
}

public static double GetGreyLevel(byte r, byte g, byte b)
{
    return (r * 0.299 + g * 0.587 + b * 0.114) / 255;
}
于 2012-07-16T11:11:47.540 に答える
4

必要なのは、Floyd-SteinbergBayer order のような優れたディザリング アルゴリズムです。バイナリ化を自分で実装するか、AForge.NETなどのライブラリを使用して実行することができます (画像処理サンプルをダウンロードしてください)。二値化のドキュメントはこちらにあります

于 2012-07-13T14:47:42.207 に答える