午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

画像の2値化の誤差拡散処理の走査を左右交互

caca.zoy.org 昨日に続いて、こちらを参考にして処理の方向を左右交互にするのを試した

f:id:gogowaten:20200404160404p:plain
昨日の記事では処理の方向は偶数行、奇数行ともに右進行だったのを、今回は奇数行を左進行にしてみる、これで処理の方向は左右交互になる

左進行時の誤差拡散
f:id:gogowaten:20200404161104p:plain
右進行とは左右対称になるような方向に拡散…で合っていると思う

効果
f:id:gogowaten:20200404161709p:plain
輝度250の画像で比較
f:id:gogowaten:20200404163549p:plain
左右交互にすると波状模様が緩和される

FloydSteinberg法+左右交互走査で誤差拡散

/// <summary>
/// 誤差拡散、FloydSteinberg、PixelFormat.Gray8グレースケール画像専用
/// </summary>
/// <param name="source">元画像のピクセルの輝度値</param>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="stride">横1行分のbyte数</param>
/// <returns></returns>
private BitmapSource D1_FloydSteinberg(byte[] source, int width, int height, int stride)
{
    int count = source.Length;
    byte[] pixels = new byte[count];//変換先画像用
    double[] gosaPixels = new double[count];//誤差計算用
    Array.Copy(source, gosaPixels, count);
    int p, yp;//座標
    double gosa;//誤差(変換前 - 変換後)

    for (int y = 0; y < height; y++)
    {
        yp = y * stride;
        if (y % 2 == 0)
        {
            //偶数行は右進行
            //->->->
            //  * 7
            //3 5 1
            // ̄16 ̄
            for (int x = 0; x < width; x++)
            {
                //注目ピクセルのインデックス
                p = yp + x;
                //しきい値127.5未満なら0にする、それ以外は255にする
                SetBlackOrWhite(gosaPixels[p], pixels, p);
                //誤差拡散
                gosa = (gosaPixels[p] - pixels[p]) / 16.0;
                if (x != width - 1)
                    //右
                    gosaPixels[p + 1] += gosa * 7;
                if (y < height - 1)
                {
                    p += stride;
                    //下
                    gosaPixels[p] += gosa * 5;
                    if (x != 0)
                        //左下
                        gosaPixels[p - 1] += gosa * 3;
                    if (x != width - 1)
                        //右下
                        gosaPixels[p + 1] += gosa * 1;
                }
            }
        }
        else
        {
            //奇数行は左進行
            //<-<-<-
            //7 *
            //1 5 3
            // ̄16 ̄
            for (int x = width - 1; x >= 0; x--)
            {
                //注目ピクセルのインデックス
                p = yp + x;
                //しきい値127.5未満なら0にする、それ以外は255にする
                SetBlackOrWhite(gosaPixels[p], pixels, p);
                //誤差拡散
                gosa = (gosaPixels[p] - pixels[p]) / 16.0;
                if (x != 0)
                    //左
                    gosaPixels[p - 1] += gosa * 7;
                if (y < height - 1)
                {
                    p += stride;
                    //下
                    gosaPixels[p] += gosa * 5;
                    if (x != width - 1)
                        //右下
                        gosaPixels[p + 1] += gosa * 3;
                    if (x != 0)
                        //左下
                        gosaPixels[p - 1] += gosa * 1;
                }
            }
        }
    }
    return BitmapSource.Create(width, height, 96, 96, PixelFormats.Gray8, null, pixels, stride);
}


右進行と左進行の比較
f:id:gogowaten:20200404174105p:plain
奇数行は偶数行のコピペ改変
左進行だから横移動のカウンタxの値は画像の幅から1づつ減らす(191行目)
拡散方向が逆なので、注目ピクセルが端なのかチェックしている3つのif(199,207,210行目)の条件を逆にして、拡散対象のインデックス決定の増減計算の符号を逆にする(201,209,212行目)


他の誤差拡散法でも比較
f:id:gogowaten:20200404183500p:plain
f:id:gogowaten:20200404183608p:plain
あんまり変わらないねえ

違いが出やすい画像
f:id:gogowaten:20200404165911p:plain
Atkinsonは全く変化ないけど、それ以外は左右交互のほうがきれいになっている

普通の写真画像の場合
f:id:gogowaten:20200404170317p:plain
f:id:gogowaten:20200404171018p:plain
これは違いがわからないねえ、絶対誤差拡散感があればわかるかも

f:id:gogowaten:20200404171605p:plain
微妙なグラデーションがある画像
f:id:gogowaten:20200404172021p:plain
7,9番は左右交互のほうで波模様が出て不自然になった、これは計算を間違って書いてしまったのかなあと思って見直したけど、わかんない、こういう結果になることもあるのかも

全部同じじゃないですか!?
f:id:gogowaten:20200404183500p:plain ゴト
両津「これが元のグレースケール画像」

f:id:gogowaten:20200404185038p:plain ゴト
両津「白黒2値をFloydSteinberg法で誤差拡散したもの」
中川「へえー」

f:id:gogowaten:20200404185157p:plain ゴト
両津「FloydSteinberg法+誤差拡散処理の走査を左右交互にしたもの」

f:id:gogowaten:20200404191551p:plain ゴト
両津「JaJuNiでの誤差拡散」

f:id:gogowaten:20200404191645p:plain ゴト
両津「JaJuNi+走査を左右交互にしたもの」
中川「全部同じじゃないですか!?」
本田「ちがいますよーっ」
両津「これだからしろうとはダメだ!もっとよく見ろ!」

f:id:gogowaten:20200404190329p:plain
両津「FloydSteinbergの左右交互走査では、ここの不自然な波模様が解消されている」



今回のアプリダウンロード
ファイル名:20200404_誤差拡散法蛇行走査.zip

github.com
f:id:gogowaten:20200404223155g:plain
画像ファイルをドロップで表示
ボタンで変換
右上のCopyボタンで変換した画像をクリップボードにコピー
画像を左クリックしている間は変換前の画像を表示



関連記事

減色パレットの色で減色時に誤差拡散のテストは2週間後
gogowaten.hatenablog.com

次回のC#記事は
gogowaten.hatenablog.com

前回のWPF記事は昨日

gogowaten.hatenablog.com