プロが教える店舗&オフィスのセキュリティ対策術

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;
}

「C# Lanczos拡大縮小がうまくいき」の質問画像

A 回答 (2件)

勘ですが、エイリアシングノイズじゃないですかね。



デジタル信号処理でやりませんでしたか?

縮小するということは、もともとのサンプリング間隔Δx
よりも大きいサンプリング間隔になるってことですよね。

ということは、必然的に表現できる周波数成分がF=1/2Δx
よりも減っちゃうって事ですよね。じゃあ原画像に入って
いたものもとの周波数成分の信号はどうなってしまうのか?

エイリアシングとして折り返しノイズを生むんではないで
しょうか?それが上記の紋様の正体でないかと思います。

Lanczosも畳み込み積分の一種だと思うので、エイリアシング
を生むかどうかもLanczosの周波数特性次第かなと思いました。

正しいかどうかは置いといて、それをどう対策するかが
腕の見せ所ではないでしょうか?

(勘です。ソースコードは読んでおりません。)
    • good
    • 0
この回答へのお礼

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

お礼日時:2012/06/03 00:07

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

    • good
    • 0
この回答へのお礼

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

お礼日時:2012/06/03 00:10

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!