уменьшить изображение до N цветов в OpenCV Python

Я могу найти примеры только на C/C++, и они, похоже, никогда не сопоставляются с API OpenCV. Я загружаю видеокадры (как из файлов, так и с веб-камеры) и хочу уменьшить их до 16 цветов, но сопоставить с 24-битным цветовым пространством RGB (это то, что требуется для моего вывода — гигантский светодиодный дисплей).

Я читаю данные так:

ret, frame = self._vid.read() 
image = cv2.cvtColor(frame, cv2.COLOR_RGB2BGRA)

Я нашел приведенный ниже пример Python, но не могу понять, как сопоставить его с типом выходных данных, которые мне нужны:

import numpy as np
import cv2

img = cv2.imread('home.jpg')
Z = img.reshape((-1,3))

# convert to np.float32
Z = np.float32(Z)

# define criteria, number of clusters(K) and apply kmeans()
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 8
ret,label,center=cv2.kmeans(Z,K,None,criteria,10,cv2.KMEANS_RANDOM_CENTERS)

# Now convert back into uint8, and make original image
center = np.uint8(center)
res = center[label.flatten()]
res2 = res.reshape((img.shape))

cv2.imshow('res2',res2)
cv2.waitKey(0)
cv2.destroyAllWindows()

Это, очевидно, работает для средства просмотра изображений OpenCV, но пытается сделать те же ошибки в моем выходном коде, поскольку мне нужен формат RGB или RGBA. Мой вывод работает следующим образом:

for y in range(self.height):
    for x in range(self.width):
        self._led.set(x,y,tuple(image[y,x][0:3]))

Каждый цвет представлен кортежем (r,g,b).

Любые мысли о том, как заставить это работать?


person Adam Haile    schedule 20.09.2015    source источник
comment
res2 в приведенном выше примере кода OpenCV — это изображение с цветовым квантованием BGR, и это работает нормально. Значит, у тебя проблема с дисплеем _led?   -  person Miki    schedule 20.09.2015
comment
Хм... Я сделал ошибку в коде, я скопировал этот пример кода, и теперь он работает... вроде того. Но это ОЧЕНЬ медленно. около 1,5 секунд на кадр   -  person Adam Haile    schedule 20.09.2015
comment
Что ж, первая проблема решена! Это медленно, вероятно, потому, что вы выполняете kmeans (очень-очень медленный алгоритм) в каждом кадре. Вы хотите, чтобы квантованный цвет менялся со временем? Или вы устанавливаете фиксированный набор цветов и используете их навсегда? Также обратите внимание, что использование алгоритма medianCut может быть быстрее, чем kmeans.   -  person Miki    schedule 20.09.2015


Ответы (2)


Я думаю, что следующее может быть быстрее, чем kmeans, особенно с k = 16.

  1. Преобразовать цветное изображение в серый
  2. Контрастно растяните это серое изображение так, чтобы результирующие уровни серого изображения находились в диапазоне от 0 до 255 (используйте normalize с NORM_MINMAX).
  3. Рассчитайте гистограмму этого растянутого серого изображения, используя 16 в качестве количества интервалов (calcHist).
  4. Теперь вы можете изменить эти 16 значений гистограммы. Например, вы можете сортировать и назначать ранги (скажем, от 0 до 15) или назначать 16 равномерно распределенных значений от 0 до 255 (я думаю, это может дать вам согласованный результат для видео).
  5. Спроецируйте эту гистограмму на растянутое серое изображение (calcBackProject).
  6. Примените карту цветов к этому обратно проецируемому изображению (возможно, вы захотите масштабировать обратно проецированное изображение перед применением карты цветов с помощью applyColorMap)

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

person dhanushka    schedule 21.09.2015
comment
Простите мое невежество, но я понятия не имею, как это сделать... Я полный новичок в OpenCV. - person Adam Haile; 21.09.2015
comment
@AdamHaile О. Я обновил свой ответ советом для kmeans. Так как вы уже использовали kmeans, можете попробовать предложенную модификацию. - person dhanushka; 22.09.2015

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

person Mikael Rousson    schedule 22.09.2015
comment
Как бы вы выбрали ближайший центр тяжести? Если я перебираю центроиды и вычисляю каждое расстояние от центроида до пикселя, а затем выбираю наименьшее расстояние, это занимает слишком много времени. - person Adi; 20.07.2016
comment
@Adi взгляните на алгоритмы поиска ближайшего соседа для этого. - person Mikael Rousson; 21.07.2016