3

そのため、Windows RT のアプリに WriteableBitmapEx を使用しています。sobel 演算子を使用して、画像にエッジ検出を実装しようとしています。.Convolute() を使用して、x 検出と y 検出の両方のカーネルを画像に正常に適用しましたが、両方の画像を 1 つに追加するのに行き詰まりました。問題は、両方の画像のすべてのピクセルの透明度が 0 のように見えることです (つまり、ARGB の A)。問題なく両方の画像を単独で表示できますが、それらを追加すると黒い画像になります。だから私の質問は:

  • 畳み込み後にすべてのピクセルの透明度が 0 に設定されるのはなぜですか?
  • イメージを真っ黒にせずに表示できるのはなぜですか?
  • 2 つの画像を追加すると黒くなるのはなぜですか?
  • 2 つの画像を結合するより良い方法はありますか? 残念なことに、この種のピクセル追加はサポートされていないようです。しかし、ForEach は本当に遅いです...

カルリフィケーションのために、これまでの私のコードは次のとおりです。wbmpY と wbmpX の両方を表示できますが、finalbmp は真っ黒です。

    public int[,] sobelY = new int[3, 3] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };
    public int[,] sobelX = new int[3, 3] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };

    public void trim(WriteableBitmap wbmp)
    {
        var graybmp = wbmp.Clone();
        graybmp.ForEach(toGrayscale);

        var wbmpY = graybmp.Clone();
        var wbmpX = graybmp.Clone();

        wbmpY = wbmpY.Convolute(sobelY, 1, 0);
        wbmpX = wbmpX.Convolute(sobelX, 1, 0);

        var finalbmp = combineSobel(wbmpX, wbmpY);           
     }

    public WriteableBitmap combineSobel(WriteableBitmap img, WriteableBitmap img2)
    {
        int height = img.PixelHeight;
        int width = img.PixelWidth;
        WriteableBitmap result = img.Clone();
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                Color imgColor = img.GetPixel(x, y);
                Color img2Color = img2.GetPixel(x, y);
                Color newColor = Color.FromArgb(
                    Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.A, 2) + Math.Pow(img2Color.A, 2)), (byte)255),
                    Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.R, 2) + Math.Pow(img2Color.R, 2)), (byte)255),
                    Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.G, 2) + Math.Pow(img2Color.G, 2)), (byte)255),
                    Math.Min((byte)Math.Sqrt(Math.Pow(imgColor.B, 2) + Math.Pow(img2Color.B, 2)), (byte)255)
                );

                result.SetPixel(x, y, newColor);
            }
        }
        return result;
    }
4

3 に答える 3

2

畳み込みは、使用可能なすべてのチャネルに適用されます。赤、緑、青(この場合は必要なもの)だけでなく、アルファチャネルも処理されます。これにより、アルファ値はゼロ(100%透明)になります。次の例を考えてみましょう。

1 0 -1 255 255 255
2 0 -2 255 255 255
1 0 -1 255 255 255
1 * 255 0 * 255 -1 * 255 255 0 -255
2 * 255 0 * 255 -2 * 255 = 510 0 -510
1 * 255 0 * 255 -1 * 255 255 0 -255

2 * 255 + 510 + 3 * 0-2 * 255-510=0すべてのピクセル

技術的にはすべて問題ありません。アルファチャネル上のエッジは検出されません。ただし、機能的には、これはこの場合に必要なものではありません。この動作が望ましくない場合は、アルファチャネルの処理をスキップするか(ソースで許可されている場合)、後でアルファを255にリセットできます。

使用した技術の経験がないので、画面に表示される黒い画像について推測します。多くのフレームワークは、最初に画像を単色にリセットします(この場合は黒であると想定しています)。これは、透明な画像(またはその一部)を処理している場合に前の画像がにじまないようにするために必要です。複雑な(透明な)画像をこの単色に追加すると、同じ単色になります。そのため、画像はすべて黒で表示されます。

注:combineSobelはすべてのチャネルを使用しますが、以前はグレースケールに変換されていたため、色の作成を最適化することをお勧めします。

于 2013-02-17T13:34:31.670 に答える
1

2つの画像を組み合わせるより良い方法はありますか?Blit unfortunatleyは、この種のピクセル追加をサポートしていないようです。しかし、ForEachは本当に遅いです...

そのため、安全でないコードを使用する必要があります。これを確認してください。安全でないコードのブロックが安全なコードよりも遅いのはなぜですか。

速度の問題として、ピクセルごとに16個の関数(4xMath.Min、4xMath.Pow、4xMath.Sqrt)を呼び出すことを考えてください。それは大きなオーバーヘッドです。

ピクセル値は[0,255]の範囲にありますMath.Pow([0,255]、2)+ Mat.Pow([0,255]、2)の結果は[0,2 * Math.Pow(255,2)]の範囲になります=>[0,130050]次のようなルックアップテーブルを作成します。

byte[] LUT4Sqrt = new byte[130051];

for (int i = 0; i < 130051; i++)
{
    LUT4Sqrt[i] = (byte)(Math.Sqrt(i));
}

Math.Powのルックアップテーブルも作成できます。

于 2013-02-20T16:02:47.627 に答える
0

コードには2つの問題があります。

1)試してみましたが、複雑な方法では機能しません。空の画像が作成されるだけです。それはあなたのせいではないと思います。2つの空の画像を組み合わせると、空の画像が得られます。

2)結合の実装に小さな問題があります。r、g、bの場合とは異なり、sqrtcalcを使用せずに透明度を255に設定できます。

お役に立てれば

于 2013-02-17T22:23:16.037 に答える