Я работаю над веб-приложением, которое позволит вам редактировать изображения с использованием цветов, например, раскрашивая черно-белое изображение или переключая цвет автомобиля на цветное изображение.
Мой первый подход к выбору цвета заключался в том, чтобы сэмплировать цвет в rgb, а затем работать с ним. Проблема с rgb заключается в том, что цветовая вариация настолько велика, что, выбирая похожие оттенки фиолетового на изображении, вы можете в конечном итоге повторно выбрать несколько раз и все еще иметь маленькие квадратики, где вы еще не пробовали их. Например, вот исходное изображение:
Используя веб-приложение, я выбираю небольшую область между изображением для выборки цветов, а затем показываю, где цвета совпадают ниже.
вы можете сказать, что у него есть небольшая область прямоугольника и маленькие пятна снаружи, где совпадает цвет.
Я провел некоторое исследование и обнаружил, что HSV, который представляет собой оттенок, насыщенность и значение, является более простым способом выбора оттенков цветов.
Таким образом, используя тот же метод, веб-приложение предоставляет интересующую область, я, как обычно, получаю интересующую область, а затем, если использую hsv, получаю значение оттенка интересующей области и использую его для проверки изображения, все пиксели с одинаковыми значение hue, и оно возвращается как новая область выбора и затем используется для выборки всех этих новых пикселей для цветов.
Это код ниже
def get_hsv_positions(image, positions):
#hsv comes in hue (color of image), saturation and value that's supposed to be how light or dark the color is
hsv_image = image.convert('HSV')
values = []
for pixel in positions:
values.append(hsv_image.getpixel(tuple(pixel)))
hues = set([x[0] for x in values])
hue_matches = []
width, height = image.size
img_width, img_height = 0,0
for _ in range(width * height):
if img_width == width:
img_height += 1
img_width = 0
pixel = hsv_image.getpixel((img_width, img_height))
if pixel[0] in hues:
hue_matches.append((img_width, img_height))
img_width += 1
return hue_matches
def get_color_range(request):
...
#this gets the x,y position on the image of interest to sample colors
cells = []
for item in selection:
pos = get_position(item['position']['left'], item['position']['top'], image.width, image.height, width, height)
item['position']['top'] = pos[1]
item['position']['left'] = pos[0]
scale_grid(item, width, height, image.width, image.height)
cells.extend(get_cells(item))
image = PILImage.open(image_url)
if hsv:
cells = get_hsv_positions(image, cells)
и вот как он теперь сэмплирует изображение
это лучше, но не идеально, как вы можете сказать здесь
Как вы можете сказать, HSV получает больше тех маленьких тонов, которые появляются в виде маленьких фигур на изображении, но не получают их все.
Я надеюсь, что кто-то, кто играл с манипулированием изображениями, будь то для науки о данных или чего-то еще, может помочь.
Кроме того, это проект с открытым исходным кодом, который я создал и над которым работаю в свободное время, поэтому, если вам интересно, вы можете проверить исходный код, вот видео краткое введение о том, как это работает. Так что, если вы хотите внести свой вклад, вы знаете каналы :) или используйте его бесплатно, как вам удобно.
Что-либо в цветовом пространстве HSV часто становится безумным и блочным после сжатия JPEG - я думаю, что это может иметь какое-то отношение к понижению дискретизации цветности, но никогда не исследовал это.






Решение, которое приходит на ум, состоит в том, чтобы размыть канал оттенка вашего изображения при выборе пикселей в диапазоне. Это должно сгладить резкие изменения оттенка (например, квадратные артефакты на портрете).
Один из способов сделать это — преобразовать канал оттенка в пустой массив, применить фильтр Гаусса, а затем использовать его для создания маски. Это грубая переработка вашей функции get_hsv_positions, которая использует numpy и scipy вместо циклов.
import numpy as np
from scipy.ndimage import gaussian_filter
def get_hsv_positions_smooth(image, positions):
#hsv comes in hue (color of image), saturation and value that's supposed to be how light or dark the color is
hsv_image = image.convert('HSV')
# convert to numpy array
hsv_image = np.array(hsv_image)
hue_channel = hsv_image[0, :, :] # indexed (channel, row, column)
# get the matching hues
matching_hues = set()
for x,y in positions:
matching_hues.add(hue_channel[x, y])
# blur hue channel
SIGMA = 2
blurred_hue = gaussian_filter(hue_channel, SIGMA)
# mask the matching values
mask = np.isin(blurred_hue, matching_hues)
# get a list of [(x, y), ...] true values in the mask
xs, ys = np.nonzero(mask)
return zip(xs, ys)
Стоит отметить, что вы можете исследовать другие виды размытия, которые имеют другие свойства.
Редактировать: Я переместил предыдущее решение вниз, потому что думаю, что размытие, вероятно, даст вам лучшие результаты.
Одним из решений может быть искусственное сглаживание/расширение hues списка разрешенных оттенков. В данной области интереса выбранные вами оттенки могут не быть непрерывными (например, темно-синий и голубой, но не синие между ними) и могут прерываться на неестественном пороге.
Я бы предложил превратить hues в np.array, чтобы было немного проще выполнять такие манипуляции. Если бы ваш массив hues выглядел примерно как [30, 31, ..., 40, 230, 231, ..., 240], вы могли бы сгладить его с помощью
import numpy as np
hues = np.array(hues) # numpy it
blended_hues = np.concatenate([
hues + 1, hues + 2, ... # as many points out as you want
hues - 1, hues - 2, ...
])
all_hues = list(np.unique(np.concatenate([hues, blended_hues])))
И тогда у all_hues будут ваши оригинальные оттенки с некоторыми дополнениями.
Другим решением может быть просмотр цветовых пространств МКО. В них обычно X и Y оттенки расширены в двух измерениях, так что вы можете сделать более сложное смешивание цветов.
Я понимаю, что вы имеете в виду, я попробую и посмотрю, как это работает
Также эта реализация hues = set([x[0] for x in values]) hues = np.array(hues) # numpy it blended_hues = np.concatenate([ hues + 1, hues + 2, hues - 1, hues - 2, ]) all_hues = list(np.unique(np.concatenate([hues, blended_hues]))) выдает эту ошибку File "/home/sam/code/pixelart/pixelart/views.py", line 430, in get_hsv_positions hues + 1, hues + 2, TypeError: unsupported operand type(s) for +: 'set' and 'int'
Вам нужно будет изменить строку с hues = np.array(hues) на hues = np.array(list(hues)). Массивы Numpy упорядочены и не поддерживают приведение напрямую из set объектов. В качестве альтернативы просто удалите часть set(...) в начале — значения в любом случае будут сделаны уникальными с помощью np.unique(...).
это работает, но немного перебарщивает и пробует цвета, такие как цвет ее помады, который намного меньше похож на цвет кожи.
Я добавил еще одно решение, которое включает сглаживание канала оттенка. Это также должно сделать совпадающие области немного более гладкими.
Получение этой ошибки с вашей реализацией matching_hues.add(hue_channel[x, y]) IndexError: only integers, slices (:), ellipsis (...), numpy.newaxis (None) and integer or boolean arrays are valid indices
Я только что получил шанс проверить это. Я перепутал индексы на hue_channel = hsv_image[...]. Нарезка [:, :, 0] испортила x, y координаты, что привело к ошибке вашего индекса. Правильный порядок нарезки массивов numpy — [channel, row, column], поэтому я изменил строку на hue_channel = hsv_image[0, :, :]. Извини за это! Здесь — полезная статья о нарезке Numpy!
Он по-прежнему возвращает ту же ошибку File "/home/sam/code/pixelart/pixelart/views.py", line 493, in get_color_range cells = get_hsv_positions_smooth(image, cells) File "/home/sam/code/pixelart/pixelart/views.py", line 458, in get_hsv_positions_smooth matching_hues.add(hue_channel[x, y]) IndexError: only integers, slices (:), ellipsis (...), numpy.newaxis (None) and integer or boolean arrays are valid indices [09/Apr/2019 06:51:13] "POST /color-range HTTP/1.1" 500 16846
Некоторые направления, чтобы попробовать (но не полный ответ): со вторым изображением (или для вашей идеи «автомобиля») похоже, что сегментация может помочь. Вы смотрели на «водораздел» opencv? Кроме того, участки, которые вы пропустили, имеют квадратную форму, что заставляет меня думать, что они могут быть связаны со сжатием JPEG, и может помочь фильтр Гаусса с учетом границ.