Алгоритм поиска следа
Я пытаюсь придумать алгоритм оптимизации формы многоугольника (или нескольких многоугольников), чтобы максимизировать значение, содержащееся в этой форме.
У меня есть данные с 3 столбцами:
- X: расположение на оси x
- Y: расположение на оси y
- значение: значение блока, которое может иметь положительные и отрицательные значения.
Эти данные взяты из регулярной сетки, поэтому расстояние между каждым значением x и y непротиворечиво.
Я хочу создайте ограничивающий полигон, который максимизирует содержащееся значение с добавленным условием.
- Во всех точках многоугольника должен поддерживаться минимальный радиус. Это означает, что мы либо потеряем некоторые блоки положительных значений, либо получим некоторые блоки отрицательных значений.
Текущий алгоритм, который я использую, делает следующее
- находит максимальное значение блока в качестве начальной точки (или заданное пользователем)
- находит все блоки в пределах минимального радиуса и определяет, является ли это жизнеспособной точкой, проверяя, что общее значение является положительным
- удаляет все блоки в минимальном радиусе поиска из дальнейших вычислений значений и помечает их как часть конечной формы
- перемещается в следующую точку, определяемую спиралью вокруг исходной точки. (центр всегда является точкой сетки, поэтому перемещается по deltaX или deltaY)
Похоже, что это собирает некоторые клетки, которые не нужны. Я уверен, что есть алгоритмы формы, но я не знаю. есть какие-нибудь идеи, что искать, чтобы найти помощь.
Ниже приведена картинка, которая, как мы надеемся, поможет сформулировать вопрос. Положительные клетки показаны красным цветом (отрицательные клетки не показаны). Черный контур показывает форму, в которую возвращается моя текущая рутина. Я считаю, что левая сторона должна быть привлечена больше. Минимальный радиус-100 м. нижний левый черный круг примерно такой.
Сейчас код работает в R, но я, вероятно, перейду к чему-то другому или если я смогу правильно построить алгоритм.
В ответ на неясное голосование проблема, которую я пытаюсь решить без фона или попытки решения, звучит так:
"создайте ограничивающий полигон (или полигоны) вокруг ряда точек, чтобы максимизировать содержащееся значение, сохраняя при этом минимальный радиус кривизны вдоль полигона"
Правка:
Данные
Я должен был включить некоторые данные, которые можно найти здесь.
Файл представляет собой csv. 4 колонки (X, Y, Z [не используется], значение), длина ~25k размер 800kb.
2 ответа:
Если множество решений должно быть объединением дисков заданного радиуса, я попробую жадный подход. (Я подозреваю, что проблема может быть неразрешимой-экспоненциальное время выполнения-если вы хотите получить точное решение.)
Для всех пикселов (ваших "блоков") вычислите сумму значений в диске вокруг него и возьмите тот, у которого наибольшая сумма. Отметьте этот пиксель и отрегулируйте суммы всех пикселей, которые находятся на его диске, вычтя его значение, потому что отмеченный пиксель был "израсходован". Затем сканируйте все пиксели, соприкасающиеся с ним по краю или углу, и отметьте пиксель с наибольшей суммой.
Продолжайте этот процесс, пока все суммы не станут отрицательными. Тогда сумма больше не может увеличиваться.
Для эффективной реализации вам нужно будет сохранить список граничных пикселей, то есть немаркированных пикселей, которые являются соседями отмеченного пикселя. После того как вы выбрали пиксель границы с наибольшей суммой и отметили его, вы удаляете его из списка и пересчитываете суммы для немаркированные пиксели внутри диска; вы также добавляете немаркированные пиксели, которые касаются его.
На изображении пиксели отмечены синим цветом, а границы-зеленым. Выделенные пикселы
- тот, который помечен,
Те, для которых сумма должна быть пересчитана.Вычислительное время будет пропорционально площади изображения, умноженной на площадь диска (для начального вычисления сумм), плюс площадь фигуры умножается на площадь диска (для обновления сумм), плюс общая длина последовательных периметров фигуры, пока она растет (чтобы найти наибольшую сумму). [Поскольку последние термины могут быть дорогостоящими-в порядке произведения площади фигуры на ее длину периметра-целесообразно использовать структуру данных heap, которая уменьшит сумму длин до суммы их логарифмов.]
Графический подход
Я бы подошел к этому графически. Моя интуиция говорит мне, что внутренние точки полностью находятся внутри литых кругов с минимальным радиусомr
от всех соседних точек отпечатка. Это означает, что если вы отбрасываете окружность из каждой точки контура с радиусомr
, то все точки, которые находятся внутри по крайней мере половины всех соседних окружностей, находятся внутри вашего полигона. Чтобы быть менее расплывчатым, если вы находитесь глубоко внутри полигона, то у вас естьPi*r^2
такие перекрывающиеся круги в любом пикселе. если вы находитесь на грани, что вы получили половину из них. Это легко вычислимо.Сначала мне нужен набор данных. Так как вы предоставили только jpg файл, у меня нет
vales
только сюжет. Поэтому я решаю эту проблему как двоичное изображение. Сначала мне нужно было перекрасить изображение, чтобы удалитьjpg цветовые искажения. После этого это мой ввод:Я выбираю черный фон, чтобы легко применить аддитивную математику к изображению, а также мне он нравится больше, чем белый и оставьте след красным (максимально насыщенным). Теперь алгоритм:
Создать временный образ
Он должен быть одинакового размера и очищен до черного цвета. Обрабатывайте его пиксели, как целочисленные счетчики перекрывающихся кругов.Литые круги
Для каждого красного пикселя в
source image
добавьте+1
к каждому пикселю внутри круга с минимальным радиусомr
вокруг того же пикселя, но вtemp image
. Результат примерно такой (Синие-это нижние части моегоpixelformat
):В качестве
r
я использовалr=24
, поскольку это нижний левый радиус окружности в вашем примере +/-пиксель.Выберите только внутренние пиксели
Итак, перекрасьте временное изображение. Все пиксели с
color < 0.5*pi*r^2
перекрашиваются в черный, а остальные в красный. Результат примерно такой:Выберите точки окружности полигона только
Просто перекрасьте всекрасные пиксели рядом с черными пикселями в какой-нибудь нейтральный цветсиний , а остальные вчерный . Результат:Теперь просто полигонизировать результат. Для сравнения с входным изображением вы можете объединить их оба (I
OR
их вместе):[Примечания]
Вы можете играть с минимальным радиусом или свойством area treshold для достижения различное поведение. Но я думаю, что это очень близко к вашей проблеме.
Вот некоторый исходный код C++ для этого:
//picture pic0,pic1; // pic0 - source // pic1 - output/temp int x,y,xx,yy; const int r=24; // min radius const int s=float(1.570796*float(r*r)); // half of min radius area const DWORD c_foot=0x00FF0000; // red const DWORD c_poly=0x000000FF; // blue // resize and clear temp image pic1=pic0; pic1.clear(0); // add min radius circle to temp around any footprint pixel found in input image for (y=r;y<pic1.ys-r;y++) for (x=r;x<pic1.xs-r;x++) if (pic0.p[y][x].dd==c_foot) for (yy=-r;yy<=r;yy++) for (xx=-r;xx<=r;xx++) if ((xx*xx)+(yy*yy)<=r*r) pic1.p[y+yy][x+xx].dd++; pic1.save("out0.png"); // select only pixels which are inside footprint with min radius (half of area circles are around) for (y=0;y<pic1.ys;y++) for (x=0;x<pic1.xs;x++) if (pic1.p[y][x].dd>=s) pic1.p[y][x].dd=c_foot; else pic1.p[y][x].dd=0; pic1.save("out1.png"); // slect only outside pixels pic1.growfill(c_foot,0,c_poly); for (y=0;y<pic1.ys;y++) for (x=0;x<pic1.xs;x++) if (pic1.p[y][x].dd==c_foot) pic1.p[y][x].dd=0; pic1.save("out2.png"); pic1|=pic0; // combine in and out images to compare pic1.save("out3.png");
Я использую свой собственный класс
picture
для изображений, поэтому некоторые члены:
xs,ys
размер изображения в пикселяхp[y][x].dd
- пиксель в позиции(x,y)
как32
бит целочисленного типаclear(color)
- очищает весь образresize(xs,ys)
- изменение размера изображения до нового разрешения[Edit1] у меня есть небольшая ошибка в исходный код
Я заметил, что некоторые края были слишком острыми, поэтому я проверил код и забыл добавить условие круга при заполнении, поэтому он заполнял квадраты вместо этого. Я восстановил исходный код выше. Я действительно только что добавил строку
if ((xx*xx)+(yy*yy)<=r*r)
. Результаты немного изменились, поэтому я также обновил изображения новыми результатамиЯ играл с соотношением коэффициентов внутренней области и этим:
const int s=float(0.75*1.570796*float(r*r));
Приводит к еще лучшей партии для вас. Чем он меньше, тем больше может быть многоугольник. перекрытие внешнего следа ноги. Результат: