Spec-Zone.ru › scikit-learn

Примечание

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

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

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

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

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

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

Обратите внимание, что в зависимости от версии SciPy нам нужно адаптировать импорт, так как функция, возвращающая изображение, расположена не в том же модуле. Кроме того, SciPy >= 1.10 требует установки пакета pooch.

try:  # Scipy >= 1.10
    from scipy.datasets import face
except ImportError:
    from scipy.misc 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.625,  46.875,  78.125, 109.375, 140.625, 171.875, 203.125,
       234.375])
_, 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-средних, чтобы найти более оптимальное отображение.

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.90885631,  53.34346583,  82.64447187, 109.28225276,
       134.70763101, 159.78681467, 185.17226834, 224.02069427])
_, 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 минут 2,050 секунды)

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–2025 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.6/auto_examples/cluster/plot_face_compress.html

Spec-Zone.ru

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