[PYTHON] Low-rank approximation of images by singular value decomposition

Introduction

Do you do singular value decomposition? > Greeting

I feel that people around me are just decomposing singular values recently, so I decided to try it too. For the time being, think of the image as a matrix as it is and perform singular-value-decomposition (SVD) to create a low-rank approximate image. For singular value decomposition [Wikipedia](https://ja.wikipedia.org/wiki/%E7%89%B9%E7%95%B0%E5%80%A4%E5%88%86%E8%A7% Please refer to A3).

I don't know who started the story of singular value decomposition of images, but I wrote this paper [T. Kumamoto, M. Suzuki, and H. Matsueda arXiv: 1608.08333](https://arxiv.org/abs/ I learned from 1608.08333).

The source code is https://github.com/kaityo256/image_svd It is in.

Loading images

Use Python Pillow to load the image. If not

$ pip install Pillow

Let's put it in.

Although it is an input image, it is easier to understand the approximation accuracy if there are characters, so I prepared something like this for the time being.

stop.jpg

First convert this to grayscale.

from PIL import Image
img = Image.open('stop.jpg')
gray_img = img.convert('L')
gray_img.save('stop_mono.jpg')

I feel like this.

stop_mono.jpg

Singular value decomposition

Use numpy and scipy for singular value decomposition. Let's import it.

import numpy as np
from scipy import linalg

Access to image data can be read as it is with asarray of numpy. After that, you can SVD with scipy's linalg.svd. It's too easy.

a = np.asarray(gray_img)
u, s, v = linalg.svd(a)

The u, s, and v created here are the results of the singular value decomposition. Of these, s is a list of singular values.

Low-rank approximation

Of the singular value decompositions, selecting only the higher ranks and reconstructing them will result in a low-rank approximation.

image.png

However, although Σ (s in the code) is really a matrix, it is a vector in the return value of linalg.svd, so it needs to be converted to a matrix. After that, if you multiply by the sliced ones as much as you need, a low-rank approximation matrix is completed. The code looks like this.

rank = 10
ur = u[:, :rank]
sr = np.matrix(linalg.diagsvd(s[:rank], rank,rank))
vr = v[:rank, :]
b = np.asarray(ur*sr*vr)
img2 = Image.fromarray(np.uint8(b))
img2.save('stop_r10_mono.jpg')

The result obtained is like this.

stop_r10_mono.jpg

Color version

The above is grayscale for simplicity, but if you SVD each element of RGB, you can also make a color version. The only addictive point here is that in the monochrome version, I used Image.fromarray as it is, but in color, I have to make it an array of tuples (r, g, b). The object that can be created by multiplying the matrix of numpy is a Matrix object, so in order to access it as an array after that, you have to return it to an array with np.asarray. If you pay attention to that, you can do it right away.

Create a function of low-rank approximation of a matrix like this.

def perform_svd(a,rank):
    u, s, v = linalg.svd(a)
    ur = u[:, :rank]
    sr = np.matrix(linalg.diagsvd(s[:rank], rank,rank))
    vr = v[:rank, :]
    return np.asarray(ur*sr*vr)

The point is that the return value is an array with asarray. With this, you can easily make a color version.

    w = img.width
    h = img.height
    A = np.asarray(img)
    r = perform_svd(A[:,:,0],rank).reshape(w*h)
    g = perform_svd(A[:,:,1],rank).reshape(w*h)
    b = perform_svd(A[:,:,2],rank).reshape(w*h)
    B = np.asarray([r,g,b]).transpose(1,0).reshape(h,w,3)
    img2 = Image.fromarray(np.uint8(B))
    img2 = Image.fromarray(np.uint8(data))
    img2.save(`stop_r10.jpg`)

If you create a numpy array from image data, it will be a three-dimensional array of (height, width, color), so if you SVD each color and attach it, it will be an array of (color, height * width). So, after replacing it with transpose, reshape it to the original 3D array shape and plunge it into ʻImage.fromarray`.

The resulting image looks like this.

stop_r10.jpg

in conclusion

With this image, if you take 20 ranks, you can almost restore the original image. It would be fun to find out how much to take and what kind of image has what kind of spectrum.

No, but if you read the image, make it grayscale, make it into a matrix, SVD it, perform low rank approximation, return it to the image and save it, and import the appropriate library with Python, you can do it in 15 lines. I see ... I think that "the richness of the library is the strength of the language" ...

Recommended Posts

Low-rank approximation of images by singular value decomposition
Low-rank approximation of images by Tucker decomposition
Low-rank approximation of images by HOSVD step by step
Low-rank approximation of images by HOSVD and HOOI
Singular value decomposition (SVD), low-rank approximation (LRA) in Python
Try singular value decomposition of the daimyo matrix
Try singular value decomposition with Python
Story of power approximation by Python
Face detection by collecting images of Angers.
Classification of guitar images by machine learning Part 1
Classification of guitar images by machine learning Part 2
Search by the value of the instance in the list
Optical Flow, the dynamics of images captured by OpenCV
I compared the identity of the images by Hu moment