午後わてんのブログ

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

バイキュービック法で画像の拡大してみようとしたけど、難しすぎた

バイキュービック法で画像の拡大してみようとしたけど、できたようなできていないような感じになった、難しすぎた
 
2021/04/23追記
やっぱり間違っていたので書き直した 
2021/04/23追記ここまで
 
 
前回のバイリニア法は
近くの上下左右の4つのピクセルとの距離から拡大後の色を決めていた
イメージ 1
上下左右2ピクセルの距離まで伸ばした合計16ピクセルを参照してさらに
距離に応じた重み付けは曲線になる
 
イメージ 2
これはバイリニア法、距離と重みの関係グラフ
1ピクセル先までだから距離の最大は1になる
重みは距離に反比例っていうのか?距離が離れるほど重みは下がる
下がり方は直線だからわかりやすかった
 
バイキュービックで重みを求める式は
イメージ 4
なんでこうなるのかわからないけど、こうらしい
使う式は3つあるけど実際にはA,Bの2つかなあ
距離が0以上1未満のときはAの式
距離が1以上2未満のときはBの式
 
画質を決める定数を-1にした場合で距離が0.1のときの重みは
距離が0.1だから使う式はA
(-1+2)*(0.1*0.1*0.1)-(-1+3)*(0.1*0.1)+1=0.981
とかになって
定数-1と-0.5のとき距離0から最大の2までを0.1刻みごとの重みを表にすると
イメージ 5
こうなって、これをグラフにすると
 
イメージ 3
こう、きれいな曲線になるねえ
距離0から1までの重みははだんだん小さくなって1で0になる、これが式A
1より離れるとマイナスになるのは式Bなんだよねえ
不思議だわ
 
 
 
横方向だけで計算してみる
拡大後のあるピクセルのx座標が元座標だと9.6だった場合に、参照する横範囲は
イメージ 6
左から8,9,10,11の4ピクセルになる
それぞれの距離は
1.6, 0.6, 0.4, 1.4
使う式は
B, A, A, Bになる
ピクセルの値が100, 200, 150, 50で
画質を決める定数を-1にしたとき補間される値は194
イメージ 7
 
縦横で計算してみる

f:id:gogowaten:20191212150512p:plain

こうなった
これであっているかはわかんないけど、値を見ると大きく外してはいないみたいねえ
 
 
 
 
参照範囲が元画像の外側になる場合、つまり画像の周縁部の重みはどうすればいいのか
イメージ 9
左方向に見ると近い左は0ピクセル目でいいけど、遠い左は-1ピクセル目になる
-1ピクセルってピクセルはないから値もない
右に見ても、もしもと画像の幅が1ピクセルだった場合は存在しない座標になる
 
 
 
 
イメージ 13
値が200の2x1ピクセルを10x1に拡大、値は全部200のピクセルなので拡大しても200になりそうだけど?
元画像の外側は重みを0にして計算した場合

f:id:gogowaten:20191212150522p:plain

縦幅は拡大前後で1と変化しないので無視して横方向だけで計算
左2が遠い左、左1が近い左ピクセルになる
横2ピクセルだから左2と右2は存在しないピクセルなので重み(ウェイト)を0にしている
結果の補間をグラフにすると
 
イメージ 11
画像だと
イメージ 12
グラデーションになっている
元画像の全部のピクセルの値が200なんだから、拡大後も全部200になりそうだけどバイキュービックの場合はこれで正しい?
 
なので普通にバイキュービックで拡大すると画像の周縁部は色が明るくなる
イメージ 14
これを2倍すると
イメージ 15
周縁部の1ピクセルの色が白くなっている
拡大すると
イメージ 16
 
元画像の外側は1つ内側の色を代入して重み普通に計算した場合

f:id:gogowaten:20191212150539p:plain

赤文字がさっきと違うところ
イメージ 17
こうすると全部200になって
 
イメージ 18
周縁部の色が白(明る)くならなくなった
これでいいのかなあ
 
 
 
バイリニア法と比較してみる
バイキュービックの画質のaは-1
 
イメージ 23
黒背景に白文字の画像の拡大
イメージ 24
上がバイリニア法、下がバイキュービック
イメージ 25
こういうのはバイキュービックのほうがきれいに感じる
 
 
 
 
バイリニア
イメージ 28
縮小0.5倍
バイリニア
イメージ 29
 
2x2の
□■
■□
を10倍してから最近傍補間法で8倍くらい
バイリニア
イメージ 30

f:id:gogowaten:20191212145316j:plain

サイズ1024x768、これを0.3倍

バイリニア
イメージ 31
おなじに見える…けど
日付のところはバイキュービックのほうがきれい
 
追記2018/04/27
縮小処理でほとんど差が出ないのは縮小処理が間違っていたからだった
 
 
これを2倍
イメージ 20
バイキュービックのほうがくっきりした感じになる、輪郭が強調される感じ
同時に輪郭部分のもやもやしたノイズも強調されている
 
 
画質を決める定数aを変えてみる
イメージ 21
 
