Не получается пережать скриншот jpeg'ом
В коде ниже нужно уменьшить количество оригинальных цветов на изображении перед их кластеризацией с помошью k-means и последующим сокращением оригинальных цветов до k. Без пережатия кластеризация даже на малом изображении будет чудовещно долгая, так что я решил пережать png в jpeg с 30% качеством. Вроде все сделал по мануалу на сайте opencv, но строчка с декодированием почему-то выдает ошибку. Подскажите почему?
Код:
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]
encodedImg = cv2.imencode('.jpg', img, params=[int(cv2.IMWRITE_JPEG_QUALITY), quality])
decodedImg = cv2.imdecode(encodedImg, cv2.IMREAD_COLOR)
decodedImg = cv2.resize(img, None, fx=0.125, fy=0.125)
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/Screenshot.png')
resizedImage = prepare_screenshot(screenShot, 30, 156, 320, 924, 1600)
resizedImage = cv2.resize(resizedImage, None, fx=4.0, fy=4.0,interpolation=cv2.INTER_NEAREST)
cv2_imshow(resizedImage)
points, means = initialize_means(resizedImage, 32)
means, index = k_means(points, means, 32)
pastirizedImg = compress_image(means, index, resizedImage)
pastirizedImg = cv2.resize(pastirizedImg, None, fx=4.0, fy=4.0,interpolation=cv2.INTER_NEAREST)
cv2_imshow(pastirizedImg)
Ошибка:
error Traceback (most recent call last)
/tmp/ipython-input-1103729291.py in <cell line: 0>()
57
58 screenShot = cv2.imread('/content/gdrive/MyDrive/Screenshot.png')
---> 59 resizedImage = prepare_screenshot(screenShot, 30, 156, 320, 924, 1600)
60 resizedImage = cv2.resize(resizedImage, None, fx=4.0, fy=4.0,interpolation=cv2.INTER_NEAREST)
61 cv2_imshow(resizedImage)
/tmp/ipython-input-1103729291.py in prepare_screenshot(img, quality, crop_y, crop_x, crop_h, crop_w)
7 img = img[crop_y:crop_h, crop_x:crop_w]
8 encodedImg = cv2.imencode('.jpg', img, params=[int(cv2.IMWRITE_JPEG_QUALITY), quality])
----> 9 decodedImg = cv2.imdecode(encodedImg, cv2.IMREAD_COLOR)
10 decodedImg = cv2.resize(img, None, fx=0.125, fy=0.125)
11 return img
error: OpenCV(4.12.0) :-1: error: (-5:Bad argument) in function 'imdecode'
> Overload resolution failed:
> - buf is not a numerical tuple
> - Expected Ptr<cv::UMat> for argument 'buf'
Ответы (1 шт):
Оставлю развёрнутый ответ, на случай если кто-то столкнётся с такой же проблемой.
error: OpenCV(4.12.0) :-1: error: (-5:Bad argument) in function 'imdecode'
> Overload resolution failed:
> - buf is not a numerical tuple
> - Expected Ptr<cv::UMat> for argument 'buf'
Читаем\переводим ошибку: "buf is not a numerical tuple" -> buf не является числовым кортежем.
cv2.imencode() возвращает кортеж (True/False, буфер
):
- Первый параметр кортежа - это флаг успеха (True, если кодирование прошло успешно и False если нет).
- Второй параметр кортежа - это уже сам закодированный массив байтов.
Соответственно, пытаясь скормить кортеж методу cv2.imdecode() и выбивает ошибку: "buf is not a numerical tuple" так как cv2.imdecode()
ожидает массив байтов, а получает на вход кортеж.
В данном случае, поправить можно так:
encodedImg = cv2.imencode('.jpg', img, params=[int(cv2.IMWRITE_JPEG_QUALITY), quality])[1]
Или сразу распаковать кортеж.
_, encodedImg = cv2.imencode('.jpg', img, params=[int(cv2.IMWRITE_JPEG_QUALITY), quality])