I have a numpy array A of shape (512, 512, 4)
Each element is a tuple: (r, g, b, a). It represents a 512x512 RGBA image.
I have a numpy array B of shape (512, 512, 3)
Each element is a tuple: (r, g, b). It represents a similar, RGB image.
I want to fast copy all the 'a' (alpha) values from each element of A into corresponding elements in B. (basically transferring the alpha channel).
resulting B shape would be (512, 512, 4).
How can I achieve this? The algorithm is based on fast pixel manipulation technique laid out here.
Code:
## . input_image is loaded using PIL/pillow
rgb_image = input_image
print(f"Image: {rgb_image}")
rgb_image_array = np.asarray(rgb_image) # convert to numpy array
print(f"Image Array Shape: {rgb_image_array.shape}")
gray_image = rgb_image.convert("L") # convert to grayscale
print(f"Gray image: {gray_image}")
gray_image_array = np.asarray(gray_image)
print(f"Gray image shape: {gray_image_array.shape}")
out_image_array = np.zeros(rgb_image_array.shape, rgb_image_array.dtype)
print(f"Gray image array shape: {out_image_array.shape}")
rows, cols, items = out_image_array.shape
# create lookup table for each gray value to new rgb value
LUT = []
for i in range(256):
color = gray_to_rgb(i / 256.0, positions, colors)
LUT.append(color)
LUT = np.array(LUT, dtype=np.uint8)
print(f"LUT shape: {LUT.shape}")
# get final output that uses lookup table technique.
# notice that at this point, we don't have the alpha channel
out_image_array = LUT[gray_image_array]
print(f"output image shape: {out_image_array.shape}")
# How do I get the alpha channel back from rgb_image_array into out_image_array
Output:
Image: <PIL.Image.Image image mode=RGBA size=512x512 at 0x7FDEF5F2F438>
Image Array Shape: (512, 512, 4)
Gray image: <PIL.Image.Image image mode=L size=512x512 at 0x7FDEF5C25CF8>
Gray image shape: (512, 512)
Gray image array shape: (512, 512, 4)
LUT shape: (256, 3)
output image shape: (512, 512, 3)
Using numpy slices:
import numpy as np
A = [[(1,1,1,4)], [(1,1,1,5)]]
B = [[(2,2,2)], [(3,3,3)]]
# A and B are tensors of order 3
A = np.array(A)
B = np.array(B)
print("A=")
print(A)
print("B=")
print(B)
C = np.copy(A)
# assign along all 1st and 2nd dimensions, but only the first three elements of the third dimension
C[:,:,0:3] = B
print("C=")
print(C)
Output:
A=
[[[1 1 1 4]]
[[1 1 1 5]]]
B=
[[[2 2 2]]
[[3 3 3]]]
C=
[[[2 2 2 4]]
[[3 3 3 5]]]
Let's be careful about terminology
I have a numpy array A of shape (512, 512, 4) Each element is a tuple: (r, g, b, a). It represents a 512x512 RGBA image.
If A has that shape, and has a numeric dtype (e.g. np.int32), then it has 512*512*4 elements. The only way it can have a tuple element is if the dtype was object. I suspect rather that you have a 512x512 image where each pixel is represented by 4 values.
A[0,0,:]
will be a (4,) shape array representing those 4 values (sometimes called channels) of one pixel.
A[:,:,0]
is the r value for the whole image.
If they really are 3d arrays, then #mocav's solution of copying columns (indexing on the last dimension) to a new array is the right one.
Another possibility is that they are structured 2d arrays with 4 and 3 fields respectively. That would print (str) as tuples, though the repr print will make the compound dtype explicit. But the solution will be similar - make a new array of the right shape and dtype (like A), and copy values by field name from B and A. (I'll wait with details until you clarify the situation).
Related
I'm trying to calculate image histograms of an numpy array of images. The array of images is of shape (n_images, width, height, colour_channels) and I want to return an array of shape (n_images, count_in_each_bin (i.e. 255)). This is done via two intermediary steps of averaging each colour channel for each image and then flattening each 2D image to a 1D one.
I think have successfully done this with the code below, however I have cheated a bit with the for loop at the end. My question is this - is there a way of getting rid of the last for loop and using an optimised numpy function instead?
def histogram_helper(flattened_image: np.array) -> np.array:
counts, _ = np.histogram(flattened_image, bins=[n for n in range(0, 256)])
return counts
# Using 10 RGB images of width and height 300
images = np.zeros((10, 300, 300, 3))
# Take the mean of the three colour channels
channel_avg = np.mean(images, axis=3)
# Flatten each image in the array of images, resulting in a 1D representation of each image.
flat_images = channel_avg.reshape(*channel_avg.shape[:-2], -1)
# Now calculate the counts in each of the colour bins for each image in the array.
# This will provide us with a count of how many times each colour appears in an image.
result = np.empty((0, len(self.histogram_bins) - 1), dtype=np.int32)
for image in flat_images:
colour_counts = self.histogram_helper(image)
colour_counts = colour_counts.reshape(1, -1)
result = np.concatenate([result, colour_counts])
You don't necessarily need to call np.histogram or np.bincount for this, since pixel values are in the range 0 to N. That means that you can treat them as indices and simply use a counter.
Here's how I would transform the initial images, which I imaging are of dtype np.uint8:
images = np.random.randint(0, 255, size=(10, 5, 5, 3)) # 10 5x5 images, 3 channels
reshaped = np.round(images.reshape(images.shape[0], -1, images.shape[-1]).mean(-1)).astype(images.dtype)
Now you can simply count the histograms using unbuffered addition with np.add.at:
result = np.zeros((images.shape[0], 256), int)
index = np.arange(len(images))[:, None]
np.add.at(result, (index, reshaped), 1)
The last operation is in-place and therefore returns None, but the answer will be in result nevertheless.
So I am trying to write a function that converts RGB to HSI on Python.
I have an image that is saved in np.ndarray (tensor I suppose?) with dimensions (1080, 1920, 3), that is - 1080x1920 pixels in RGB. How can I extract matrix of R/G/B; after I get H/S/I, how do I concatenate the matrices to get back the tensor (1080, 1920, 3)?
Assuming your_image contains the RGB channels, you can extract each channel using the corresponding index:
r = your_image[..., 0]
g = your_image[..., 1]
b = your_image[..., 2]
Note: You may need to normalize the values to the interval [0.0, 1.0]. If so, divide them by 255.0.
Conversely, you can stack the three channels together as follows:
import numpy as np
your_image = np.dstack((r, g, b))
I have an 640x480 RGB image with numpy shape (480, 640, 3) -- 3 for the R, G, B channels
and I would like to at each pixel, transform the RGB value according to:
# for each pixel
for i in range(img.shape[0]):
for j in range(img.shape[1]):
# set the new RGB value at that pixel to
# (new RGB vector) = (const matrix A) * (old RGB vector) + (const vector B)
img[i,j,:] = np.reshape((np.matmul(A, np.expand_dims(img[i,j,:], axis = 1)) + B), (3,))
Where A is of shape (3,3) and B is of shape (3,1).
How do I do this with numpy without writing a loop over the pixels?
One way would be with np.einsum -
img = np.einsum('ij,klj->kli',A,data) + B
Another with np.tensordot -
img = np.tensordot(data,A,axes=(-1,-1)) + B
Related - Understanding tensordot
I assumed B to be 1D array. If not so, use B.ravel() in place of B.
I have 3 (512×512) numpy arrays representing the Hue, Saturation and Value channels of my desired HSV image, containing float values.
How do I construct a single 512×512 image from these 3 numpy arrays?
To create an HSV image from the 3 channels, you can put them in a numpy array and transpose it to convert the shape from (3, 512, 512) to (512, 512, 3):
hsv = np.transpose([h, s, v], (1, 2, 0))
With OpenCV you can also use cv2.merge.
I have a numpy array with a shape like this
x.shape
(100, 1, 300, 300)
Think of this as 100 observations of grayscale images of size 300x300.
Grayscale images have only 1 channel, hence the second 1 in the shape.
I want to convert this to an array of RGB images, with 3 channels.
I want to just copy the grayscale image to the two other channels.
So the final shape would be (100, 3, 300, 300)
How can I do that?
Use np.repeat -
np.repeat(x,3,axis=1)
Sample run -
In [8]: x = np.random.randint(11,99,(2,1,3,4))
In [9]: np.repeat(x,3,axis=1).shape
Out[9]: (2, 3, 3, 4)