Объединить похожие ячейки гистограммы изображения

#python #opencv #image-processing #computer-vision #histogram

#python #opencv #обработка изображений #компьютерное зрение #гистограмма

Вопрос:

Я работаю над проектом CV с использованием Python (без искусственного интеллекта, только классические инструменты) и столкнулся с проблемой. Я пытаюсь определить цвет руки и кожи по известному ROI, у которого рука находится на белом фоне (поскольку это с веб-камерой, цвет может быть неточным — например, серый цвет для меня может считаться белым). Я попытался создать гистограмму цветов, оттуда я извлеку цвет руки. Для получения списка цветов, которые я использовал Image.getcolors(width*height) , и поместить его в гистограмму. К сожалению, я получаю огромный список цветов, многие из которых похожи на другие (например, (255,0,0) и (255,0,1) представлены в отдельных ячейках) (из-за плохого качества камеры, освещения и т. Д.). Мой вопрос в том, как я могу объединить эти ячейки и получитьнадежная гистограмма, из которой я мог бы извлечь цвет кожи. Вот часть кода, который я написал:

 pilRoi = Image.fromarray(coloredRoi)
w,h = pilRoi.size
colorsInRoi = pilRoi.getcolors(w*h)
sortedColors = sorted(colorsInRoi, key=lambda tup: tup[0])[::-1]
 

для сортировки цветов.
И:

     for idx, color in enumerate(sortedColors):
    if(idx<config.NUM_COLORS):
        plt.bar(idx, color[0], color=helper.toHex(color[1]),edgecolor=helper.toHex(color[1]))
    else:
        break
plt.show()
 

для гистограммы.
Я попытался удалить сумму пикселей белого диапазона, проблема остается с другими цветами:

     for color in sortedColors:
    if isInWhiteRange(color[1]) or color[1] == config.BLUE:
        toRemove.append(color)

for color in toRemove:
    sortedColors.remove(color)
 

Спасибо!

Комментарии:

1. Один из способов справиться с этим — уменьшить изображение до небольшого размера перед получением гистограммы (например, 50×50). Если в нем все еще слишком много цветов, используйте -kmeans num_colors, чтобы ограничить количество цветов, например, 256 цветов или меньше.

2. Похоже, что кластеризация цветов и затем взятие центров кластеров могут лучше соответствовать проблеме, которую вы описываете. Взгляните на tberg.dk/post/determining-dominant-colors

Ответ №1:

Обычно с помощью гистограмм вы помещаете цвета в ячейки. Например, если у вас было 256 интенсивностей и 32 ячейки, это означает, что каждая ячейка имеет ширину 4. Интенсивности в диапазоне от 0-3 объединяются в первую ячейку, интенсивности в диапазоне от 4-7 объединяются во вторую ячейку и т.д. Это то, что формально известно как равномерное квантование цвета, когда мы квантовываем пиксель так, чтобы он попадал в один из заранее определенного набора ячеек.

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

Поскольку у вас уже есть изображение в виде NumPy coloredRoi , я предполагаю, что оно уже есть в трех плоскостях, поэтому 3D-массив с каналами является последним измерением. Я также предполагаю, что вы имеете дело с 8-разрядными целочисленными значениями без знака для каждого канала. Что-то простое вроде этого может сработать:

 # Define number of bins per channel
num_red_bins = 8
num_green_bins = 8
num_blue_bins = 8

# Define threshold per bin
thresh_red = 256 // num_red_bins
thresh_green = 256 // num_green_bins
thresh_blue = 256 // num_blue_bins

# Extract planes
red = coloredRoi[..., 0]
green = coloredRoi[..., 1]
blue = coloredRoi[..., 2]

# Calculate bin number per location
bin_red = red // thresh_red
bin_green = green // thresh_green
bin_blue = blue // thresh_blue

# Calculate 1D bin locations
bins = num_red_bins * num_green_bins * bin_blue   num_green_bins * bin_red   bin_green

# Calculate histogram
histo = np.bincount(bins, minlength=num_red_bins * num_green_bins * num_blue_bins)
 

Код довольно понятен, но последние две строки могут сбивать с толку. До этого момента мы преобразовали пиксели RGB в их местоположения ячеек в красном, зеленом и синем каналах. Их коллекция даст нам, где этот пиксель будет отображаться относительно конечного 3D-ячейки. Это уникальный кортеж, который будет отображаться в одном месте на одномерной гистограмме. Чтобы вычислить конечный номер ячейки 1D, представьте, что красный цвет перемещается по строкам этого пространства, а зеленый — по столбцам этого пространства. Предполагая, что нам нужно иметь дело только с красным и зеленым, каждый раз, когда нам нужно перейти к новому пространству в красном, мы должны num_green_bins перепрыгивать, поэтому у нас есть num_green_bins * bin_red . Каждый раз, когда мы переходим к новому пространству, выделенному зеленым цветом, нам просто нужно смещать столбцы, чтобы мы могли добавлять bin_green num_green_bins * bin_red bin_green . Наконец, если мы хотим перейти к синему цвету, нам нужно переходить num_red_bins * num_green_bins к каждому синему пространству, которое мы хотим, поскольку теперь мы переходим к 3D, поэтому теперь мы также добавляем with num_red_bins * num_green_bins * bin_blue . Затем мы используем numpy.bincount для вычисления окончательной гистограммы на основе только что вычисленных одномерных ячеек.

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

 out_img = np.dstack((thresh_red * bin_red, thresh_green * bin_green, thresh_blue * bin_blue))
 

numpy.dstack берет 2D-массивы и складывает их в третьем измерении для создания консолидированного 3D-массива. Если вы все сделали правильно, небольшие изменения цвета исчезнут, когда вы визуализируете квантованный результат, который хранится out_img в. Обратите внимание, что количество ячеек на цветовой канал — это параметры, которые необходимо настроить. Чем больше количество ячеек, тем более мелкозернистыми вы получаете цвета, что, таким образом, увеличивает динамический диапазон того, что вы представляете, но при использовании гранулированных цветов приходится обрабатывать очень похожие пиксели RGB по-разному. Аналогично, чем меньше количество ячеек, тем более похожие цвета будут выглядеть в более широком диапазоне значений, что ослабит различительную способность вашей классификации. Я бы предложил изменить количество ячеек таким образом, чтобы вы делали больше преувеличений в ячейках, которые отражают тон кожи человека (красный / зеленый), и меньше внимания уделяли цветам, которые этого не делают (синий).