#algorithm #line
#алгоритм #линия
Вопрос:
я где-то видел эту проблему с пересечениями линий и пытался ее решить.
Существует сетка размером 64×64 с 8 битными пикселями, на которой есть вертикальные и горизонтальные линии шириной 1 пиксель разных цветов. Все параллельные линии имеют по крайней мере один пробел между ними. Задача состоит в том, чтобы найти количество пересечений линий, которые совершает каждый набор цветных линий (например, все пересечения зеленых линий учитываются в одной сумме). И найдите, какой набор линий имел наименьшее количество пересечений. Кроме того, все вертикальные линии одного цвета имеют одинаковый размер, а все горизонтальные линии одного цвета имеют одинаковый размер.
У меня было несколько идей, но все они кажутся довольно неэффективными. Для этого потребуется просмотреть каждый пиксель в сетке, если вы столкнетесь с цветом, определите, является ли это вертикальной или горизонтальной линией, а затем двигайтесь в направлении линии, все время проверяя смежные стороны на наличие разных цветов.
Я пытаюсь решить, ускорит ли процесс первый подсчет длины горизонтальных и вертикальных линий для каждого цвета. У вас, ребята, есть какие-нибудь блестящие идеи о том, как это сделать?
Вот два примера. Обратите внимание, что между параллельными линиями всегда есть пробел.
Комментарии:
1. не уверен, что я понимаю, но мне кажется, что для каждого цвета вам нужно количество горизонтальных линий, умноженное на количество вертикальных линий. а затем отсортируйте это, чтобы увидеть, какой цвет имеет наибольшее значение? итак, проблема в том, как подсчитать количество / тип линий для каждого цвета путем проверки пикселей изображения?
2. Что я хочу, так это количество раз, когда линия пересекается любым цветом, отличным от его собственного. Вы делаете это для каждой линии, а затем суммируете все пересечения для каждого цвета. Кроме того, поскольку это неоднозначно, потому что линия может быть внизу или нет, если линии пересекаются на T (или боковом T) пересечении, это пересечение не учитывается. Например, общее количество пересечений для грина на первом рисунке равно 7.
3. а, ладно. стоило бы включить это в вопрос, спасибо.
Ответ №1:
На сетке с вашими линиями найдите 2 типа квадратов 3×3:
1).a. 2).a.
bab bbb
.a. .a.
«.» представляет фон (всегда черный?), «a» и «b» представляют 2 разных цвета (также отличающихся от цвета фона). Если найдено, увеличьте на 1 количество пересечений линий a-цвета и пересечений линий b-цвета.
Комментарии:
1. Это звучит как победитель. А как насчет T пересечений? Не будут ли они пренебрегать? Кроме того, по краям вы не можете сравнить весь квадрат, потому что вы бы проиндексировали. Я начну работать над этим.
2. @BobBusby: если вы проверите, что все 9 ячеек точно образуют один из двух приведенных выше шаблонов, вы проигнорируете t-пересечения. Разве это не то, что вы хотели, вы упомянули, что была двусмысленность? Если вы хотите, вы можете точно так же посчитать t-пересечения, вам нужно будет найти еще 8 шаблонов, используя этот алгоритм.
3. Боб Басби написал в комментарии: «поскольку это неоднозначно, потому что линия может быть внизу или нет, если линии пересекаются на T (или боковом T) пересечении, это пересечение не учитывается». Если мы примем это за то, что T пересечений не считаются пересечениями, то метод @Alex завершен. Однако неверно, что все T неоднозначны. Например, во втором примере правые концы двух горизонтальных ярко-желтых линий T с красной линией. Можно однозначно сделать вывод, что под красной линией находятся желтые квадраты, потому что верхняя желтая строка заканчивается столбцом, содержащим красную строку.
4. Я никогда не говорил, что параллельные линии одного цвета должны быть одинаковой длины, но это так. Это добавляет сложности. Итак, я предполагаю, что это означает, что длина каждой строки должна сравниваться при прохождении линии.
5. Я согласен, что вы не сказали, что «параллельные линии одного цвета должны быть одинаковой длины», но во 2-м абзаце постановки задачи вы написали «все вертикальные линии одного цвета имеют одинаковый размер, а все горизонтальные линии одного цвета имеют одинаковый размер», что подразумевает, чтопараллельные линии одного цвета должны быть одинаковой длины.
Ответ №2:
Сканирование пиксельной сетки на самом деле очень быстрое и эффективное. Это стандарт для систем компьютерного зрения, чтобы делать такие вещи. Многие из этих сканирований выдают отфильтрованные версии изображений, которые подчеркивают типы деталей, которые ищут.
Основным техническим термином является «свертка» (см. http://en.wikipedia.org/wiki/Convolution ). Я думаю об этом как о своего рода взвешенной скользящей средней, хотя веса могут быть отрицательными. Анимации в Википедии показывают свертку с использованием довольно скучного импульса (он мог бы иметь более интересную форму) и на одномерном сигнале (например, звуковом), а не на 2D (изображении), но это очень общая концепция.
Заманчиво думать, что можно сделать что-то более эффективное, избегая вычисления этой отфильтрованной версии заранее, но обычный эффект этого заключается в том, что, поскольку вы предварительно не рассчитали все эти пиксели, вы в конечном итоге вычисляете каждый пиксель несколько раз. Другими словами, думайте о отфильтрованном изображении как об оптимизации на основе справочной таблицы или своего рода динамическом программировании.
Вот некоторые конкретные причины, по которым это происходит быстро…
- Он очень удобен для кэширования, поэтому эффективно обращается к памяти.
- Это именно то, для чего предназначены векторные инструкции (MMX, SIMD и т. Д.).
- В наши дни вы даже можете переложить работу на свою видеокарту.
Еще одним преимуществом является то, что вам нужно написать только одну функцию свертки для фильтрации изображений. Специфичная для приложения часть заключается в том, как вы определяете ядро фильтра, которое представляет собой просто сетку значений веса. На самом деле вам, вероятно, вообще не стоит писать эту функцию самостоятельно — различные библиотеки числового кода могут предоставить высокооптимизированные версии.
Для обработки разных цветов линий я бы просто сначала сгенерировал одно изображение в оттенках серого для каждого цвета, затем отфильтровал и проверил каждое отдельно. Опять же, думайте об этом как об оптимизации — попытка избежать создания отдельных изображений, скорее всего, приведет к увеличению объема работы, а не к уменьшению.
- Если подумать, я, возможно, понял это требование. Возможно, имеет смысл сгенерировать черно-белое изображение из черно-цветного, отфильтровать его и найти все пересечения по нему. Когда у вас будут точки пересечения, вернитесь к исходному черно-цветному изображению, чтобы классифицировать их для подсчета. Фильтрация и поиск пересечений остаются очень эффективными для каждого пикселя. Классификация не так эффективна для каждого пикселя, но выполняется только в нескольких точках.
Вы могли бы выполнить свертку, основанную на следующем ядре фильтра FIR…
.*.
***
.*.
Точки — это нули (пиксель не имеет значения) или, возможно, отрицательные значения (предпочитают черный цвет). Звездочки — это положительные значения (предпочтительнее белые). То есть, ищите кресты три на три на пересечениях.
Затем вы просматриваете отфильтрованный результат в поисках пикселей в оттенках серого, которые ярче некоторого порога — наилучшего соответствия вашему шаблону. Если вам действительно нужны только точные точки пересечения, вы можете принять только идеально подходящий цвет, но вы можете захотеть сделать поправку на соединения в стиле T и т. Д.
Комментарии:
1. Спасибо за помощь! Я недостаточно продвинут, чтобы делать что-то настолько причудливое, но такие люди, как вы, заставляют меня хотеть стать лучше. Единственная свертка, которую я когда-либо видел, является одномерной, поэтому я не уверен, как применить ее к матрице.
Ответ №3:
Ваш единственный ввод — сетка 64×64? Если это так, то вы смотрите на что-то с размером 64×64, поскольку другого способа убедиться, что вы обнаружили все линии, нет. Итак, я предполагаю, что вы говорите об оптимизации на операционном уровне, а не асимптотически. Кажется, я помню, что в старой серии «Graphics Gems» было много подобных примеров, с акцентом на сокращение количества команд. Я сам лучше разбираюсь в асимптотических вопросах, но вот несколько небольших идей:
Ячейки сетки обладают тем свойством, что сетка [i, j] является зеленым пересечением, если
(grid[i,j] == green)
amp;amp;
(grid[i 1,j]==green || grid[i-1,j]==green)
amp;amp;
(grid[i,j 1]==green || grid[i, j-1]==green)
Таким образом, вы можете просто сканировать массив один раз, не беспокоясь о явном обнаружении горизонтальных и вертикальных линий… просто пройдите через него, используя это соотношение, и подсчитывайте пересечения по мере их нахождения. Так что, по крайней мере, вы просто используете один цикл 64×64 с довольно простой логикой.
Поскольку никакие две параллельные линии не являются непосредственно смежными, вы знаете, что можете увеличить счетчик внутреннего цикла на 2, когда проходите заполненную ячейку. Это сэкономит вам немного работы.
В зависимости от вашей архитектуры у вас может быть быстрый способ и вся сетка со смещенными копиями самой себя, что было бы отличным способом вычисления приведенной выше формулы пересечения. Но тогда вам все равно придется перебирать все подряд, чтобы найти оставшиеся заполненные ячейки (которые являются точками пересечения).
Даже если у вас нет чего-то вроде графического процессора, который позволяет вам И всей сетке, вы можете использовать эту идею, если количество цветов меньше половины вашего размера слова. Например, если у вас 8 цветов и 64-разрядная машина, то вы можете втиснуть 8 пикселей в одно целое число без знака. Итак, теперь вы можете выполнять операцию сравнения внешнего цикла для 8 ячеек сетки одновременно. Это может сэкономить вам столько работы, что стоит сделать два прохода, один для горизонтального внешнего цикла и один для вертикального внешнего цикла.
Комментарии:
1. Я думаю, мое объяснение было плохим. Я ищу пересечения линий одного цвета со всеми другими цветами, кроме самого себя. Также они не должны пересекаться со всех сторон. Это может быть просто пересечение одной линии с другой. Спасибо за совет, хотя!
2. Это не проверяет наличие пересечений со всех сторон — посмотрите на редакторы еще раз. Чтобы адаптировать его для пересечений с не-зелеными линиями, вы можете настроить его так, чтобы сказать: либо влево, либо вправо не пусто, либо сверху, либо снизу не пусто, и либо влево, либо вправо, либо сверху, либо вниз не зеленый.
Ответ №4:
Простой способ найти прямое пересечение
def straight_intersection(straight1, straight2):
p1x = straight1[0][0]
p1y = straight1[0][1]
p2x = straight1[1][0]
p2y = straight1[1][1]
p3x = straight2[0][0]
p3y = straight2[0][1]
p4x = straight2[1][0]
p4y = straight2[1][1]
x = p1y * p2x * p3x - p1y * p2x * p4x - p1x * p2y * p4x p1x * p2y * p3x - p2x * p3x * p4y p2x * p3y * p4x p1x * p3x * p4y - p1x * p3y * p4x
x = x / (p2x * p3y - p2x * p4y - p1x * p3y p1x * p4y p4x * p2y - p4x * p1y - p3x * p2y p3x * p1y)
y = ((p2y - p1y) * x p1y * p2x - p1x * p2y) / (p2x - p1x)
return (x, y)