Рост вычислительной сложности k-means с ростом уникальных и неуникальных дата-поинтов?

Стоит задача сократить количество оригинальных цветов на изображении до заданного числа. Такой вопрос... поправьте если я ошибся в своих рассуждениях.

Положение точки в пространстве RGB определяется величиной интесивности этих каналов. А тремя координатами (R, G, B) в трехмерном пространстве может быть задана одна и только одна точка.

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

Но есть одно но. Я слямзил соответствующий код с geeksforgeeks (см ниже), и при увеличении изображения эта зараза скейлится прямо пропорционально добавленной площади, что говорит о том что алгоритм не группирует одинаковые по цвету дата-поинты. Но что еще хуже, так он еще и на уменьшение количества оригинальных цветов по средствам jpeg сжатия не реагирует, хотя должен - количество уникальных дата поинтов при сжатии то уменьшается. Вот только как работает основной кусок кода (функция k_means) на случай правки кода господа забугорные гики обьяснить забыли.

from google.colab.patches import cv2_imshow
from google.colab import drive
drive.mount('/content/gdrive')
import cv2
import numpy as np
def prepare_screenshot(img, quality, crop_y, crop_x, crop_h, crop_w):
  #Кроп интересующего фрагмента
  img = img[crop_y:crop_h, crop_x:crop_w] 
  img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
  img = cv2.medianBlur(img, 15)
  H, S, V = cv2.split(img)
  v = cv2.equalizeHist(V)
  img = cv2.merge([H, S, v])
  img = cv2.cvtColor(img, cv2.COLOR_HSV2RGB)
  img = cv2.resize(img, None, fx=0.0625, fy=0.0625)
  encodedImg = cv2.imencode('.jpg', img, params=[int(cv2.IMWRITE_JPEG_QUALITY), quality])[1]
  decodedImg = cv2.imdecode(encodedImg, cv2.IMREAD_COLOR)
  return img

def initialize_means(img, clusters):
  points = img.reshape((-1, img.shape[2]))
  m, n = points.shape
  means = np.zeros((clusters, n))
  for i in range(clusters):
    rand_indieces = np.random.choice(m, size=10, replace=False)
    means[i] = np.mean(points[rand_indieces], axis=0)
  return points, means

def distance(x1, y1, x2, y2):
  dist = np.square(x1 - x2) + np.square(y1 - y2)
  dist = np.sqrt(dist)
  return dist

def k_means(points, means, clusters):
  iterations = 5
  m, n = points.shape
  index = np.zeros(m)

  while iterations > 0:
    for j in range(m):
      min_dist = float('inf')
      temp = None

      for k in range(clusters):
        x1, y1 = points[j, 0], points[j, 1]
        x2, y2 = means[k, 0], means[k, 1]

        if distance(x1, y1, x2, y2)<= min_dist:
          min_dist = distance(x1, y1, x2, y2)
          temp = k
          index[j] = k
    for k in range(clusters):
      cluster_points = points[index==k]
      if len(cluster_points) > 0:
        means[k] = np.mean(cluster_points, axis=0)
    iterations -= 1
  return means, index

def compress_image(means, index, img):
  centroid = np.array(means)
  recovered = centroid[index.astype(int), :]
  recovered = recovered.reshape(img.shape)
  return recovered

screenShot = cv2.imread('/content/gdrive/MyDrive/Безымянный2.png')
resizedImage = prepare_screenshot(screenShot, 20, 156, 320, 924, 1600)

points, means = initialize_means(resizedImage, 256)
means, index = k_means(points, means, 256)
pastirizedImg = compress_image(means, index, resizedImage)

final = cv2.resize(pastirizedImg, None, fx=16.0, fy=16.0,interpolation=cv2.INTER_NEAREST)
cv2_imshow(final)

Господа земляки, объясните что есть что и что нужно переписывать, пожалуйста (в k_means, я имею в виду). А то в итоге отсутствия выше названных оптимизаций расчет изображения уменьшеного с 744x1280 до к примеру 46x80 (угу, у меня нокоть на мизинце больше) c 256 кластерами занял 24 секунды, при том что эти расчеты должны делаться хотябы за 10ю долю секунды, это уже не говоря о том что при таком размере выходного изображения на нем отразятся только самые крупные и однородные формы (предпологается выделение обьектов по набору цветов).

В некоторых игровых локациях прокатывает уменьшить количество кластеров из-за небольшого разнообразия цветов и объектов, но только в некоторых.

Update: Я понял в чем дело. На вход алгоритму k-means нужно подавать сплющенный массив, дедуплицированный с помощью

imgUnigueColors, indx = np.unique(img, return_inverse=True)  

Правда, я пока не понял как восстановить сплющенный, обработанный кластеризацией массив по индексам от return_inverse и k_means одновременно. Подскажите, как это оформить.


Ответы (0 шт):