3

CCLアルゴリズムの実装の最適化についてサポートが必要です。画像の黒い部分を検出するために使用します。2000x2000では、11秒かかります。これはかなりの時間です。実行時間を可能な限り最小の値に減らす必要があります。また、同じことを実行できるが、これよりも高速なアルゴリズムが他にあるかどうかを知りたいと思います。だからここに私のコードがあります:

    //The method returns a dictionary, where the key is the label
    //and the list contains all the pixels with that label
    public Dictionary<short, LinkedList<Point>> ProcessCCL()
    {
        Color backgroundColor = this.image.Palette.Entries[1];
        //Matrix to store pixels' labels
        short[,] labels = new short[this.image.Width, this.image.Height];
        //I particulary don't like how I store the label equality table
        //But I don't know how else can I store it
        //I use LinkedList to add and remove items faster
        Dictionary<short, LinkedList<short>> equalityTable = new Dictionary<short, LinkedList<short>>();
        //Current label
        short currentKey = 1;
        for (int x = 1; x < this.bitmap.Width; x++)
        {
            for (int y = 1; y < this.bitmap.Height; y++)
            {
                if (!GetPixelColor(x, y).Equals(backgroundColor))
                {
                    //Minumum label of the neighbours' labels
                    short label = Math.Min(labels[x - 1, y], labels[x, y - 1]);
                    //If there are no neighbours
                    if (label == 0)
                    {
                        //Create a new unique label
                        labels[x, y] = currentKey;
                        equalityTable.Add(currentKey, new LinkedList<short>());
                        equalityTable[currentKey].AddFirst(currentKey);
                        currentKey++;
                    }
                    else
                    {
                        labels[x, y] = label;
                        short west = labels[x - 1, y], north = labels[x, y - 1];
                        //A little trick:
                        //Because of those "ifs" the lowest label value
                        //will always be the first in the list
                        //but I'm afraid that because of them
                        //the running time also increases
                        if (!equalityTable[label].Contains(west))
                            if (west < equalityTable[label].First.Value)
                                equalityTable[label].AddFirst(west);
                        if (!equalityTable[label].Contains(north))
                            if (north < equalityTable[label].First.Value)
                                equalityTable[label].AddFirst(north);
                    }
                }
            }
        }
        //This dictionary will be returned as the result
        //I'm not proud of using dictionary here too, I guess there 
        //is a better way to store the result
        Dictionary<short, LinkedList<Point>> result = new Dictionary<short, LinkedList<Point>>();
        //I define the variable outside the loops in order 
        //to reuse the memory address
        short cellValue;
        for (int x = 0; x < this.bitmap.Width; x++)
        {
            for (int y = 0; y < this.bitmap.Height; y++)
            {
                cellValue = labels[x, y];
                //If the pixel is not a background
                if (cellValue != 0)
                {
                    //Take the minimum value from the label equality table 
                    short value = equalityTable[cellValue].First.Value;
                    //I'd like to get rid of these lines
                    if (!result.ContainsKey(value))
                        result.Add(value, new LinkedList<Point>());
                    result[value].AddLast(new Point(x, y));
                }
            }
        }
        return result;
    }

前もって感謝します!

4

3 に答える 3

1

一度に 1 つのスキャン ラインを処理し、黒いピクセルの各実行の開始と終了を追跡します。

次に、各スキャン ラインで、それを前のラインのランと比較します。前の行の実行と重ならない現在の行の実行がある場合、それは新しいブロブを表します。現在の行の実行と重複する前の行に実行がある場合、前と同じ BLOB ラベルが取得されます。etc. etc. わかります。

辞書などは使わないようにしています。私の経験では、プログラムをランダムに停止すると、プログラミングが徐々に容易になる可能性がありますが、new-ing による重大なパフォーマンス コストが発生する可能性があります。

于 2012-08-28T20:32:23.423 に答える
1

問題はGetPixelColor(x, y)に関するもので、画像データへのアクセスに非常に時間がかかります。Set/GetPixel 関数は C# では非常に遅いため、それらを頻繁に使用する必要がある場合は、代わりに Bitmap.lockBits を使用する必要があります。

