- Вопрос или проблема
- Ответ или решение
- Шаг 1: Импорт необходимых библиотек
- Шаг 2: Определение функций для вычисления площади и проверки нахождения точки внутри треугольника
- Шаг 3: Получение изображения и определение bounding box треугольника
- Шаг 4: Цикл по пикселям внутри bounding box
- Шаг 5: Вывод результата
- Заключение
Вопрос или проблема
Предположим, у меня есть изображение в формате RGB I. Я хочу получить средний цвет нескольких случайных треугольников с вершинами A, B и C. Вершины A, B и C гарантированно находятся внутри границ изображения I, но не гарантированно имеют целочисленные значения. Как эффективно получить средний цвет этой области?
Простой способ получить близкое значение — это получить ограничивающий прямоугольник треугольника, затем пройтись по каждой строке, сохраняя текущий подсчет цветных пикселей и общие суммы для каждого канала, а затем разделить в конце. (Это также имеет преимущество в том, что позволяет заранее вычислить суммы отдельных строк, что полезно, если нужно рассчитать очень большое количество треугольников T.) Однако это наиболее точно для очень больших треугольников; чем меньше треугольники и чем меньше угол в треугольнике, тем больше проблем мы сталкиваемся с пикселями, которые лишь частично находятся внутри треугольника, что приводит к недоучету или перерасчету.
Менее простой способ заключается в том, чтобы взять тот же ограничивающий прямоугольник; затем для каждого пикселя в ограничивающем прямоугольнике вычислить площадь каждого пикселя, который также содержится в треугольнике; после этого добавить площадь пикселя и средний цвет, умноженный на площадь пикселя, к текущему итогу. Однако это непрактично медленно.
Хотя я в первую очередь ищу «питонистские» ответы и библиотеки, которые могут иметь способы реализации этого, около 90% будет связано с нахождением правильного алгоритма, а 10% — с его реализацией.
ИЗМЕНЕНИЕ: Вот иллюстрация некоторых вырожденных случаев, связанных с маленькими треугольниками, очень близкими к ярким границам, чтобы продемонстрировать проблемы с простыми методами.
Для наглядности я создал изображение, которое использует только два цвета: маджента и синий, на поле высотой 6 пикселей и шириной 16 пикселей.
Если мы просто будем считать пиксели, чьи центры находятся в треугольнике, треугольник A имеет площадь 10 (8 маджентовых, 4 синих), треугольник B имеет площадь 2 (2 маджентовых), а треугольник C имеет площадь 4 (3 маджентовых, 1 синий). Это явно неверная оценка как общей площади каждого треугольника, так и соотношения маджентового цвета к синему цвету в каждом треугольнике. Если мы будем считать любую часть пикселя как “внутри” треугольника, треугольник A имеет площадь 19 (10 маджентовых пикселей и 9 синих пикселей), треугольник B имеет площадь 8 (4 маджентовых пикселя и 4 синих пикселя), а треугольник C также имеет площадь 8 (4 маджентовых пикселя и 4 синих пикселя). Несмотря на то, что треугольники B и C имеют одинаковую “площадь” при учете всех пикселей, частично обрезанных треугольником, пропорции их площадей заметно различаются и не должны иметь одинаковый средний цвет.
Треугольник, по определению, — это множество точек, заключенных внутри метрики, образованной тремя секциями, которые пересекаются. Секция линии определяется уравнением, которое выглядит как одно из следующих:
- x = a
- y = b
- y = cX + d
Если вы переберете точки в прямоугольнике, вы можете определить, находится ли точка внутри треугольника, проверяя, находится ли точка на какой-либо из секций или с правильной стороны от всех из них. Вот некоторые реализации для этого:
https://www.geeksforgeeks.org/check-whether-a-given-point-lies-inside-a-triangle-or-not/
# Утилита для вычисления площади
# треугольника, образованного (x1, y1),
# (x2, y2) и (x3, y3)
def area(x1, y1, x2, y2, x3, y3):
return abs((x1 * (y2 - y3) + x2 * (y3 - y1)
+ x3 * (y1 - y2)) / 2.0)
# Функция для проверки, находится ли точка P(x, y)
# внутри треугольника, образованного
# A(x1, y1), B(x2, y2) и C(x3, y3)
def isInside(x1, y1, x2, y2, x3, y3, x, y):
# Вычислить площадь треугольника ABC
A = area(x1, y1, x2, y2, x3, y3)
# Вычислить площадь треугольника PBC
A1 = area(x, y, x2, y2, x3, y3)
# Вычислить площадь треугольника PAC
A2 = area(x1, y1, x, y, x3, y3)
# Вычислить площадь треугольника PAB
A3 = area(x1, y1, x2, y2, x, y)
# Проверить, если сумма A1, A2 и A3
# равна A
if(A == A1 + A2 + A3):
return True
else:
return False
# Драйверная программа для проверки вышеуказанной функции
# Давайте проверим, находится ли точка P(10, 15)
# внутри треугольника, образованного
# A(0, 0), B(20, 0) и C(10, 30)
if (isInside(0, 0, 20, 0, 10, 30, 10, 15)):
print('Внутри')
else:
print('Не внутри')
# Этот код был предложен Данишем Разой
Если вы хотите сократить количество точек, которые вы собираетесь рассмотреть, то получите ограничивающий прямоугольник треугольника и переберите каждую строку, решая уравнения сегмента для этой строки, находя два решения (или одно решение на границе) и перебирая правую подсекцию этой строки.
Ответ или решение
Чтобы эффективно получить средний цвет треугольника, накладываемого на растровое изображение в формате RGB, нужно учесть несколько факторов, таких как расположение вершин треугольника и работа с пикселями, что может значительно повлиять на точность конечного результата. Вот подход к решению этой задачи с использованием Python и популярных библиотек, таких как NumPy и PIL (Pillow).
Шаг 1: Импорт необходимых библиотек
import numpy as np
from PIL import Image
Шаг 2: Определение функций для вычисления площади и проверки нахождения точки внутри треугольника
Мы будем использовать функцию, чтобы определить, находится ли точка внутри треугольника и чтобы рассчитать площадь треугольника:
def area(x1, y1, x2, y2, x3, y3):
return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0)
def is_inside(x1, y1, x2, y2, x3, y3, x, y):
A = area(x1, y1, x2, y2, x3, y3)
A1 = area(x, y, x2, y2, x3, y3)
A2 = area(x1, y1, x, y, x3, y3)
A3 = area(x1, y1, x2, y2, x, y)
return A == A1 + A2 + A3
Шаг 3: Получение изображения и определение bounding box треугольника
Загрузите ваше изображение и определите границы треугольника:
image = Image.open('image.png')
pixels = np.array(image)
height, width, _ = pixels.shape
A = (x1, y1) # Координаты первой вершины
B = (x2, y2) # Координаты второй вершины
C = (x3, y3) # Координаты третьей вершины
# Определение bounding box
min_x = int(max(min(x1, x2, x3), 0))
max_x = int(min(max(x1, x2, x3), width))
min_y = int(max(min(y1, y2, y3), 0))
max_y = int(min(max(y1, y2, y3), height))
Шаг 4: Цикл по пикселям внутри bounding box
Теперь нам нужно перебрать все пиксели в bounding box и вычислить их только для тех, которые находятся внутри треугольника:
total_color = np.array([0, 0, 0], dtype=np.float64)
num_pixels = 0
for y in range(min_y, max_y):
for x in range(min_x, max_x):
if is_inside(x1, y1, x2, y2, x3, y3, x + 0.5, y + 0.5): # Используем центр пикселя
total_color += pixels[y, x]
num_pixels += 1
if num_pixels > 0:
average_color = total_color / num_pixels
else:
average_color = np.array([0, 0, 0]) # Если нет пикселей внутри треугольника
Шаг 5: Вывод результата
Теперь вы можете вывести средний цвет:
# Преобразуем средний цвет в целые числа
average_color = np.round(average_color).astype(int)
print(f'Средний цвет треугольника: R = {average_color[0]}, G = {average_color[1]}, B = {average_color[2]}')
Заключение
Этот метод позволяет эффективно получить средний цвет треугольника на изображении, учитывая только пиксели, которые находятся внутри него. Вы можете улучшить производительность, уменьшив количество проверок, изменяя шаг в цикле или используя более сложные алгоритмы, такие как трассировка лучей.