Самый быстрый алгоритм заполнения перекрывающихся прямоугольников пикселей

У меня есть изображение. Значение пикселя — 0 или 1. Все пиксели изначально равны нулю.

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

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

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

Я собираюсь написать алгоритм, сначала найдя множества объединений для каждого столбца, как показано ниже:

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

Обновление, больше контекста: проблема связана с поиском ближайшего соседа в большом наборе смоделированных ураганов. Ураган — это пространственная серия, первоначально характеризующаяся последовательностью пикселей на изображении. Для каждого пикселя мы создаём больше функций, раскрашивая пиксели в одном и том же районе. Район обычно представляет собой прямоугольник или квадрат. Разработка функций выполняется несколько раз с увеличением размера прямоугольника, поэтому для каждого урагана создается несколько изображений. Затем эти изображения сглаживаются и объединяются в битовый массив в качестве окончательного вектора признаков урагана. Расстояние Жаккара затем используется для поиска ближайшего соседа. При профилировании кода узким местом является разработка функций, что побудило меня задуматься о лучшем способе заполнения этих битовых векторов.

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

Cris Luengo 02.07.2024 01:32

@CrisLuengo Просто перебирая пиксели в прямоугольниках, количество пикселей, которые будут «затронуты», в среднем в 19 раз превышает количество всех пикселей в изображении. Анализ актуален для поиска ближайшего соседа среди десятков миллионов смоделированных следов ураганов.

user2961927 02.07.2024 02:11

Звучит как вариант задачи Клее о мере, в которой вы хотите заполнить область, а не просто посчитать ее.

Luatic 02.07.2024 02:43

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

user2961927 02.07.2024 03:10

Используйте подмножество алгоритмов «развертки по строке». Он имеет линейную сложность по количеству пикселей и прямоугольникам.

minorlogic 02.07.2024 20:24
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
5
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Заполнение прямоугольника — относительно дешевая операция, и ее легко реализовать (с помощью любой библиотеки, предназначенной для обработки изображений или 2D-массивов, вы можете получить работающую реализацию буквально за считанные минуты). Если это окажется слишком медленным для вашего приложения (не делайте предположений, сначала попробуйте), вы можете реализовать что-то гораздо более сложное, которое касается каждого пикселя только один раз, следующим образом:

  1. Вычислите пересечение прямоугольников. Это многоугольник. Это нетривиальный алгоритм, для правильной реализации я бы использовал существующую реализацию. Например, в C++ есть Boost Geometry или Clipper. Последний также доступен в Delphi и C#. Мне очень нравится Клипер.

  2. Нарисуйте многоугольник, используя алгоритм заполнения многоугольника. Это также нетривиальный алгоритм, поэтому, если возможно, используйте существующую реализацию. Я нашел это объяснение алгоритма очень полезным.


В комментарии вы сказали, что это «имеет отношение к поиску ближайшего соседа среди десятков миллионов смоделированных следов урагана». Поиск ближайшего соседа лучше всего осуществлять с помощью R*-дерева. Опять же, это нетривиальный алгоритм, вам следует использовать существующую реализацию. Boost Geometry, ссылка на которую приведена выше, реализует R*-деревья.

Ответ принят как подходящий

Использование python с очень наивной формой алгоритма развертки линии:

rects = [box(x, y, x + w, y + h) for (x, y), w, h in R]

uu = unary_union(rects)
_, ymin, _, ymax = uu.bounds
xs = sorted({x for x, y in uu.exterior.coords})
gcollections = [split(uu, LineString([(x, ymin), (x, ymax)])) for x in xs[1:-1]]

hsegments = []
for i, (lg, rg) in enumerate(pairwise(gcollections)):
    lp1, rp1, lp2, rp2 = chain.from_iterable(
        map(lambda geom: sorted(
            geom, key=lambda p: p.bounds[0]
        ), [lg.geoms, rg.geoms])
    )
    hseg = rp1.intersection(lp2)
    if i == 0:
        hsegments.append(lp1)
    hsegments.append(hseg)
    if i == (len(gcollections) - 2):
        hsegments.append(rp2)

Настраивать :

from itertools import chain, pairwise

import matplotlib.pyplot as plt
from shapely import LineString, MultiLineString, Polygon, box, unary_union
from shapely.ops import split
from shapely.plotting import plot_polygon

# set of rectangles (op)
R = {
    ((60, 100), 89, 256),
    ((210, 58), 180, 212),
    ((120, 229), 209, 256),
    ((1, 143), 449, 171),
}

Другие вопросы по теме