How do I apply a DCT to an image in Python? - python

I want to apply a Discrete Cosine Transform (as well as the inverse) to an image in Python and I'm wondering what is the best way to do it and how. I've looked at PIL and OpenCV but I still don't understand how to use it.

Example with scipy.fftpack:
from scipy.fftpack import dct, idct
# implement 2D DCT
def dct2(a):
return dct(dct(a.T, norm='ortho').T, norm='ortho')
# implement 2D IDCT
def idct2(a):
return idct(idct(a.T, norm='ortho').T, norm='ortho')
from skimage.io import imread
from skimage.color import rgb2gray
import numpy as np
import matplotlib.pylab as plt
# read lena RGB image and convert to grayscale
im = rgb2gray(imread('images/lena.jpg'))
imF = dct2(im)
im1 = idct2(imF)
# check if the reconstructed image is nearly equal to the original image
np.allclose(im, im1)
# True
# plot original and reconstructed images with matplotlib.pylab
plt.gray()
plt.subplot(121), plt.imshow(im), plt.axis('off'), plt.title('original image', size=20)
plt.subplot(122), plt.imshow(im1), plt.axis('off'), plt.title('reconstructed image (DCT+IDCT)', size=20)
plt.show()
Also, if you plot a small slice of the 2D DCT coefficients array imF (in log domain), you will get a figure like the following (with a checkerboard pattern):

From OpenCV:
DCT(src, dst, flags) → None
Performs a forward or inverse Discrete Cosine transform of a 1D or 2D
floating-point array.
Parameters:
src (CvArr) – Source array, real 1D or 2D array
dst (CvArr) – Destination array of the same size and same type as the source
flags (int) –
Transformation flags, a combination of the following values
CV_DXT_FORWARD do a forward 1D or 2D transform.
CV_DXT_INVERSE do an inverse 1D or 2D transform.
CV_DXT_ROWS do a forward or inverse transform of every individual row of
the input matrix. This flag allows user to transform multiple vectors simultaneously
and can be used to decrease the overhead (which is sometimes several times larger
than the processing itself), to do 3D and higher-dimensional transforms and so forth.
Here is an example of it being used.
The DCT is also available in scipy.fftpack.

Related

Why does the scipy.ndimage.gaussian_filter changes the shape of my masked image?

I would like to apply a Gaussian filter to a masked array, however, this appears to alter the shape of my image. The higher I put sigma, the more of the image disappears. I don't really understand why this happens? I would expect both figures to have the same shape.
## Filtering with a Gaussian
x = np.log10(np.abs(PV))
# PV is a (50,2041) array and represents potential vorticity
# along a latitudinal transect in the ocean
from scipy.ndimage import gaussian_filter
y = gaussian_filter(x, 3)
Original and Gaussian filtered transect:

calculating eigenvalue of an image

I have some images and would like to look at the eigenvalues of the images (as image is a matrix). My issue is that the image is in the shape of TensorShape([577, 700, 3])
How can I possibly to some preprocessing to be able to have its eigen decomposition?
My try:
import tensorflow as tf
import numpy as np
from numpy import linalg as LA
import matplotlib.pyplot as plt
image_path = tf.keras.utils.get_file('YellowLabradorLooking_new.jpg', 'https://storage.googleapis.com/download.tensorflow.org/example_images/YellowLabradorLooking_new.jpg')
image_raw = tf.io.read_file(image_path)
image = tf.image.decode_image(image_raw)
image = tf.cast(image, tf.float32)
image = tf.image.resize(image, (224, 224))
LA.eig(image)
If you have n images, and if images are of the same size, and if images are somehow centered, then you may consider that images are samples from a distribution, and you can use eigenvalue decomposition to study how different pixels in the image vary across the collection.
In this situation: say you have a collection of n [H,W] images. You can flatten images and form a [H*W, n] matrix. If the images are RGB, it can be a [H*W*3, n] array -- i.e. each pixel location and each color channel is treated as an independent dimension.
Eigenvalue decomposition will give you a collection of H*W*3-dimensional vectors, which can be reshaped back into RGB images. Getting all eigenvectors is going to be impossible (H*W*3*H*W*3 is usually huge), however calculating top 3-5 eigenvalues and eigenvectors shouldn't be a problem even if HxWx3 is large.
You can find a more detailed description searching for "Eigenfaces"; e.g. opencv-eigenfaces-for-face-recognition, wikipedia, classic CVPR91 paper, etc.
A grayscale image can be (and usually is) represented as a matrix. A colored image can not. It is represented using three matrices, one for each color channel.
This is the problem with your code snippet. la.eig() expects a square array, or an array containing square arrays in its final two axes, but got an array of shape (224, 224, 3).
To fix this, you can shift the two 224 axes to the end using the np.rollaxis() function. The eigenvalues and -vectors will be calculated separately for each color channel.

