Рост вычислительной сложности 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
одновременно. Подскажите, как это оформить.