Subtract 2D array from 4D array - python

I have a greyscale image, represented by a 2D array of integers, shape (1000, 1000).
I then use sklearn.feature_extraction.image.extract_patches_2d() to generate an array of 3x3 'patches' from this image, resulting in an array of shape (1000000, 3, 3), as there are 1 million 3x3 arrays for each pixel value in the original image.
I reshape this to (1000, 1000, 3, 3), which is a 1000x1000 array of 3x3 arrays, one 3x3 array for each pixel in the original image.
I now want to effectively subtract the 2D array from the 4D array. I have already found a method to do this, but I would like to make one using vectorisation.
I currently iterate through each pixel and subtract the value there from the 3x3 array at the same index. This is a little bit slow.
This is what currently loads images, formats the arrays before hand, and then performs this subtraction.
from PIL import Image, ImageOps
from skimage import io
from sklearn.feature_extraction import image
import numpy
jitter = 1
patchsize = (jitter*2)+1
#load image as greyscale image using PIL
original = load_image_greyscale(filename)
#create a padded version of the image so that 1000x1000 patches are made
#instead of 998x998
padded = numpy.asarray(ImageOps.expand(original,jitter))
#extract these 3x3 patches using sklearn
patches = image.extract_patches_2d(padded,(patchsize,patchsize))
#convert image to numpy array
pixel_array = numpy.asarray(original)
#then reshape the array of patches so it matches array_image
patch_array = numpy.reshape(patches, (pixel_array.shape[0],pixel_array.shape[1],patchsize,patchsize))
#create a copy for results
patch_array_copy = numpy.copy(patch_array)
#iterate over each 3x3 array in the patch array and subtract the pixel value
#at the same index in the pixel array
for x in range(pixel_array.shape[0]):
for y in range(pixel_array.shape[1]):
patch_array_copy[x,y] = patch_array[x,y] - pixel_array[x,y]
I would like a way to perform the final step in the for loop using matrix operations.
I would also like to extend this at some point to work with RGB images, effectively making it a subtraction of an array with shape(1000,1000,3) from an array with shape(1000,1000,3,3,3). But i'm trying to go one step at a time here.
Any help or tips or suggestions or links to helpful resources would be greatly appreciated.

Related

Shuffle the Position of Pixel in image to get shuffled image and revert it back to get original image

I am trying to shuffle the pixel positions in image to get encrypted(distorted) image and decrypt the image using the original position in python. This is what i got from GPT and the shuffled images appear to be black.
from PIL import Image
import numpy as np
# Load the image
img = Image.open('test.png')
# Convert the image to a NumPy array
img_array = np.array(img)
# Flatten the array
flat_array = img_array.flatten()
# Create an index array that records the original pixel positions
index_array = np.arange(flat_array.shape[0])
# Shuffle the 1D arrays using the same random permutation
shuffled_index_array = np.random.permutation(index_array)
shuffled_array = flat_array[shuffled_index_array]
# Reshape the shuffled 1D array to the original image shape
shuffled_img_array = shuffled_array.reshape(img_array.shape)
# Convert the NumPy array to PIL image
shuffled_img = Image.fromarray(shuffled_img_array)
# Save the shuffled image
shuffled_img.save('shuffled_image.png')
# Save the shuffled index array as integers to a text file
np.savetxt('shuffled_index_array.txt', shuffled_index_array.astype(int), fmt='%d')
# Load the shuffled index array from the text file
shuffled_index_array = np.loadtxt('shuffled_index_array.txt', dtype=int)
# Rearrange the shuffled array using the shuffled index array
reshuffled_array = shuffled_array[shuffled_index_array]
# Reshape the flat array to the original image shape
reshuffled_img_array = reshuffled_array.reshape(img_array.shape)
# Convert the NumPy array to PIL image
reshuffled_img = Image.fromarray(reshuffled_img_array)
# Save the reshuffled image
reshuffled_img.save('reshuffled_image.png')
I'm trying to shuffle the pixel positions in an image but im stuck with what is wrong going on here.
You are really just missing a reversion of the permutation performed by numpy in the line np.random.permutation(index_array). That can be obtained by changing the line creating the reshuffled array to the following
# Rearrange the shuffled array using the shuffled index array
reshuffled_array = shuffled_array[np.argsort(shuffled_index_array)]
An explanation for reversion can be found here: Inverting permutations in Python

Convert 1D Numpy Array into a 1D image using PIL

