• 締切済み

C# Lanczos拡大縮小がうまくいきません

C#でWritableBitmapをLanczos縮小するためにさまざまなwebページを参考に 下記コードを書いておりますが、 (文字数の関係で見づらく申し訳ありません) 添付画像のとおり、縮小するとキレイに縮小されず、格子状?のあるパターンで 模様が入ってしまいます。また、かすれたりしているように思われます。 ずっと考えているのですが、どこが悪いのか分かりません。 どなたかご教授ください。 private WriteableBitmap Lanzcos3Scaler(WriteableBitmap procBmp) { //処理対象データのデータを取得 int bpp = procBmp.Format.BitsPerPixel / 8; int w = procBmp.PixelWidth; int h = procBmp.PixelHeight; int stride = w * bpp; byte[] Source = new byte[w * bpp * h]; //byte配列にもとデータをコピー procBmp.CopyPixels(Source, stride, 0); //処理 int n = 3; // N値 int nx = n - 1; //スケールを定義 double scale = 0.4; //出力先を定義 int w2 = (int)(w * scale), h2 = (int)(h * scale); Int32Rect Rect = new Int32Rect(0, 0, w2, h2); WriteableBitmap resBmp = new WriteableBitmap(w2, h2, procBmp.DpiX, procBmp.DpiY, procBmp.Format, null); int bpp2 = resBmp.Format.BitsPerPixel / 8; int stride2 = w2 * bpp2; byte[] Result = new byte[w2 * bpp2 * h2]; //処理 Parallel.For(0, h2, y => { for (int x = 0; x < w2; x++) { //ソース側の対応点計算 double x0 = (x + 0.5) / scale; double y0 = (y + 0.5) / scale; //ソース側の対応インデックス計算 int xBase = (int)x0; int yBase = (int)y0; //色要素を格納する配列を作成 byte[] colors = new byte[bpp]; for (int i = 0; i < bpp; i++) colors[i] = 0; double[] color_element = new double[bpp]; for (int i = 0; i < color_element.Length; i++) color_element[i] = 0.0; double w_total = 0.0; // 周辺(a*2)^2画素を取得して処理 for (int i = -nx; i <= n; i++) { for (int j = -nx; j <= n; j++) { int xCurrent = xBase + i; int yCurrent = yBase + j; // 距離決定 double distX = ((double)xCurrent + 0.5) - x0; double distY = ((double)yCurrent + 0.5) - y0; double dist = Math.Sqrt(distX * distX + distY * distY); // 重み付け double weight = 0.0; if (dist == 0.0) { weight = 1.0; } else if (dist < (double)n) { double dPI = Math.PI * dist; //MessageBox.Show(dist.ToString()); weight = (Math.Sin(dPI) * Math.Sin(dPI / (double)n)) / (dPI * (dPI / (double)n)); } else { weight = 0.0; } //鏡像処理 if (yCurrent < 0) yCurrent *= -1; else if (yCurrent >= h) yCurrent = 2 * h - yCurrent - 1; if (xCurrent < 0) xCurrent *= -1; else if (xCurrent >= w) xCurrent = 2 * w - xCurrent - 1; // 画素取得 for (int k = 0; k < bpp; k++) { color_element[k] += ((double)Source[(yCurrent * stride) + xCurrent * bpp + k]) * weight; } w_total += weight; } } for (int l = 0; l < bpp; l++) { if (w_total != 0.0) color_element[l] /= w_total; color_element[l] = (color_element[l] > 255) ? 255 : (color_element[l] < 0) ? 0 : color_element[l]; colors[l] = (byte)(int)color_element[l]; } for (int m = 0; m < bpp; m++) { if (y < 0 || y >= h2 || x < 0 || x >= w2) break; Result[(y * stride2) + x * bpp2 + m] = colors[m]; } } }); resBmp.WritePixels(Rect, Result, stride2, 0);//WriteableBitmapに出力 return resBmp; }

みんなの回答

回答No.2

ちなみに、真っ黒画像でなく、通常の写真等を縮小した場合も気になるノイズは出ますか?

WeeknPrgrmr
質問者

お礼

ご回答ありがとうございます。 写真だと濃淡のノイズは視認できませんが、縮小がひどく汚いように感じます。 斜めの境界線が直線がつながっているような縮小画像になります。 距離計算の辺りが怪しい気がするのでコード見直してみます。

回答No.1

勘ですが、エイリアシングノイズじゃないですかね。 デジタル信号処理でやりませんでしたか? 縮小するということは、もともとのサンプリング間隔Δx よりも大きいサンプリング間隔になるってことですよね。 ということは、必然的に表現できる周波数成分がF=1/2Δx よりも減っちゃうって事ですよね。じゃあ原画像に入って いたものもとの周波数成分の信号はどうなってしまうのか? エイリアシングとして折り返しノイズを生むんではないで しょうか?それが上記の紋様の正体でないかと思います。 Lanczosも畳み込み積分の一種だと思うので、エイリアシング を生むかどうかもLanczosの周波数特性次第かなと思いました。 正しいかどうかは置いといて、それをどう対策するかが 腕の見せ所ではないでしょうか? (勘です。ソースコードは読んでおりません。)

WeeknPrgrmr
質問者

お礼

アドバイスありがとうございます。 エイリアシングも考慮してコード見直してみます。

関連するQ&A