ESLearner
ESLearner

Reputation: 87

Python Code - SVD with numpy

I would like to get some help with my code on Python. I am a novice to Python.

At high level - I read a (.png) file from command line , put into original array , compute svd , find high rank of svd based on command line , multiple with original array and then finally put the file and the array out.

My issue : The generated file is distorted and does not look like the real picture i intended to generate.

My question : I have put the snippet of code I am using , can you please point to what I am doing incorrectly ?

import sys
import os
import numpy
import numpy.linalg
import scipy.misc

def getOutputPngName(path, rank):
    filename, ext = os.path.splitext(path)
    return filename + '.' + str(rank) + '.png'

def getOutputNpyName(path, rank):
    filename, ext = os.path.splitext(path)
    return filename + '.' + str(rank) + '.npy'

if len(sys.argv) < 3:
    sys.exit('usage: task1.py <PNG inputFile> <rank>')

    inputfile = sys.argv[1]
    rank = int(sys.argv[2])
    outputpng = getOutputPngName(inputfile, rank)
    outputnpy = getOutputNpyName(inputfile, rank)

# Import pic.png into array im as command parameter
img = scipy.misc.imread(inputfile)

# Perform SVD on im and obtain individual matrices
P, D, Q = numpy.linalg.svd(img, full_matrices=False)

# Compute overall SVD matrix based on individual matrices
svd_decomp = numpy.dot(numpy.dot(P, numpy.diag(D)), Q)

# Keep Top entries in svd_decomp
initial = svd_decomp.argsort()
temp = numpy.array(initial)
svd_final = numpy.argpartition(temp,-rank)[-rank:]

# Multiply to obtain the best rank-k approximation of the original array 
img = numpy.transpose(img)
final = (numpy.dot(svd_final,img))

#Saving the approximated array as a binary array file(1) and as a PNG file(2)
numpy.save(outputnpy, final)
scipy.misc.imsave(outputpng, final)

Upvotes: 1

Views: 4163

Answers (2)

Vikalp Veer
Vikalp Veer

Reputation: 437

No Need to sort. Just compute your matrix over rank

svd_decomp = np.zeros((len(P), len(Q)))
for i in range(rank):
    svd_decomp += D[i] * np.outer(P.T[i], Q[i])

Upvotes: 0

Cihan
Cihan

Reputation: 2307

The biggest issue is the svd_decomp.argsort(). argsort() without any arguments flattens out the whole matrix and sorts it like that, it's not what you want to do.

In fact, you don't need to do any sorting, because linalg's svd() function does it for you. See the documentation.

The singular values for every matrix, sorted in descending order.

So you just have to do the following

import sys
import os
import numpy
import numpy.linalg
import scipy.misc

def getOutputPngName(path, rank):
    filename, ext = os.path.splitext(path)
    return filename + '.' + str(rank) + '.png'

def getOutputNpyName(path, rank):
    filename, ext = os.path.splitext(path)
    return filename + '.' + str(rank) + '.npy'

if len(sys.argv) < 3:
    sys.exit('usage: task1.py <PNG inputFile> <rank>')

inputfile = sys.argv[1]
rank = int(sys.argv[2])
outputpng = getOutputPngName(inputfile, rank)
outputnpy = getOutputNpyName(inputfile, rank)

# Import pic.png into array im as command parameter
img = scipy.misc.imread(inputfile)

# Perform SVD on im and obtain individual matrices
P, D, Q = numpy.linalg.svd(img, full_matrices=True)

# Select top "rank" singular values
svd_decomp = numpy.matrix(P[:, :rank]) * numpy.diag(D[:rank]) * numpy.matrix(Q[:rank, :])

# Save the output
numpy.save(outputnpy, svd_decomp)
scipy.misc.imsave(outputpng, svd_decomp)

Notice that all we do is select "rank" singular values, no need to sort.

Example outputs:

Base Image:

enter image description here

Rank = 1

enter image description here

Rank = 10

enter image description here

Upvotes: 3

Related Questions