How can I prevent Numpy/ SciPy gaussian blur from converting image to grey scale?

I want to perform gaussian blur on an image but I don't want to be convert to grey scale. Is there anyway to perform this operation and keep the color?
from scipy import misc
import scipy
import numpy as np
a = misc.imread('A.jpg')
# A retains its color
misc.imsave('color.jpg', a)
# A_G_Blur gets converted to grey scale, I want to prevent this
a_g_blure = ndimage.uniform_filter(a, size=11)
# I want it to keep it's color
misc.imsave('now_grey.jpg', a)
a is a 3-d array with shape (M, N, 3). The problem is that ndimage.uniform_filter(a, size=11) applies a filter with length 11 to each dimension of a, include the third axis that holds the color channels. When you apply the filter with length 11 to an axis with length 3, the resulting values are all pretty close to the average of the three values, so you get something pretty close to a gray scale. (Depending on the image, you might have some color left.)
What you actually want is to apply a 2-d filter to each color channel separately. You can do this by giving a tuple as the size argument, using a size of 1 for the last axis:
a_g_blure = ndimage.uniform_filter(a, size=(11, 11, 1))
Note: uniform_filter is not a Gaussian blur. For that, you would use scipy.ndimage.gaussian_filter. You might also be interested in the filters provided by scikit-image. In particular, see skimage.filters.gaussian_filter.
For a gaussian blur, I recommend using skimage.filters.gaussian_filter.
from skimage.io import imread
from skimage.filters import gaussian_filter
sigma=5 # blur radius
img = imread('path/to/img')
# this will only return grayscale
grayscale_blur = gaussian_filter(src_img, sigma=sigma)
# passing multichannel param as True returns colors
color_blur = gaussian_filter(src_img, sigma=sigma, multichannel=True)

imnoise in Python?

