午後わてんのブログ

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

パレットを使った減色で誤差拡散

減色パレットで誤差拡散

ダウンロード先
イメージ 2
できた
 
 
いつもの画像を
4色減色で
イメージ 3
誤差拡散無し 誤差拡散あり
誤差拡散はFloydSteinberg式
 
8色
イメージ 4
倍の色数になったけど大差ない見た目
 
8色
最多ピクセル数を分割Cubeに選択して
グラデーションが得意なパレット
イメージ 5
誤差拡散無しだと空の不自然な縞模様が増えただけ
誤差拡散ありだと8色でもかなりきれいになる
誤差拡散素晴らしい
 
 
極端色パレットで4色
イメージ 6
Cubeからの色選択にRGB中心から遠い隅でパレットを作成すると
極端な色のパレットができる
赤、白、黒、青
ピンク、白、黒、水色
イメージ 7
いいねえ
 
 
256色
イメージ 8
上段が普通パレットの誤差拡散の有無
下段はグラデーションが得意なパレットでの誤差拡散の有無
256色まで増やすと誤差拡散なくてもパレットのできできれいになるねえ
 
 
元画像
 
普通パレットで4色
イメージ 9
これはあんまり変わんないねえ
 
極端色パレット4色
どちらも白、黒、赤、黄色の4色
イメージ 10
いいねえ、こういうのを望んでいた
極端色パレットは誤差拡散を使えば
かなりいい結果を得られるんじゃないかなあって思っていたのよね
期待どおりで嬉しい
 
別の極端色パレット
イメージ 12
「Cubeから色選択」をCube中心から遠い色で
できたパレット
イメージ 11
誤差拡散なしだと元画像からかけ離れたもの色合いになるけど
誤差拡散を使うと同じ色を使っているとは思えない画像になった
これはここまでは予想していなかった
こうなるんだなあ
 
 
5色
イメージ 14
極端色パレットはこうなって
結果
イメージ 13
右下いいねえ、たった5色なのに元画像に近い
パレットに緑はないけど黒、水色、黄色の配置で緑色に見える
 
 
16色
イメージ 15
色数が増えてくると普通パレットとの差がなくなってくる
 
 
 
 
イメージ 1
4色
イメージ 16
左上が普通パレット、それ以外は極端色パレット
葉っぱの緑だけでいったら左下がいいなあ
 
 
 
イメージ 17
普通パレットは当たり外れがないけど地味な色合いになる
選ぶとしたら右上もいいけど右下かなあ
 
16色
イメージ 18
これは普通パレットがいいかなあ
極端色パレットは色数が極端に少ない時用かな
 
 
もともと色数が少ない画像
 
イメージ 19
4色
トマトだけで言ったら赤に2色使っている左下
全体だと右下かな
 
16色
イメージ 20
やっぱり普通パレットは安定している
分割Cube選択に最大体積もいい
 
グラデーション
イメージ 21
 
イメージ 22
4色
 
256色
イメージ 23
パレットの差は殆ど出なかったので普通パレットだけ
右が誤差拡散
 
 
誤差拡散は使ったほうがきれいになるけど処理に時間がかかる
256色だと上のような小さな画像でも8秒くらいかかった!
他のアプリだと一瞬で終わるのもあるんだよねえ
VieasとかCOLGA、JTrimがそれ
一体どんな処理をしているんだろう
誤差拡散は進行方向に誤差を拡散していくから
Parallelクラスを使った並列処理はできないと思うんだよねえ
そもそも僕の場合は誤差拡散なしでも1秒以上かかっている
 
 
 