PIL returns IndexError: tuple index out of range when converting a 1D numpy array into an PIL image object.
I am trying to covert a 1D Numpy Array of length 2048 having value between 0 and 255 into an image using PIL. I think this is an issue with my array being 1D. I have also tried converting a random 1D array integer to an image and I get the same error.
Random integer example:
from PIL import Image
import numpy as np
arr = np.random.randint(255, size=(2048))
arr = arr.astype('uint8')
img = Image.fromarray(arr, 'L')
img.show()
I would expect the code to show an image of a singe line of pixels having varying shades of gray.
When I tried to run your code, the problem was just that your array was a 1D array. So try:
arr2d = arr.reshape(-1,1)
Image.fromarray(arr2d,'L').show()
The input array has to be 2D, even if one dimension is 1. You just need to decide if you want the image to be a horizontal or vertical row of pixels, and add a dimension when creating your array.
arr = np.random.randint(255, size=(2048, 1)) # vertical image
arr = np.random.randint(255, size=(2048, 1)) # horizontal image

Preserve 3-dimensionality when converting PIL image to numpy array

I have a list of PIL images: p0, p1, ..., p85999 (a total of 86000 of them). They are all RGB, of size 30x30px.
I need to convert them to normalized numpy arrays, I did the following:
[np.asarray(r).astype('float32') / 255.0) for r in images]
where r is a PIL image.
This gives an array of numpy arrays.
However, these arrays are sometimes of shape (30,30,3) and sometimes of shape (30,30).
I want them always to be of shape (30,30,3).
I'm guessing numpy does this for performance reasons (when RGB is not
needed, eg. white images?).
Anyway, how to get the desired result - get all numpy arrays to be of size (30,30,3)?
Also, ideally I would want my final numpy array to be of size (30, 30, 3, 86000). Is there a shortcut to create such an array straight from PIL images?
I'm guessing numpy does this for performance reasons
Numpy has nothing to do with it, this is your PIL Image having one channel only.
The simplest solution is to just convert everything to RGB:
ims = [np.asarray(r.convert('RGB')).astype('float32') / 255.0) for r in images]
If you then call np.asarray(ims), you'll obtain an array of shape [N,30,30,3] where N is the number of images, which you can then transpose to your desired ordering.

How can I take generate mean image of many 32x32 grayscale images in Python?

I have array are which is 50000x32x32. arr[i] stores the i-th grayscale image.
I want to compute the mean image of these images. I tried the following code(I got this code from stack overflow itself). This code was actually meant for RGB images.
I know, these changes of mine have a lot of mistakes, Apologies.
import os, numpy, PIL
from PIL import Image
# Access all PNG files in directory
allfiles=os.listdir(os.getcwd())
imlist=arr
N=len(imlist)
# Assuming all images are the same size, get dimensions of first image
w,h=Image.fromarray(imlist[0]).size
# Create a numpy array of floats to store the average (assume RGB images)
brr=numpy.zeros((h,w),numpy.float)
# Build up average pixel intensities, casting each image as an array of floats
for im in imlist:
imarr=numpy.array(Image.fromarray(im),dtype=numpy.float)
brr=brr+imarr/N
# Round values in array and cast as 8-bit integer
brr=numpy.array(numpy.round(arr),dtype=numpy.uint8)
# Generate, save and preview final image
out=Image.fromarray(brr,mode="L")
out.save("Average.png")
out.show()
Once you have your 5000 × 32 × 32 array, you can compute the mean image by using np.mean() with axis=0 (the first axis, which contains the collection of images). Let's make some random data:
import numpy as np
images = np.random.random((5000, 32, 32))
Now we can compute the mean image:
mean_image = images.mean(axis=0)
We can look at it with:
import matplotlib.pyplot as plt
plt.imshow(mean_image)
Which looks something like:

Python - get white pixels of image

I'm would like to go from an image filename to a list of coordinates of the white pixels in the image.
I know it involves PIL. I have tried using Image.load() but this doesn't help because the output is not indexable (to use in a for loop).
You can dump an image as a numpy array and manipulate the pixel values that way.
from PIL import Image
import numpy as np
im=Image.open("someimage.png")
pixels=np.asarray(im.getdata())
npixels,bpp=pixels.shape
This will give you an array whose dimensions will depend on how many bands you have per pixel (bpp above) and the number of rows times the number of columns in the image -- shape will give you the size of the resulting array. Once you have the pixel values, it ought to be straightforward to filter out those whose values are 255
To convert a numpy array back to an image use:
im=Image.fromarray(pixels)

Categories

Resources