Spec-Zone.ru › scikit-learn

Примечание

Перейти в конец, чтобы скачать полный код примера или запустить этот пример в браузере с помощью JupyterLite или Binder.

Пример векторного квантования

В этом примере показано, как с помощью KBinsDiscretizer выполнить векторное квантование набора учебных изображений — изображения лица енота.

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

Исходное изображение

Для начала загрузим изображение лица енота из SciPy. Также проверим некоторые сведения об изображении, например его форму и тип данных, используемый для его хранения.

from scipy.datasets import face

raccoon_face = face(gray=True)

print(f"The dimension of the image is {raccoon_face.shape}")
print(f"The data used to encode the image is of type {raccoon_face.dtype}")
print(f"The number of bytes taken in RAM is {raccoon_face.nbytes}")
The dimension of the image is (768, 1024)
The data used to encode the image is of type uint8
The number of bytes taken in RAM is 786432

Итак, изображение представляет собой двумерный массив высотой 768 пикселей и шириной 1024 пикселя. Каждое значение — это 8-битное беззнаковое целое число, то есть изображение закодировано с использованием 8 бит на пиксель. Общий объём памяти, занимаемой изображением, составляет 786 килобайт (1 байт равен 8 битам).

Использование 8-битного беззнакового целого числа означает, что изображение закодировано максимум с использованием 256 различных оттенков серого. Проверим распределение этих значений.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(ncols=2, figsize=(12, 4))

ax[0].imshow(raccoon_face, cmap=plt.cm.gray)
ax[0].axis("off")
ax[0].set_title("Rendering of the image")
ax[1].hist(raccoon_face.ravel(), bins=256)
ax[1].set_xlabel("Pixel value")
ax[1].set_ylabel("Count of pixels")
ax[1].set_title("Distribution of the pixel values")
_ = fig.suptitle("Original image of a raccoon face")
Original image of a raccoon face, Rendering of the image, Distribution of the pixel values

Сжатие с помощью векторного квантования

Идея сжатия с помощью векторного квантования состоит в том, чтобы уменьшить количество уровней серого, используемых для представления изображения. Например, можно использовать 8 значений вместо 256. Это означает, что для кодирования одного пикселя можно эффективно использовать 3 бита вместо 8 и тем самым уменьшить объём используемой памяти примерно в 2,5 раза. Позже мы подробнее обсудим использование памяти.

Стратегия кодирования

Сжатие можно выполнить с помощью KBinsDiscretizer. Нужно выбрать стратегию определения 8 оттенков серого для субдискретизации. Самая простая стратегия — задать равные интервалы между значениями, что соответствует настройке strategy="uniform". Из предыдущей гистограммы мы знаем, что эта стратегия, безусловно, не оптимальна.

from sklearn.preprocessing import KBinsDiscretizer

n_bins = 8
encoder = KBinsDiscretizer(
    n_bins=n_bins,
    encode="ordinal",
    strategy="uniform",
    random_state=0,
)
compressed_raccoon_uniform = encoder.fit_transform(raccoon_face.reshape(-1, 1)).reshape(
    raccoon_face.shape
)

fig, ax = plt.subplots(ncols=2, figsize=(12, 4))
ax[0].imshow(compressed_raccoon_uniform, cmap=plt.cm.gray)
ax[0].axis("off")
ax[0].set_title("Rendering of the image")
ax[1].hist(compressed_raccoon_uniform.ravel(), bins=256)
ax[1].set_xlabel("Pixel value")
ax[1].set_ylabel("Count of pixels")
ax[1].set_title("Sub-sampled distribution of the pixel values")
_ = fig.suptitle("Raccoon face compressed using 3 bits and a uniform strategy")
Raccoon face compressed using 3 bits and a uniform strategy, Rendering of the image, Sub-sampled distribution of the pixel values

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

Видно, что значения пикселей были сопоставлены с 8 различными значениями. Проверим соответствие между этими значениями и исходными значениями пикселей.

bin_edges = encoder.bin_edges_[0]
bin_center = bin_edges[:-1] + (bin_edges[1:] - bin_edges[:-1]) / 2
bin_center
array([ 15.5625,  46.6875,  77.8125, 108.9375, 140.0625, 171.1875,
       202.3125, 233.4375])
