簡體   English   中英

如何檢測圖像中的不規則邊框?

[英]How do I detect irregular borders in an image?

鑒於下面的圖像,我可以使用什么算法來檢測區域1和2(由顏色標識)是否有邊框?

http://img823.imageshack.us/img823/4477/borders.png

如果那里有一個C#示例,那將是非常棒的,但我真的只是在尋找任何示例代碼。

編輯:使用Jaro的建議,我想出了以下內容......

public class Shape
{
    private const int MAX_BORDER_DISTANCE = 15;

    public List<Point> Pixels { get; set; }

    public Shape()
    {
        Pixels = new List<Point>();
    }

    public bool SharesBorder(Shape other)
    {
        var shape1 = this;
        var shape2 = other;

        foreach (var pixel1 in shape1.Pixels)
        {
            foreach (var pixel2 in shape2.Pixels)
            {
                var xDistance = Math.Abs(pixel1.X - pixel2.X);
                var yDistance = Math.Abs(pixel1.Y - pixel2.Y);

                if (xDistance > 1 && yDistance > 1)
                {
                    if (xDistance * yDistance < MAX_BORDER_DISTANCE)
                        return true;
                }
                else
                {
                    if (xDistance < Math.Sqrt(MAX_BORDER_DISTANCE) &&
                        yDistance < Math.Sqrt(MAX_BORDER_DISTANCE))
                        return true;
                }
            }
        }

        return false;
    }

    // ...
}

單擊兩個共享邊框的形狀返回相當快,但非常距離的形狀或具有大量像素的形狀有時需要3秒以上。 我有什么選擇來優化它?

2個具有邊界的區域意味着在某個小區域內應該存在3種顏色:紅色,黑色和綠色。

因此,一個非常無效的解決方案呈現出來:使用Color pixelColor = myBitmap.GetPixel(x, y); 你可以掃描一個區域的3種顏色。 當然,該區域必須大於邊界的寬度。

當然有足夠的優化空間(比如以50像素的步長並不斷降低精度)。 由於黑色是最少使用的顏色,因此您首先會搜索黑色區域。

這應該解釋我在本主題的各種評論中所寫的內容:

namespace Phobos.Graphics
{
    public class BorderDetector
    {
        private Color region1Color = Color.FromArgb(222, 22, 46);
        private Color region2Color = Color.FromArgb(11, 189, 63);
        private Color borderColor = Color.FromArgb(11, 189, 63);

        private List<Point> region1Points = new List<Point>();
        private List<Point> region2Points = new List<Point>();
        private List<Point> borderPoints = new List<Point>();

        private Bitmap b;

        private const int precision = 10;
        private const int distanceTreshold = 25;

        public long Miliseconds1 { get; set; }
        public long Miliseconds2 { get; set; }

        public BorderDetector(Bitmap b)
        {
            if (b == null) throw new ArgumentNullException("b");

            this.b = b;
        }

        private void ScanBitmap()
        {
            Color c;

            for (int x = precision; x < this.b.Width; x += BorderDetector.precision)
            {
                for (int y = precision; y < this.b.Height; y += BorderDetector.precision)
                {
                    c = this.b.GetPixel(x, y);

                    if (c == region1Color) region1Points.Add(new Point(x, y));
                    else if (c == region2Color) region2Points.Add(new Point(x, y));
                    else if (c == borderColor) borderPoints.Add(new Point(x, y));
                }
            }
        }

        /// <summary>Returns a distance of two points (inaccurate but very fast).</summary>
        private int GetDistance(Point p1, Point p2)
        {
            return Math.Abs(p1.X - p2.X) + Math.Abs(p1.Y - p2.Y);
        }

        /// <summary>Finds the closests 2 points among the points in the 2 sets.</summary>
        private int FindClosestPoints(List<Point> r1Points, List<Point> r2Points, out Point foundR1, out Point foundR2)
        {
            int minDistance = Int32.MaxValue;
            int distance = 0;

            foundR1 = Point.Empty;
            foundR2 = Point.Empty;

            foreach (Point r1 in r1Points)
                foreach (Point r2 in r2Points)
                {
                    distance = this.GetDistance(r1, r2);

                    if (distance < minDistance)
                    {
                        foundR1 = r1;
                        foundR2 = r2;
                        minDistance = distance;
                    }
                }

            return minDistance;
        }

        public bool FindBorder()
        {
            Point r1;
            Point r2;

            Stopwatch watch = new Stopwatch();

            watch.Start();
            this.ScanBitmap();
            watch.Stop();
            this.Miliseconds1 = watch.ElapsedMilliseconds;

            watch.Start();
            int distance = this.FindClosestPoints(this.region1Points, this.region2Points, out r1, out r2);
            watch.Stop();
            this.Miliseconds2 = watch.ElapsedMilliseconds;

            this.b.SetPixel(r1.X, r1.Y, Color.Green);
            this.b.SetPixel(r2.X, r2.Y, Color.Red);

            return (distance <= BorderDetector.distanceTreshold);
        }
    }
}

這很簡單。 以這種方式搜索只需要大約2 + 4 ms (掃描並找到最近的點)。

您也可以遞歸搜索:首先是精度= 1000,然后是精度= 100,最后精度= 10,用於大圖像。 FindClosestPoints實際上會給你一個估計的矩形區域,邊界應該位於該區域(通常邊界就是這樣)。

然后你可以使用我在其他評論中描述的向量方法。

我把你的問題看作是問這兩個點是否存在於不同的地區。 它是否正確? 如果是這樣,我可能會使用洪水填充的變化。 實現起來並不困難(不要遞歸地實現它,你幾乎肯定會耗盡堆棧空間)並且它能夠看到復雜的情況,比如U形區域,它有兩個點之間的邊界,但是實際上不是不同的地區 基本上運行泛洪填充,並在您的坐標與目標坐標匹配時返回true(或者當它足夠接近您的滿意度時,根據您的使用情況返回)

[編輯]這是我為我的一個項目寫的洪水填充的一個例子 該項目是CPAL許可的,但實現非常具體到我用它的方式,所以不要擔心復制它的一部分。 並且它不使用遞歸,因此它應該能夠縮放到像素數據。

[編輯2]我誤解了這個任務。 我沒有任何示例代碼可以完全滿足您的需求,但我可以說,比較每像素像素並不是您想要做的事情。 您可以通過將每個區域划分為更大的網格(可能是25x25像素)來降低復雜性,並首先比較這些扇區,如果其中任何一個足夠接近,則在這兩個扇區內進行像素/像素比較。

[Edit2.5] [四叉樹] 3也許能夠幫到你。 我沒有很多經驗,但我知道它在2D碰撞檢測中很受歡迎,這與你在這里做的很相似。 可能值得研究。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM