Алгоритм для нахождения нарисованного региона на холсте

Обновление: Я пытаюсь вытащить немного помехи из этого сообщения и суммировать его более кратко. Посмотрите исходное редактирование в случае необходимости.

Я в настоящее время пытаюсь проследить серию единственных цветных блобов на Растровом холсте.

например, пример битового массива, который я пытаюсь проследить, был бы похож на следующее: сопроводительный текст http://www.refuctored.com/polygons.bmp

После успешной трассировки основ этих 3 блобов на изображении у меня был бы класс, который содержал цвет блоба, связанного со списком точки, представляющим схему блоба (не все пиксели в блобах).

Проблемой, с которой я сталкиваюсь, является логика в экземплярах, где соседний пиксель не имеет никаких окружающих пикселей кроме предыдущего пикселя.

например, главный пример проследил бы прекрасный, но второе перестанет работать, потому что пиксель имеет не, куда пойти, так как предыдущие пиксели уже использовались.

сопроводительный текст http://www.refuctored.com/error.jpg

Я прослеживаю слева направо, способствующие диагональные углы от начала до конца по прямым углам. Я должен смочь перерисовать точную копию региона, базирующегося от данных, которые я извлекаю, таким образом, пиксели в списке должны быть в правильном порядке для копии для работы.

К настоящему времени моя попытка была пронизана отказом и парой дней получения по запросу моих волос, пытаясь переписать алгоритмы, немного отличающиеся каждый раз для решения проблемы. К настоящему времени я был неудачен. У кого-либо еще была подобная проблема как моя, у кого есть хороший алгоритм для нахождения краев?

6
задан George Johnston 25 June 2010 в 17:53
поделиться

5 ответов

Один простой трюк, чтобы избежать этих тупиков, заключается в удвоении размера изображения, которое вы хотите отследить, используя алгоритм масштабирования ближайшего соседа, прежде чем отслеживать его. Таким образом, вы никогда не получите одиночные полоски.

Альтернативой является использование алгоритма маршевых квадратов - но, похоже, у него все еще есть один или два случая, когда он терпит неудачу: http://www.sakri.net/blog/2009/05/28/detecting-edge-pixels-with-marching-squares-algorithm/

2
ответ дан 17 December 2019 в 18:09
поделиться

Вместо того чтобы использовать рекурсию, используйте стек.

Псевдокод:

Add initial pixel to polygon
Add initial pixel to stack
while(stack is not empty) {
    pop pixel off the stack
    foreach (neighbor n of popped pixel) {
        if (n is close enough in color to initial pixel) {
            Add n to polygon
            Add n to stack
        }
    }
}

Это будет использовать гораздо меньше памяти, чем то же решение с использованием рекурсии.

0
ответ дан 17 December 2019 в 18:09
поделиться

Рассматривали ли вы алгоритмы обнаружения блобов? Например, http://opencv.willowgarage.com/wiki/cvBlobsLib если вы можете интегрировать OpenCV в свое приложение. В сочетании с пороговой фильтрацией для создания бинарных изображений для каждого цвета (или цветового диапазона) в вашем изображении, вы могли бы легко найти сгустки, которые имеют одинаковый цвет. Повторите процедуру для каждого цвета в изображении, и вы получите список пятен, отсортированных по цвету.

Если вы не можете использовать OpenCV напрямую, возможно, статья, на которую ссылается эта библиотека ("A linear-time component labeling algorithm using contour tracing technique", F.Chang et al.), предоставит хороший метод для поиска пятен.

1
ответ дан 17 December 2019 в 18:09
поделиться

Просто отправьте ваше 'Image' в функцию BuildPixelArray, а затем вызовите FindRegions. После этого переменная 'colors' будет содержать список ваших цветов и координаты пикселей в каждом члене списка.

Я скопировал исходник из одного из моих проектов, здесь могут быть некоторые неопределенные переменные или синтаксические ошибки.

    public class ImageProcessing{
    private int[,] pixelArray;
    private int imageWidth;
    private int imageHeight;
    List<MyColor> colors;

    public void BuildPixelArray(ref Image myImage)
    {
        imageHeight = myImage.Height;
        imageWidth = myImage.Width;
        pixelArray = new int[imageWidth, imageHeight];
        Rectangle rect = new Rectangle(0, 0, myImage.Width, myImage.Height);
        Bitmap temp = new Bitmap(myImage);
        BitmapData bmpData = temp.LockBits(rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
        int remain = bmpData.Stride - bmpData.Width * 3;
        unsafe
        {
            byte* ptr = (byte*)bmpData.Scan0;
            for (int j = 15; j < bmpData.Height; j++)
            {
                for (int i = 0; i < bmpData.Width; i++)
                {
                    pixelArray[i, j] = ptr[0] + ptr[1] * 256 + ptr[2] * 256 * 256;
                    ptr += 3;
                }
                ptr += remain;
            }
        }
        temp.UnlockBits(bmpData);
    }

    public void FindRegions()
    {
        colors = new List<MyColor>();

        for (int i = 0; i < imageWidth; i++)
        {
            for (int j = 0; j < imageHeight; j++)
            {
                int tmpColorValue = pixelArray[i, j];
                MyColor tmp = new MyColor(tmpColorValue);
                if (colors.Contains(tmp))
                {
                    MyColor tmpColor = (from p in colors
                                        where p.colorValue == tmpColorValue
                                        select p).First();

                    tmpColor.pointList.Add(new MyPoint(i, j));
                }
                else
                {
                    tmp.pointList.Add(new MyPoint(i, j));
                    colors.Add(tmp);
                }
            }
        }
    }
}

public class MyColor : IEquatable<MyColor>
{
    public int colorValue { get; set; }
    public List<MyPoint> pointList = new List<MyPoint>();
    public MyColor(int _colorValue)
    {
        colorValue = _colorValue;
    }
    public bool Equals(MyColor other)
    {
        if (this.colorValue == other.colorValue)
        {
            return true;
        }
        return false;
    }
}
public class MyPoint
{
    public int xCoord { get; set; }
    public int yCoord { get; set; }

    public MyPoint(int _xCoord, int _yCoord)
    {
        xCoord = _xCoord;
        yCoord = _yCoord;
    }
}
0
ответ дан 17 December 2019 в 18:09
поделиться

Если вы получаете переполнение стека, я бы предположил, что вы не исключаете уже проверенные пиксели. При посещении площади в первую очередь следует проверить, бывали ли вы здесь раньше.

Кроме того, я не так давно работал над связанной проблемой, и я придумал другой подход, который использует намного меньше памяти:

Очередь:

AddPointToQueue(x, y);
repeat
   x, y = HeadItem;
   AddMaybe(x - 1, y); x + 1, y; x, y - 1; x, y + 1;
until QueueIsEmpty;

AddMaybe(x, y):
if Visited[x, y] return;
Visited[x, y] = true;
AddPointToQueue(x, y);

Суть этого подхода в том, что вы в конечном итоге ваша очередь в основном содержит линию, обернутую вокруг отображаемой области. Это ограничивает использование памяти лучше, чем стек.

Если необходимо, его также можно тривиально изменить, чтобы получить расстояние перемещения до любого квадрата.

0
ответ дан 17 December 2019 в 18:09
поделиться
Другие вопросы по тегам:

Похожие вопросы: