Saving small numpy array as a large image - python

I have a small binary numpy array X
eg.
[[0,0,0,0,0],
[0,0,1,0,0],
[0,1,0,1,0],
[0,0,1,0,0],
[0,0,0,0,0]]
I save it to an image using
plt.imsave('file.png', X, cmap=cm.gray)
the only problem is that the image is tiny at 5x5 resolution. How could I increase the resolution of the image while still keeping the information in the image?

You can use PyPNG Library. It can be very simple with this library like
import png
png.from_array(X, 'L').save("file.png")
You can also use scipy like following
import scipy.misc
scipy.misc.imsave('file.png', X)

You can use Numpy to maximize the dimension of your array and increase the number of ones around each index respectively:
In [48]: w, h = a.shape
In [49]: new_w, new_h = w * 5, h * 5
In [50]: new = np.zeros((new_w, new_h))
In [51]: def cal_bounding_square(x, y, new_x, new_y):
x = x * 5
y = y * 5
return np.s_[max(0, x-5):min(new_x, x+5),max(0, y-5):min(new_y, y+5)]
....:
In [52]: one_indices = np.where(a)
In [53]: for i, j in zip(*one_indices):
slc = cal_bounding_square(i, j, new_w, new_h)
new[slc] = 1
....:

An image that has more pixels will hold more information, but the pixels could be redundant. You could make a larger image in which each rectangle is black or white:
your_data = [[0,0,0,0,0],
[0,0,1,0,0],
[0,1,0,1,0],
[0,0,1,0,0],
[0,0,0,0,0]]
def enlarge(old_image, horizontal_resize_factor, vertical_resize_factor):
new_image = []
for old_row in old_image:
new_row = []
for column in old_row:
new_row += column*horizontal_resize_factor
new_image += [new_row]*vertical_resize_factor
return new_image
# Make a 7x7 rectangle of pixels for each of your 0 and 1s
new_image = enlarge(your_data, 7, 7)

This problem can be solved using a simply numpy hack. Resize the array and fill it with zeros.
Consider X as your present numpy array
X = np.array([[0,0,0,0,0],
[0,0,1,0,0],
[0,1,0,1,0],
[0,0,1,0,0],
[0,0,0,0,0]])
Making a new array of zeros with the required dimensions
new_X = np.zeros((new_height,new_width))
Add your original array to it
new_X[:X.shape[0], :X.shape[1]] = X
new_X is required array, now save it using any method you like.

Related

Storing pixel values of a greyscale video ,averaging them and then showing the resulting video

`n = 3
array = np.ones((n,n)) / (n*n)
n = array.shape[0] * array.shape1
while(True):
ret, frame = cap.read()
if ret is True:
print("newframe")
gframe = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
dst = cv2.copyMakeBorder(gframe, 1, 1, 1, 1, borderType, None, None)
blur = cv2.blur(dst,(3,3))
if k == 1 :
lastframe = gframe
curframe = gframe
nextframe = gframe
newFrame = gframe
k = 0
else :
lf = ndimage.convolve(lastframe, array, mode='constant', cval= 0.0)
cf = ndimage.convolve(curframe, array, mode='constant', cval= 0.0)
nf = ndimage.convolve(nextframe, array, mode='constant', cval= 0.0)
lastframe = curframe
curframe = nextframe
nextframe = gframe
b = np.zeros((3, 528, 720))
b[0] = lf
b[1] = cf
b[2] = nf
result = np.mean(b, axis=0)
cv2.imshow('frame',result)
cv2.imshow('frame2',gframe)
`enter image description here
I am trying to add all pixel values of a 3x3 pixel and then average them. I need to do that for every pixel and every frame and replace the primary pixel with the averaged one. However the way i am trying to do it makes it really slow and not really accurate.
This sounds like a convolution.
import numpy as np
from scipy import ndimage
a = np.random.random((5, 5))
a
[[0.14742615 0.83548453 0.67433445 0.59162829 0.21160044]
[0.1700598 0.89074466 0.84155171 0.65092969 0.3842437 ]
[0.22662423 0.2266929 0.47757456 0.34480112 0.06261333]
[0.89402116 0.00101947 0.90503461 0.93112109 0.44817247]
[0.21788789 0.3338606 0.07323461 0.28944439 0.91217591]]
Convolution operation with window size 3x3
n = 3
k = np.ones((n, n)) / (n * n)
n = k.shape[0] * k.shape[1]
b = ndimage.convolve(a, k, mode='constant', cval=0.0)
b
[[0.22707946 0.39551126 0.49829704 0.3726987 0.2042669 ]
[0.27744803 0.49894366 0.61486021 0.47103081 0.24953517]
[0.26768469 0.51481368 0.58549664 0.56067136 0.31354238]
[0.21112292 0.37288334 0.39808704 0.4937969 0.33203648]
[0.16075435 0.26945093 0.28152386 0.39546479 0.28676821]]
Now you just have to do it for the current frame, and the two prior frames.
-------- EDIT: For three frames -----------
For 3D you could write a convolution function like in this post, but its quite complex as it uses FFTs
If you just want to average across three frames, you could do:
f1 = np.random.random((5, 5)) # Frame1
f2 = np.random.random((5, 5)) # Frame2
f3 = np.random.random((5, 5)) # Frame3
n = 3
k = np.ones((n, n)) / (n * n)
n = k.shape[0] * k.shape[1]
b0 = ndimage.convolve(f1, k, mode='constant', cval=0.0)
b1 = ndimage.convolve(f2, k, mode='constant', cval=0.0)
b2 = ndimage.convolve(f3, k, mode='constant', cval=0.0)
# Create a 3D Matrix, with each fame placed along the first dimension
b = np.zeros((3, 5, 5))
b[0] = b0
b[1] = b1
b[2] = b2
# Take the average across the first dimension (across frames)
result = np.mean(b, axis=0)
There probably is a more elegant solution than this, but it gets the job done.
-------- EDIT: For Movies -----------
Based on all the questions in the comments I've decided to attempt to add some more code to help with implementation.
Firstly I'm starting out with these 7 consecutive stills from a movie:
I have not verified that the following code is bug proof or actually returns the correct result.
import cv2
import numpy as np
from scipy import ndimage
# this is a function to do previous code
def mean_frames(frames, kernel):
b = np.zeros(frames.shape)
for i in range(frames.shape[0]):
b[i] = ndimage.convolve(frames[i], k, mode='constant', cval=0.0)
b = np.mean(b, axis=0) / frames.shape[0]
return b
mean_N = 3 # frames to average
# read in 1 file to get dimenions
im = cv2.imread(f'{root}1.png', cv2.IMREAD_GRAYSCALE)
# setup numpy matrix that will hold mean_N frames at a time
frames = np.zeros((mean_N, im.shape[0], im.shape[1]))
avg_frames = [] # list to store our 3 averaged frames
count = 0 # counter to position frames in 1st dim of 3D matrix for avg
k = np.ones((3, 3)) / (3 * 3) # kernel for 2D convolution
for j in range(1, 7): # 7 images
file_name = root + str(j) + '.png'
im = cv2.imread(file_name, cv2.IMREAD_GRAYSCALE)
frames[count, ::] = im # store in 3D matrix
# if loaded more than min req. for avg, we average
if j >= mean_N:
# average and store to list
avg_frames.append(mean_frames(frames, k))
# if the count is mean_N - 1, that means we need to replace
# the 0th matrix in frames so that we are doing a 'moving avg'
if count == (mean_N - 1):
count = 0
else:
count += 1 #increase position in 0th dim for 3D matrix storage
# ouput averaged frames
for i, f in enumerate(avg_frames):
cv2.imwrite(f'{path}output{i}.jpg', f)
Then looking at the folder, there are 5 files (as expected if we did a moving average of 3 frames over 7 stills:
looking at before and after:
Image 3:
and averaged image #1:
The image not only is in gray scale (as expected) but seems quite dark. Perhaps some brightening would make things look better/more apparent.
Your question is very interesting.
I saw that you use many loops for activating this function. Let's process analysis.
Just for a frame.
You want to add all pixel values of a 3x3 pixel neighborhood. So I think Image interpolation is very suitable for this case. In OpenCV, we use resize() to interpolate pixel for image. So the INTER_NEAREST is best for this situation.
This is the formula for INTER_NEAREST.
Now you get the pixel added image.
Then you want to do that for every pixel and every frame and replace the primary pixel with the average one. And I think the Average filtering is a better solution.
The filter will work every pixel.
The code of a temporary example.
Interpolation
img = cv2.resize(img, (img.size[0]*3, img.size[1]*3), cv2.INTER_NEAREST)
Filter
img = cv2.blur(img, (3, 3))

Downsample numpy image array in Python by averaging

How do I downsample an image of any resolution to a quarter of the size by averaging the pixels in numpy?
What I came up through research only works for images that are square (i.e 512 X 512 down to 128 X 128) but will not work for images that are different dimensions (i.e 2400 X 1800 down to 600 X 450). In those cases I get a IndexError: index 450 is out of bounds for axis 1 with size 450.
I am trying to perform this task with numpy array manipulation and without installing other packages and libraries.
I researched a function
numpy.mean()
but I don't know how to use it in reference to this problem.
import cv2
import numpy as np
def quarter_res_avg(im):
original_width = im.shape[1]
original_height = im.shape[0]
width = original_width / 4
height = original_height / 4
resized_image = np.zeros(shape=(width, height, 3), dtype=np.uint8)
scale = 4
for i in range(width):
for j in range(height):
temp = np.array([0, 0, 0])
for x in range(scale):
for y in range(scale):
temp += im[i*scale+x, j*scale+y]
resized_image[i, j] = temp/(scale*scale)
return resized_image
im = cv2.imread('Lenna_test_image.png', 1)
cv2.imwrite('Lenna_test_image_avg.png', quarter_res_avg(im))
Any ideas are much appreciated.
Thanks.
import numpy as np
import skimage.measure
your_array = np.random.rand(2400, 800)
new_array = skimage.measure.block_reduce(your_array, (4,4), np.mean)
print(new_array.shape)
Out[18]: (600, 450)
First reshape your M x N image into a (M//4) x 4 x (N//4) x 4 array, then use np.mean in the second and last dimensions.
from typing import Tuple
import numpy as np
def downsample_by_averaging(img: np.ndarray, window_shape: Tuple[int, int]) -> np.ndarray:
return np.mean(
img.reshape((
*img.shape[:-2],
img.shape[-2] // window_shape[-2], window_shape[-2],
img.shape[-1] // window_shape[-1], window_shape[-1],
)),
axis=(-1, -3),
)
downsample_by_averaging(img, (4, 4))
The answer that worked for me with the help from #MarkSetchell in the comments of the question.
Without using np.mean()
def quarter_res_avg(im):
original_width = im.shape[1]
original_height = im.shape[0]
width = original_width / 4
height = original_height / 4
resized_image = np.zeros(shape=(height, width, 3), dtype=np.uint8)
scale = 4
for i in range(height):
for j in range(width):
temp = np.array([0, 0, 0])
for x in range(scale):
for y in range(scale):
temp += im[i*scale + x, j*scale + y]
resized_image[i, j] = temp/(scale*scale)
return resized_image
im = cv2.imread('Lenna_test_image.png', 1)
cv2.imwrite('Lenna_test_image_resized.png', quarter_res_avg(im))
By using np.mean() replace the for loops with:
for i in range(0, original_height, scale):
for j in range(0, original_width, scale):
resized_image[i/scale, j/scale] = np.mean(im[i:i + scale, j:j+scale], axis=(0,1))

Numpy - Fill image with corrsp. I given array of [u,v, I]

I have a numpy array that has a N*3 matrix of [u, v, I]. Pixel positions and I intensity for that pixel.
I need to generate fill an image of the corresponding I from the set of pixels in that numpy array. Right now I have a for loop to do it but it is quite slow. What is a faster way to do this?
dmap_raw = np.zeros((raw_img_size[1], raw_img_size[0])).astype(np.float32)
for i in range(0, velodata_cam_proj.shape[0]):
u = velodata_cam_proj[i,0]
v = velodata_cam_proj[i,1]
Z = velodata_cam_proj[i,2]
dmap_raw[int(v),int(u)] = Z*100
Try this:
dmap_raw = np.zeros((raw_img_size[1], raw_img_size[0])).astype(np.float32)
u = velodata_cam_proj[:,0].astype('int')
v = velodata_cam_proj[:,1].astype('int')
Z = velodata_cam_proj[:,2]
dmap_raw[v, u] = Z*100

Image texture with skimage

I'm trying to get texture properties from a GLCM I created using greycomatrix from skimage.feature. My input data is an image with multiple bands and I want the texture properties for each pixel (resulting in an image with the dimensions cols x rows x (properties *bands)), as it can be achieved using ENVI. But I'm too new to this to come to grips with greycomatrix and greycoprops. This is what I tried:
import numpy as np
from skimage import io
from skimage.feature import greycomatrix, greycoprops
array = io.imread('MYFILE.tif')
array = array.astype(np.int64)
props = ['contrast', 'dissimilarity', 'homogeneity', 'energy', 'correlation', 'ASM']
textures = np.zeros((array.shape[0], array.shape[1], array.shape[2] * len(props)), np.float32)
angles = [0, np.pi / 4, np.pi / 2, 3 * np.pi / 4]
bands = array.shape[2]
for b in range(bands):
glcm = greycomatrix(array[:, :, b], [1], angles, np.nanmax(array) + 1,
symmetric=True, normed=True)
for p, prop in enumerate(props):
textures[:, :, b] = greycoprops(glcm, prop)
Unfortunately, this gives me a 1 x 4 matrix per prop, which I guess is one value per angle FOR THE WHOLE IMAGE, but this is not what I want. I need it per pixel, like contrast for each single pixel, computed from its respective surroundings. What am I missing?
This snippet should get the job done:
import numpy as np
from skimage import io, util
from skimage.feature.texture import greycomatrix, greycoprops
img = io.imread('fourbandimg.tif')
rows, cols, bands = img.shape
radius = 5
side = 2*radius + 1
distances = [1]
angles = [0, np.pi/2]
props = ['contrast', 'dissimilarity', 'homogeneity']
dim = len(distances)*len(angles)*len(props)*bands
padded = np.pad(img, radius, mode='reflect')
windows = [util.view_as_windows(padded[:, :, band].copy(), (side, side))
for band in range(bands)]
feats = np.zeros(shape=(rows, cols, dim))
for row in range(rows):
for col in range(cols):
pixel_feats = []
for band in range(bands):
glcm = greycomatrix(windows[band][row, col, :, :],
distances=distances,
angles=angles)
pixel_feats.extend([greycoprops(glcm, prop).ravel()
for prop in props])
feats[row, col, :] = np.concatenate(pixel_feats)
The sample image has 128 rows, 128 columns and 4 bands (click here to download). At each image pixel a square local neighbourhood of size 11 is used to compute the grayscale matrices corresponding to the pixel to the right and the pixel above for each band. Then, contrast, dissimilarity and homogeneity are computed for those matrices. Thus we have 4 bands, 1 distance, 2 angles and 3 properties. Hence for each pixel the feature vector has 4 × 1 × 2 × 3 = 24 components.
Notice that in order to preserve the number of rows and columns the image has been padded using the image itself mirrored along the edge of the array. It this approach does not fit your needs you could simply ignore the outer frame of the image.
As a final caveat, the code could take a while to run.
Demo
In [193]: img.shape
Out[193]: (128, 128, 4)
In [194]: feats.shape
Out[194]: (128, 128, 24)
In [195]: feats[64, 64, :]
Out[195]:
array([ 1.51690000e+04, 9.50100000e+03, 1.02300000e+03,
8.53000000e+02, 1.25203577e+01, 9.38930575e+00,
2.54300000e+03, 1.47800000e+03, 3.89000000e+02,
3.10000000e+02, 2.95064854e+01, 3.38267222e+01,
2.18970000e+04, 1.71690000e+04, 1.21900000e+03,
1.06700000e+03, 1.09729371e+01, 1.11741654e+01,
2.54300000e+03, 1.47800000e+03, 3.89000000e+02,
3.10000000e+02, 2.95064854e+01, 3.38267222e+01])
In [196]: io.imshow(img)
Out[196]: <matplotlib.image.AxesImage at 0x2a74bc728d0>
Edit
You could cast your data to the type required by greycomatrix through NumPy's uint8 or scikit-images's img_as_ubyte.

fastest way to select 7*7 neighbor pixels for every pixel in an image in Python

need to read an image as an array and for each pixel select 7*7 neighbor pixels then reshape it and put as a first row of training set:
import numpy as np
from scipy import misc
face1=misc.imread('face1.jpg')
face1 dimensions are (288, 352, 3) , need to find 7*7 neighbor pixels for every pixel , so 49*3 color then reshape it as a (1,147) array and stack it into an array for all pixels , i took the following approach:
X_training=np.zeros([1,147] ,dtype=np.uint8)
for i in range(3, face1.shape[0]-3):
for j in range(3, face1.shape[1]-3):
block=face1[i-3:i+4,j-3:j+4]
pxl=np.reshape(block,(1,147))
X_training=np.vstack((pxl,X_training))
resulting X_training shape is (97572, 147)
and as last row contains all zeros then:
a = len(X_training)-1
X_training = X_training[:a]
above code works well for one picture but with Wall time: 5min 19s i have 2000 images, so it will take ages to do it for all the images. I am looking for a faster way to iterate over every pixel and do the above task.
Edit:
this is what i mean by neighbor pixels , for every pixel face1[i-3 : i+4 ,j-3:j+4]
An efficient way is to use stride_tricks to create a 2d rolling window over the image, then flatten it out:
import numpy as np
face1 = np.arange(288*352*3).reshape(288, 352, 3) # toy data
n = 7 # neighborhood size
h, w, d = face1.shape
s = face1.strides
tmp = np.lib.stride_tricks.as_strided(face1, strides=s[:2] + s,
shape=(h - n + 1, w - n + 1, n, n, d))
X_training = tmp.reshape(-1, n**2 * d)
X_training = X_training[::-1] # to get the rows into same order as in the question
tmp is a 5D view into the image, where tmp[x, y, :, :, c] is equivalent to the neigborhood face1[x:x+n, y:y+n, c] in color channel c.
The following is < 1s on my laptop:
import scipy as sp
im = sp.rand(300, 300, 3)
size = 3
ij = sp.meshgrid(range(size, im.shape[0]-size), range(size, im.shape[1]-size))
i = ij[0].T.flatten()
j = ij[1].T.flatten()
N = len(i)
L = (2*size + 1)**2
X_training = sp.empty(shape=[N, 3*L])
for pixel in range(N):
si = (slice(i[pixel]-size, i[pixel]+size+1))
sj = (slice(j[pixel]-size, j[pixel]+size+1))
X_training[pixel, :] = im[si, sj, :].flatten()
X_training = X_training[-1::-1, :]
I'm always a bit sad when I can't think of one-line vectorized version, but at least it's faster for you.
Using scikit-image:
import numpy as np
from skimage import util
image = np.random.random((288, 352, 3))
windows = util.view_as_windows(image, (7, 7, 3))
out = windows.reshape(-1, 7 * 7 * 3)

Categories

Resources