I'm trying to add white noise to an image after applying a lowpass filter. I know how to do it in matlab but don't know what to call for it to work in python.
import matplotlib.pyplot as plt
import numpy as np
import scipy.misc
from scipy import ndimage
import Image
J = imnoise(im,'salt & pepper',0.02);
figure.imshow(J)
What else do I need to import? Or is there another way to add noise?
scikit-image provides a function random_noise which is similar to imnoise in MATLAB.
skimage.util.random_noise(image, mode='gaussian', seed=None, clip=True, **kwargs)
It supports the following modes:
‘gaussian’ Gaussian-distributed additive noise.
‘localvar’ Gaussian-distributed additive noise, with specified
local variance at each point of image
‘poisson’ Poisson-distributed noise generated from the data.
‘salt’ Replaces random pixels with 1.
‘pepper’ Replaces random pixels with 0.
‘s&p’ Replaces random pixels with 0 or 1.
‘speckle’ Multiplicative noise using out = image + n*image, where
n is uniform noise with specified mean & variance.
Note that one difference from imnoise in MATLAB is that the output of this function would always be a floating-point image.
If the input image is a uint8 grayscale image for instance, it would be converted to float at first, but the output image wouldn't be converted to the same class as the input image.
Therefore if you care about the class of image, you should convert the output by yourself, for example using skimage.img_as_ubyte.
in discussing denoising, this tutorial adds white noise to an image with noisy = l + 0.4 * l.std() * np.random.random(l.shape)
where l is the image.
http://scipy-lectures.github.io/advanced/image_processing/#denoising
In general, you should be able to add noise simply by adding a matrix filled with the noise that you want to use to the original picture.
code sample.
import numpy as np
import cv2
from matplotlib import pyplot as plt
from skimage.util import random_noise
I = cv2.imread('image.jpg', 1); # 1/ -1: color mode; 0: gray mode
gauss = random_noise(I, mode='gaussian', seed=None, clip=True)
sp = random_noise(I, mode='s&p', seed=None, clip=True)
plt.subplot(231), plt.imshow(I), plt.title('Origin')
plt.subplot(232), plt.imshow(gauss), plt.title('Gaussian')
plt.subplot(233), plt.imshow(sp), plt.title('Salt & Pepper')
plt.show();
more info: http://scikit-image.org/docs/0.13.x/api/skimage.util.html#skimage.util.random_noise
Although there is no built-in functions like in matlab
"imnoise(image,noiseType,Amount_of_Noise)" but we can easily add required amount of random valued impulse noise or salt and pepper into an image manually.
1. to add random valued impulse noise.
import random as r
def addRvinGray(image,n): # add random valued impulse noise in grayscale
'''parameters:
image: type=numpy array. input image in which you want add noise.
n: noise level (in percentage)'''
k=0 # counter variable
ih=image.shape[0]
iw=image.shape[1]
noisypixels=(ih*iw*n)/100 # here we calculate the number of pixels to be altered.
for i in range(ih*iw):
if k<noisypixels:
image[r.randrange(0,ih)][r.randrange(0,iw)]=r.randrange(0,256) #access random pixel in the image gives random intensity (0-255)
k+=1
else:
break
return image
> to add salt and pepper noise
import random as r
def addSaltGray(image,n): #add salt-&-pepper noise in grayscale image
k=0
salt=True
ih=image.shape[0]
iw=image.shape[1]
noisypixels=(ih*iw*n)/100
for i in range(ih*iw):
if k<noisypixels: #keep track of noise level
if salt==True:
image[r.randrange(0,ih)][r.randrange(0,iw)]=255
salt=False
else:
image[r.randrange(0,ih)][r.randrange(0,iw)]=0
salt=True
k+=1
else:
break
return image
Note: for color images: first split image in to three or four
channels depending on the input image using opencv function: (B, G, R)
= cv2.split(image) (B, G, R, A) = cv2.split(image) after spliting perform the same operations on all channels. at the end merge all the
channels: merged = cv2.merge([B, G, R]) return merged I hope this will help someone.

How to convert a NumPy array to PIL image applying matplotlib colormap

I have a simple problem, but I cannot find a good solution to it.
I want to take a NumPy 2D array which represents a grayscale image, and convert it to an RGB PIL image while applying some of the matplotlib colormaps.
I can get a reasonable PNG output by using the pyplot.figure.figimage command:
dpi = 100.0
w, h = myarray.shape[1]/dpi, myarray.shape[0]/dpi
fig = plt.figure(figsize=(w,h), dpi=dpi)
fig.figimage(sub, cmap=cm.gist_earth)
plt.savefig('out.png')
Although I could adapt this to get what I want (probably using StringIO do get the PIL image), I wonder if there is not a simpler way to do that, since it seems to be a very natural problem of image visualization. Let's say, something like this:
colored_PIL_image = magic_function(array, cmap)
Quite a busy one-liner, but here it is:
First ensure your NumPy array, myarray, is normalised with the max value at 1.0.
Apply the colormap directly to myarray.
Rescale to the 0-255 range.
Convert to integers, using np.uint8().
Use Image.fromarray().
And you're done:
from PIL import Image
from matplotlib import cm
im = Image.fromarray(np.uint8(cm.gist_earth(myarray)*255))
with plt.savefig():
with im.save():
input = numpy_image
np.uint8 -> converts to integers
convert('RGB') -> converts to RGB
Image.fromarray -> returns an image object
from PIL import Image
import numpy as np
PIL_image = Image.fromarray(np.uint8(numpy_image)).convert('RGB')
PIL_image = Image.fromarray(numpy_image.astype('uint8'), 'RGB')
The method described in the accepted answer didn't work for me even after applying changes mentioned in its comments. But the below simple code worked:
import matplotlib.pyplot as plt
plt.imsave(filename, np_array, cmap='Greys')
np_array could be either a 2D array with values from 0..1 floats o2 0..255 uint8, and in that case it needs cmap. For 3D arrays, cmap will be ignored.

Categories