0

最近、画像のトリミングとサイズ変更の問題に直面しました。たとえば、次のような画像がある場合、画像の「メイン コンテンツ」をトリミングする必要がありました: (ソース: msn.com )代替テキスト

結果は、白い余白 (左右) のない msn コンテンツを含む画像になります。

X 軸で最初と最後の色の変化を検索し、Y 軸で同じものを検索します。問題は、画像を 1 行ずつ移動するのに時間がかかることです。2000x1600px の画像の場合、CropRect => x1,y1,x2,y2 データを返すのに最大 2 秒かかります。

座標ごとにトラバーサルを作成し、見つかった最初の値で停止しようとしましたが、すべてのテストケースで機能しませんでした..返されたデータが予期したものではなく、操作の期間が似ている場合がありました..

「メインコンテンツ」を囲む長方形のトラバーサル時間と発見を削減する方法はありますか?

public static CropRect EdgeDetection(Bitmap Image, float Threshold)
        {
            CropRect cropRectangle = new CropRect();
            int lowestX = 0;
            int lowestY = 0;
            int largestX = 0;
            int largestY = 0;

            lowestX = Image.Width;
            lowestY = Image.Height;

            //find the lowest X bound;
            for (int y = 0; y < Image.Height - 1; ++y)
            {
                for (int x = 0; x < Image.Width - 1; ++x)
                {
                    Color currentColor = Image.GetPixel(x, y);
                    Color tempXcolor = Image.GetPixel(x + 1, y);
                    Color tempYColor = Image.GetPixel(x, y + 1);
                    if ((Math.Sqrt(((currentColor.R - tempXcolor.R) * (currentColor.R - tempXcolor.R)) +
                        ((currentColor.G - tempXcolor.G) * (currentColor.G - tempXcolor.G)) +
                        ((currentColor.B - tempXcolor.B) * (currentColor.B - tempXcolor.B))) > Threshold)) 
                    {
                        if (lowestX > x)
                            lowestX = x;

                        if (largestX < x)
                            largestX = x;
                    }

                    if ((Math.Sqrt(((currentColor.R - tempYColor.R) * (currentColor.R - tempYColor.R)) +
                        ((currentColor.G - tempYColor.G) * (currentColor.G - tempYColor.G)) +
                        ((currentColor.B - tempYColor.B) * (currentColor.B - tempYColor.B))) > Threshold))
                    {
                        if (lowestY > y)
                            lowestY = y;

                        if (largestY < y)
                            largestY = y;
                    }
                }                
            }

            if (lowestX < Image.Width / 4)
                cropRectangle.X = lowestX - 3 > 0 ? lowestX - 3 : 0;
            else
                cropRectangle.X = 0;

            if (lowestY < Image.Height / 4)
                cropRectangle.Y = lowestY - 3 > 0 ? lowestY - 3 : 0;
            else
                cropRectangle.Y = 0;

            cropRectangle.Width = largestX - lowestX + 8 > Image.Width ? Image.Width : largestX - lowestX + 8;
            cropRectangle.Height = largestY + 8 > Image.Height ? Image.Height - lowestY : largestY - lowestY + 8;
            return cropRectangle;
        }
    }
4

3 に答える 3

3

考えられる最適化の1つは、はるかに遅いGetPixelを介してではなく、Lockbitsを使用してカラー値に直接アクセスすることです。

LockBitsのBobPowellページは良いリファレンスです。

一方、私のテストでは、GetPixelと同等のGetPixelFastを作成し、代わりにドロップインしようとすると、Lockbitsに関連するオーバーヘッドによってアプローチが遅くなることが示されています。代わりに、すべてのピクセルアクセスが複数のヒットではなく1つのヒットで行われるようにする必要があります。すべてのピクセルをロック/ロック解除しない限り、これはコードにうまく適合します。

これが例です

BitmapData bmd = b.LockBits(new Rectangle(0, 0, b.Width, b.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, b.PixelFormat);

byte* row = (byte*)bmd.Scan0 + (y * bmd.Stride);

//                           Blue                    Green                   Red 
Color c = Color.FromArgb(row[x * pixelSize + 2], row[x * pixelSize + 1], row[x * pixelSize]);

b.UnlockBits(bmd);

注意すべきもう2つのこと:

  1. このコードはポインタを使用しているため安全ではありません
  2. このアプローチはビットマップデータ内のピクセルサイズに依存するため、bitmap.PixelFormatからpixelSizeを導出する必要があります。
于 2010-05-24T15:29:03.883 に答える
2

GetPixel is probably your main culprit (I recommend running some profiling tests to track it down), but you could restructure the algorithm like this:

  1. Scan first row (y = 0) from left-to-right and right-to-left and record the first and last edge location. It's not necessary to check all pixels, as you want the extreme edges.
  2. Scan all subsequent rows, but now we only need to search outward (from center toward edges), starting at our last known minimum edge. We want to find the extreme boundaries, so we only need to search in the region where we could find new extrema.
  3. Repeat the first two steps for the columns, establishing initial extrema and then using those extrema to iteratively bound the search.

This should greatly reduce the number of comparisons if your images are typically mostly content. The worst case is a completely blank image, for which this would probably be less efficient than the exhaustive search.

In extreme cases, image processing can also benefit from parallelism (split up the image and process it in multiple threads on a multi-core CPU), but this is quite a bit of additional work and there are other, simpler changes you still make. Threading overhead tends to limit the applicability of this technique and is mainly helpful if you expect to run this thing 'realtime', with dedicated repeated processing of incoming data (to make up for the initial setup costs).

于 2010-05-24T16:17:51.880 に答える
0

これは注文でそれを良くすることはありません...しかし、あなたがあなたの閾値を二乗するならば、あなたは非常に高価である平方根をする必要はありません。

これにより、速度が大幅に向上するはずです。

于 2010-05-24T15:28:33.683 に答える