private void ProcessUsingLockbits(Bitmap ProcessedBitmap)
{
    BitmapData bitmapData = ProcessedBitmap.LockBits(new Rectangle(0, 0, ProcessedBitmap.Width, ProcessedBitmap.Height), ImageLockMode.ReadWrite, ProcessedBitmap.PixelFormat);
    int BytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(ProcessedBitmap.PixelFormat) / 8;
    int ByteCount = bitmapData.Stride * ProcessedBitmap.Height;
    byte[] Pixels = new byte[ByteCount];
    IntPtr PtrFirstPixel = bitmapData.Scan0;
    Marshal.Copy(PtrFirstPixel, Pixels, 0, Pixels.Length);
    int HeightInPixels = bitmapData.Height;
    int WidthInBytes = bitmapData.Width * BytesPerPixel;
    for (int y = 0; y < HeightInPixels; y++)
    {
        int CurrentLine = y * bitmapData.Stride;
        for (int x = 0; x < WidthInBytes; x = x + BytesPerPixel)
        {
            int OldBlue = Pixels[CurrentLine + x];
            int OldGreen = Pixels[CurrentLine + x + 1];
            int OldRed = Pixels[CurrentLine + x + 2];
            // Transform blue and clip to 255:
            Pixels[CurrentLine + x] = (byte)((OldBlue + BlueMagnitudeToAdd > 255) ? 255 : OldBlue + BlueMagnitudeToAdd);
            // Transform green and clip to 255:
            Pixels[CurrentLine + x + 1] = (byte)((OldGreen + GreenMagnitudeToAdd > 255) ? 255 : OldGreen + GreenMagnitudeToAdd);
            // Transform red and clip to 255:
            Pixels[CurrentLine + x + 2] = (byte)((OldRed + RedMagnitudeToAdd > 255) ? 255 : OldRed + RedMagnitudeToAdd);
        }
    }
    // Copy modified bytes back:
    Marshal.Copy(Pixels, 0, PtrFirstPixel, Pixels.Length);
    ProcessedBitmap.UnlockBits(bitmapData);
}

ピクセル データにアクセスするための基本的なコードを次に示します。

そして、これを2Dマトリックスに変換する関数を作成しました。操作が簡単です(ただし、少し遅くなります)

    private void bitmap_to_matrix()
    {
        unsafe
        {
            bitmapData = ProcessedBitmap.LockBits(new Rectangle(0, 0, ProcessedBitmap.Width, ProcessedBitmap.Height), ImageLockMode.ReadWrite, ProcessedBitmap.PixelFormat);
            int BytesPerPixel = System.Drawing.Bitmap.GetPixelFormatSize(ProcessedBitmap.PixelFormat) / 8;
            int HeightInPixels = ProcessedBitmap.Height;
            int WidthInPixels = ProcessedBitmap.Width;
            int WidthInBytes = ProcessedBitmap.Width * BytesPerPixel;
            byte* PtrFirstPixel = (byte*)bitmapData.Scan0;

            Parallel.For(0, HeightInPixels, y =>
            {
                byte* CurrentLine = PtrFirstPixel + (y * bitmapData.Stride);

                for (int x = 0; x < WidthInBytes; x = x + BytesPerPixel)
                {
                    // Conversion in grey level                       
                    double rst = CurrentLine[x] * 0.0721 + CurrentLine[x + 1] * 0.7154 + CurrentLine[x + 2] * 0.2125;

                    // Fill the grey matix
                    TG[x / 3, y] = (int)rst;
                }
            });               
        }
    }

そしてコードが来るサイト 「High performance SystemDrawingBitmap」

本当に良い仕事をしてくれた作者に感謝します!これが役立つことを願っています!

于 2015-02-12T18:36:22.597 に答える
1

画像を複数のサブ画像に分割し、それらを並行して処理してから、結果をマージできます。1 パス: 4 つのタスク、それぞれ 1000x1000 のサブピクチャを処理 2 パス: 2 つのタスク、それぞれがパス 1 から 2 つのサブピクチャを処理 3 パス: 1 つのタスク、パス 2 の結果を処理

C# の場合は、Task Parallel Library (TPL)をお勧めします。TPLを使用すると、タスクを相互に依存して待機させることが簡単に定義できます。次のコード プロジェクトの記事では、TPL の基本的な概要を示します: The Basics of Task Parallelism via C#

于 2012-08-28T19:31:13.673 に答える