_, ax = plt.subplots()
ax.hist(raccoon_face.ravel(), bins=256)
color = "tab:orange"
for center in bin_center:
    ax.axvline(center, color=color)
    ax.text(center - 10, ax.get_ybound()[1] + 100, f"{center:.1f}", color=color)
plot face compress

Как уже отмечалось, стратегия равномерной выборки не оптимальна. Например, обратите внимание, что пиксели, сопоставленные со значением 7, кодируют довольно мало информации, тогда как значение 3 соответствует большому количеству пикселей. Вместо этого можно использовать стратегию кластеризации, например k-means, чтобы найти более оптимальное соответствие.

encoder = KBinsDiscretizer(
    n_bins=n_bins,
    encode="ordinal",
    strategy="kmeans",
    random_state=0,
)
compressed_raccoon_kmeans = encoder.fit_transform(raccoon_face.reshape(-1, 1)).reshape(
    raccoon_face.shape
)

fig, ax = plt.subplots(ncols=2, figsize=(12, 4))
ax[0].imshow(compressed_raccoon_kmeans, cmap=plt.cm.gray)
ax[0].axis("off")
ax[0].set_title("Rendering of the image")
ax[1].hist(compressed_raccoon_kmeans.ravel(), bins=256)
ax[1].set_xlabel("Pixel value")
ax[1].set_ylabel("Number of pixels")
ax[1].set_title("Distribution of the pixel values")
_ = fig.suptitle("Raccoon face compressed using 3 bits and a K-means strategy")
Raccoon face compressed using 3 bits and a K-means strategy, Rendering of the image, Distribution of the pixel values
bin_edges = encoder.bin_edges_[0]
bin_center = bin_edges[:-1] + (bin_edges[1:] - bin_edges[:-1]) / 2
bin_center
array([ 18.90934343,  53.33478066,  82.59678424, 109.22385188,
       134.67876527, 159.67877978, 184.72384803, 223.17132867])
_, ax = plt.subplots()
ax.hist(raccoon_face.ravel(), bins=256)
color = "tab:orange"
for center in bin_center:
    ax.axvline(center, color=color)
    ax.text(center - 10, ax.get_ybound()[1] + 100, f"{center:.1f}", color=color)
plot face compress

Теперь количества пикселей в интервалах более сбалансированы, а их центры расположены не на одинаковом расстоянии друг от друга. Обратите внимание, что можно обеспечить одинаковое количество пикселей в каждом интервале, используя strategy="quantile" вместо strategy="kmeans".

Использование памяти

Ранее мы утверждали, что объём используемой памяти должен уменьшиться в 8 раз. Проверим это.

print(f"The number of bytes taken in RAM is {compressed_raccoon_kmeans.nbytes}")
print(f"Compression ratio: {compressed_raccoon_kmeans.nbytes / raccoon_face.nbytes}")
The number of bytes taken in RAM is 6291456
Compression ratio: 8.0

Довольно неожиданно, что сжатое изображение занимает в 8 раз больше памяти, чем исходное. Это прямо противоположно нашим ожиданиям. Причина заключается главным образом в типе данных, используемом для кодирования изображения.

print(f"Type of the compressed image: {compressed_raccoon_kmeans.dtype}")
Type of the compressed image: float64

Действительно, результат работы KBinsDiscretizer — это массив 64-битных чисел с плавающей точкой. Значит, он занимает в 8 раз больше памяти. Однако мы используем это представление 64-битными числами с плавающей точкой для кодирования 8 значений. Действительно, мы сэкономим память, только если преобразуем сжатое изображение в массив 3-битных целых чисел. Для этого можно использовать метод numpy.ndarray.astype. Однако представления 3-битных целых чисел не существует, поэтому для кодирования 8 значений нам также пришлось бы использовать 8-битное беззнаковое целое число.

На практике для экономии памяти исходное изображение должно быть представлено 64-битными числами с плавающей точкой.

Общее время выполнения скрипта: (0 минут 1.868 секунды)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_face_compress.ipynb

Download Python source code: plot_face_compress.py

Download zipped: plot_face_compress.zip

Связанные примеры

Удаление шума с изображений с помощью обучения словаря

Распознавание рукописных цифр

Демонстрация структурированной иерархической кластеризации Уорда на изображении монет

Сегментация изображения греческих монет на области

© 2007–2026 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.9/auto_examples/cluster/plot_face_compress.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API