NumPy: линейная алгебра в многомерных массивах | Месяц темы Python

Python анализ данных

Эта статья участвует в "Месяце тем Python", подробнее см.Ссылка на мероприятие

[toc]

Введение

В этой статье объясняется, как выполнять операции линейной алгебры над многомерными данными в NumPy в виде диаграмм.

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

Загрузка графики и инструкции

Друзья, знакомые с цветами, должны знать, что цвет может быть представлен R, G, B. Если он более сложный, то есть также A для обозначения прозрачности. Обычно мы используем массив из четырех свойств для представления.

Для двумерного изображения его разрешение можно рассматривать как матрицу X*Y, а цвет каждой точки матрицы можно представить как (R, G, B).

Обладая вышеуказанными знаниями, мы можем разложить цвет изображения.

Сначала нам нужно загрузить изображение, мы используем метод imageio.imread для загрузки локального изображения следующим образом:

import imageio
img=imageio.imread('img.png')
print(type(img))

Приведенный выше код считывает изображение из локального объекта в объект img. Используйте type для просмотра типа img. Из текущего результата мы видим, что тип img — это массив.

class 'imageio.core.util.Array'

Через img.shape можно получить, что img представляет собой трехмерный массив (80, 170, 4), а значит, разрешение этого изображения 80*170, а каждый пиксель представляет собой массив (R, B , Г, А).

Наконец, нарисуйте изображение следующим образом:

import matplotlib.pyplot as plt
plt.imshow(img)

оттенки серого графики

Для трехмерных массивов мы можем получить три массива цветов следующим образом:

red_array = img_array[:, :, 0]
green_array = img_array[:, :, 1]
blue_array = img_array[:, :, 2]

После того, как у нас есть три цвета, мы можем использовать следующую формулу для их оттенков серого:

Y=0.2126R + 0.7152G + 0.0722B

Y на изображении выше представляет оттенки серого.

Как использовать умножение матриц? Просто используйте @ :

 img_gray = img_array @ [0.2126, 0.7152, 0.0722]

Теперь img представляет собой матрицу 80*170.

Теперь используйте cmap="gray" для построения графика:

plt.imshow(img_gray, cmap="gray")

Можно получить следующее изображение в градациях серого:

Сжатие изображений в градациях серого

Изображение в градациях серого предназначено для преобразования цвета изображения, что с этим делать, если вы хотите сжать изображение?

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

Пусть A — матрица n-го порядка, если существуют константа λ и n-мерный ненулевой вектор x такие, что Ax=λx, то λ — собственное значение матрицы A, а x — собственный вектор матрицы A, принадлежащий собственное значение λ.

Набор собственных векторов матрицы — это набор ортонормированных векторов.

То есть собственный вектор подвергается линейному преобразованию A, которое только удлиняет или укорачивает вектор без изменения его направления.

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

Если A — матрица порядка m * n, q=min(m,n), арифметический квадратный корень из q неотрицательных собственных значений A*A называется сингулярным значением A.

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

A=UΣVTA=UΣV^T

где A — матрица m * n, которая должна быть разложена целью, U — квадратная матрица m * m, а Σ — матрица m * n, все недиагональные элементы которой равны нулю.VTV^Tявляется транспонированием V, которое также является матрицей n * n.

Сингулярные числа аналогичны собственным значениям, они также располагаются от большего к меньшему в матрице Σ, и редукция сингулярных значений происходит особенно быстро,Во многих случаях сумма верхних 10% или даже 1% сингулярных значений составляет более 99% суммы всех сингулярных значений.. То есть мы также можем аппроксимировать матрицу сингулярными значениями с большим первым r. r — число, намного меньшее m, n, так что матрицу можно сжать.

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

Чтобы использовать сингулярное разложение svd, вы можете напрямую вызвать linalg.svd следующим образом:

U, s, Vt = linalg.svd(img_gray)

где U — матрица размера m*m, а Vt — матрица размера n*n.

На изображении выше U — матрица (80, 80), а Vt — матрица (170, 170). А s — это массив из 80, s содержит единичные значения в img.

Если мы представим s как образ, то увидим, что большинство сингулярных значений сосредоточено в первой части:

Это также означает, что мы можем взять предыдущую часть значения s для восстановления изображения.

Чтобы восстановить изображение с помощью s, вам нужно восстановить s до матрицы 80 * 170:

# 重建
import numpy as np
Sigma = np.zeros((80, 170))
for i in range(80):
    Sigma[i, i] = s[i]

Используйте U @ Sigma @ Vt для восстановления исходной матрицы.Вы можете сравнить разницу между исходной матрицей и восстановленной матрицей, вычислив linalg.norm.

linalg.norm(img_gray - U @ Sigma @ Vt)

Или используйте np.allclose, чтобы сравнить разницу между двумя матрицами:

np.allclose(img_gray, U @ Sigma @ Vt)

Или просто возьмите первые 10 элементов массива s, перерисуйте и сравните разницу с исходным изображением:

k = 10
approx = U @ Sigma[:, :k] @ Vt[:k, :]
plt.imshow(approx, cmap="gray")

Как видите, разница не очень большая:

Сжатие исходного изображения

В предыдущем разделе мы говорили о том, как сжимать изображения в градациях серого, так как же сжимать исходные изображения?

Матрицу также можно разложить с помощью linalg.svd.

Однако перед использованием требуется некоторая обработка, потому что img_array исходного изображения представляет собой матрицу (80, 170, 3) — здесь мы удалили прозрачность и сохранили только три атрибута R, B и G.

Перед выполнением преобразования нам нужно поставить ось, которую не нужно преобразовывать, на передний план, то есть изменить index=2 на положение index=0, а затем выполнить операцию svd:

img_array_transposed = np.transpose(img_array, (2, 0, 1))
print(img_array_transposed.shape)

U, s, Vt = linalg.svd(img_array_transposed)
print(U.shape, s.shape, Vt.shape)

Аналогично, теперь s представляет собой матрицу (3, 80), которая по-прежнему на одну размерность меньше.Если изображение нужно восстановить, его нужно заполнить и обработать, и, наконец, выводится восстановленное изображение:

Sigma = np.zeros((3, 80, 170))

for j in range(3):
    np.fill_diagonal(Sigma[j, :, :], s[j, :])

reconstructed = U @ Sigma @ Vt
print(reconstructed.shape)

plt.imshow(np.transpose(reconstructed, (1, 2, 0)))

Конечно, предыдущие собственные значения K также могут быть выбраны для сжатия изображения:

approx_img = U @ Sigma[..., :k] @ Vt[..., :k, :]
print(approx_img.shape)
plt.imshow(np.transpose(approx_img, (1, 2, 0)))

Восстановленное изображение выглядит следующим образом:

При сравнении можно обнаружить, что хотя некоторая точность теряется, изображение все же можно различить.

Суммировать

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

Эта статья была включена вwoohoo.floydpress.com/08-python-вы…

Самая популярная интерпретация, самая глубокая галантерея, самые краткие уроки и множество трюков, о которых вы не знаете, ждут вас!

Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: «Программируйте эти вещи», разбирайтесь в технологиях, лучше поймите себя!