Получить средний цвет треугольника, наложенного на битмап

Вопрос или проблема

Предположим, у меня есть изображение в формате RGB I. Я хочу получить средний цвет нескольких случайных треугольников с вершинами A, B и C. Вершины A, B и C гарантированно находятся внутри границ изображения I, но не гарантированно имеют целочисленные значения. Как эффективно получить средний цвет этой области?

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

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

Хотя я в первую очередь ищу «питонистские» ответы и библиотеки, которые могут иметь способы реализации этого, около 90% будет связано с нахождением правильного алгоритма, а 10% — с его реализацией.

ИЗМЕНЕНИЕ: Вот иллюстрация некоторых вырожденных случаев, связанных с маленькими треугольниками, очень близкими к ярким границам, чтобы продемонстрировать проблемы с простыми методами.

Три треугольника, обозначенные A, B и C, пересекают яркую маджентовую область и светло-голубую область. Треугольник A почти равносторонний, перекрывая 10 пикселей в мадженте и 9 пикселей в синем; однако конкретные углы означают, что менее 1% площади нескольких синих пикселей попадает в треугольник. Только три пикселя полностью заключены внутри треугольника A. Треугольник B — это широкий равнобедренный треугольник; хотя он перекрывает 8 пикселей, ни один пиксель полностью не содержится, а треугольник в целом находится более чем на 99% в маджентовой области. Треугольник C — это скалярный треугольник; он перекрывает 8 пикселей, и большая часть площади треугольника C находится в маджентовой области

Для наглядности я создал изображение, которое использует только два цвета: маджента и синий, на поле высотой 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]}')

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...