aの値を小さくすると輪郭強調されるみたい、くっきりするけどノイズも強調される
今回の画像だと0がいいかなあ
-1~0がよく使われるそう
 
 
今回のもどこか計算間違っているかも
イメージ 22
拡大率を上げていくとブロック状になる
普通なら下のバイリニア法みたいにグラデーションになるはずなんだよなあ
それに計算量も増えたから処理時間もものすごく増えて
256x192をカラーで6倍にするだけでも約十秒もかかった
 
 
PixelFormat.gray8のBitmapSourceを指定したスケールと画質のaで拡大縮小したBitmapSourceを返す
       //グレースケール
        private BitmapSource F1バイキュービックグレースケール2(BitmapSource source, double xScale, double yScale, double factor)
        {
            if (source == null) { return null; }
            var wb = new WriteableBitmap(source);
            int h = wb.PixelHeight;
            int w = wb.PixelWidth;
            int stride = wb.BackBufferStride;
            var pixels = new byte[h * stride];
            wb.CopyPixels(pixels, stride, 0);

            //変換後のサイズは四捨五入
            int nH = (int)(Math.Round((h * yScale), MidpointRounding.AwayFromZero));
            int nW = (int)(Math.Round((w * xScale), MidpointRounding.AwayFromZero));
            var nWb = new WriteableBitmap(nW, nH, 96, 96, source.Format, source.Palette);
            int nStride = nWb.BackBufferStride;
            var nPixels = new byte[nH * nStride];
            long nP = 0;
            double motoX, motoY;
            double dx, dy;

            double yoko倍率 = (w - 1) / (nW - 1.0f);//変形後から見た倍率は長さで求めているから-1.0している
            double tate倍率 = (h - 1) / (nH - 1.0f);
            double[] dX = new double[4];//xの距離
            double[] dY = new double[4];
            double[] wX = new double[4];//ウェイト
            double[] wY = new double[4];
            double hokan = 0;//補間した値;

            int[,] pValues4x4 = new int[4, 4];//元画像の参照する4x4ピクセルの値
            for (int y = 0; y < nH; ++y)
            {
                motoY = y * tate倍率;
                dy = motoY % 1;
                //4x4のyの距離
                dY[0] = 1 + dy; dY[1] = dy; dY[2] = 1 - dy; dY[3] = 2 - dy;
                //4x4のウェイト取得
                for (int i = 0; i < 4; ++i)
                {
                    wY[i] = GetWeight(dY[i], factor);
                }
                for (int x = 0; x < nW; ++x)
                {
                    motoX = x * yoko倍率;//注目座標の元画像での座標
                    dx = motoX % 1;
                    //4x4のxの距離
                    dX[0] = 1 + dx; dX[1] = dx; dX[2] = 1 - dx; dX[3] = 2 - dx;

                    for (int i = 0; i < 4; ++i)
                    {
                        wX[i] = GetWeight(dX[i], factor);
                    }
                    //元画像の4x4の部分の値取得
                    pValues4x4 = GetPixesValue(motoX, motoY, pixels, stride, w, h);
                    //ウェイトと値をかけた値の合計
                    hokan = 0;
                    for (int i = 0; i < 4; ++i)
                    {
                        for (int j = 0; j < 4; ++j)
                        {
                            hokan += pValues4x4[j, i] * wX[j] * wY[i];
                        }
                    }
                    //合計が0から255以外なら切り捨てて収める
                    hokan = hokan > 255 ? 255 : hokan < 0 ? 0 : hokan;
                    //四捨五入
                    hokan = Math.Round(hokan, MidpointRounding.AwayFromZero);
                    //変形後のpixelsに入れる
                    nP = y * nStride + (x * 1);
                    nPixels[nP] = (byte)hokan;
                }
            }
            nWb.WritePixels(new Int32Rect(0, 0, nW, nH), nPixels, nStride, 0);
            return nWb;
        }
どこか足りないのか逆に余計なことをしているのかわからん
 
こんなこと書くよりも
WPFで画像をきれいに拡大縮小するならRenderTransformのScaleTransformを使って
表示方法を

f:id:gogowaten:20191212150705p:plain

このRenderOptionsクラスのSetBitmapScalingModeメソッドで
BitmapScalingModeをFantかHighQualityを指定するほうがいろいろ速い
 

f:id:gogowaten:20191212150718p:plain

これがHighQualityを指定して3倍で表示しているところ、きれいに拡大されている
 
 
コード
失敗?アプリ
 
関連記事
次回2018/04/27、1週間後
Lanczos法で画像の拡大してみた ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15479973.html
 
2018/4/17、前回
バイリニア法で画像の拡大縮小 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15464617.html
2018/04/16、前々回
最近傍補間法で画像の拡大縮小試してみた ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15462921.html