誤差拡散の処理
//BitmapSourceをList<color>の色に変換、PixelFormatはPbgra32限定
private BitmapSource ReduceColor指定色で減色誤差拡散B1(BitmapSource source, List<Color> palette)
{
    if (OriginBitmap == null) { return source; }
    if (palette.Count == 0) { return source; }
    var wb = new WriteableBitmap(source);
    int h = wb.PixelHeight;
    int w = wb.PixelWidth;
    int stride = wb.BackBufferStride;
    byte[] pixels = new byte[h * stride];
    wb.CopyPixels(pixels, stride, 0);
    double[] iPixels = new double[h * stride];
    pixels.CopyTo(iPixels, 0);
    long pp = 0;
    Color pColor = Colors.Black;
    double gosa = 0;
    double addGosa = 0;
    double min = double.MaxValue;
    double distance = 0;
    byte[] BGR = new byte[3];
    for (int p = 0; p < pixels.Length; p += 4)
    {
        min = double.MaxValue;
        //パレットから一番近い色を選ぶ
        for (int i = 0; i < palette.Count; ++i)
        {
            distance = GetColorDistance(iPixels[p + 2], iPixels[p + 1], iPixels[p], palette[i]);
            if (min > distance)
            {
                min = distance;
                pColor = palette[i];
            }
        }

        BGR[0] = pColor.B;
        BGR[1] = pColor.G;
        BGR[2] = pColor.R;

        for (int c = 0; c < 3; ++c)//B,G,Rの順
        {
            gosa = (iPixels[p + c] - BGR[c]) / 16f;//誤差を蓄積した元の色-パレットの色                    
            if ((p + 4) % stride != 0)//右端じゃなければ
            {
                pp = p + 4 + c;
                addGosa = iPixels[pp] + (gosa * 7f);
                iPixels[pp] = (addGosa < 0) ? 0 : (addGosa > 255) ? 255 : addGosa;
            }
            if (p < stride * (h - 1))//一番下の行じゃなければ
            {
                pp = stride + p + c;
                addGosa = iPixels[pp] + (gosa * 5f);
                iPixels[pp] = (addGosa < 0) ? 0 : (addGosa > 255) ? 255 : addGosa;//真下へ拡散
                if (p % stride != 0)//左端じゃなければ
                {
                    pp = stride + p - 4 + c;
                    addGosa = iPixels[pp] + (gosa * 3f);
                    iPixels[pp] = (addGosa < 0) ? 0 : (addGosa > 255) ? 255 : addGosa;//左下へ拡散
                }
                if ((p + 4) % stride != 0)
                {
                    pp = stride + p + 4 + c;
                    addGosa = iPixels[pp] + (gosa * 1f);
                    iPixels[pp] = (addGosa < 0) ? 0 : (addGosa > 255) ? 255 : addGosa;//右下へ拡散
                }
            }
        }
        pixels[p + 2] = pColor.R;
        pixels[p + 1] = pColor.G;
        pixels[p] = pColor.B;
        pixels[p + 3] = 255;//アルファ値は255に固定

    }

    wb.WritePixels(new Int32Rect(0, 0, w, h), pixels, stride, 0);
    return OptimisationPixelFormat(wb, palette.Count);//色数に合わせたPixelFormatに変換して返す
}
 
 
 
 

f:id:gogowaten:20191212133427j:plain

138802色の元の画像
 

f:id:gogowaten:20191212133444p:plain

8色
 

f:id:gogowaten:20191212133456p:plain

4色
上の3つの画像は縮小表示されているので
実際の画像よりたくさんの色が使われている
画像を右クリックで新しいタブで開くで等倍、等色表示
 
縮小表示されている上の画像は記事の編集中にはこう見えている
イメージ 27
ジャリジャリで記事編集中は不安になるw
 
処理速度は遅いけどパレットを使った減色での誤差拡散できた
極端色パレット+誤差拡散は期待通りの結果で満足
 
 
コード全部(GitHub)
 
 
アプリダウンロード(ヤフーボックス)
20180325_メディアンカット、誤差拡散.zipがそれ
 
 
関連記事

2018/04/10、カラーピッカー追加したのは2週間後
Owner指定するタイミング、別WindowとMainWindowとの連携、カラーピッカーを追加してパレットの色変更 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15454469.html
 
 
前回、2018/03/24は3日前
分割する場所の選択、メディアンカットで減色パレット作成 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15427320.html

 
 
2018/03/9、今回の記事の誤差拡散はこのときのが元になっている
指定色で減色+誤差拡散、減色結果を他のアプリと比較してみた ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15405037.html