I have a three dimensional array img of shape [1200,1600,3] and a two dimensional array labels of shape [1200,1600]. The first array is from an image, the second one is from labels in the image. Location [i,j] in the img array corresponds to an image pixel.
I want to create a new array of the same dimension as the img array, such that for the pixels with label 0, the original array is unchanged, but all other pixels are whitened (255,255,255).
The code I am using is:
import numpy as np
newimg=np.zeros((img.shape[0],img.shape[1],img.shape[2]))
for i in range(0,img.shape[0]):
for j in range(0,img.shape[1]):
if labels[i][j]==0:
newimg[i][j]=img[i][j]
else:
newimg[i][j]=np.array([255,255,255])
Is there a faster way of doing this?
Generally speaking, you'd do something similar to:
newimg = img.copy()
newimg[labels != 0, :] = 255
or alternatively:
newimg = np.where(labels[..., None] != 0, img, 255)
Related
I reshaped an image (included below) as a list of pixels, and now I want to remove the black ones (with value [255,255,255]). What is an efficient way to do it?
I tried using IM[IM != [255,255,255]] and I got a list of values, instead of a list of value triplets. Here is the code I'm using:
import cv2
import numpy as np
IM = cv2.imread('Test_image.png')
image = cv2.cvtColor(IM, cv2.COLOR_BGR2RGB)
# reshape the image to be a list of pixels
image_vec = np.array(image.reshape((image.shape[0] * image.shape[1], 3)))
image_clean = image_vec[image_vec != [255,255,255]]
print(image_clean)
The issue is that numpy automatically does array-boradcasting, so using IM != [255,255,255] will compare each element to [255,255,255] and return a boolean array with the same shape as the one with the image data. Using this as a mask will return the values as 1D array.
An easy way to fix this is to use np.all:
image_vec[~ np.all(image_vec == 255, axis=-1)]
I am working on an image processing/building problem. I have a smaller image that I want to place into a larger one. As normal the image is represented as a 3d array. This works fine with the following code (both element_pixels and image_pixels are 3d ndarrays with depth 3 representing RGB, element_pixels is equal to or smaller than image_pixels in the other dimensions):
element_pixels = element.get_pixels()
image_pixels[element.position[0]:element.position[0]+element.height, element.position[1]:element.position[1]+element.width, :] = element_pixels
However I want to treat black pixels in the element as transparent. The simplest way to do this seems to be to mask the element so I don't modify image_pixels where element_pixel is black. I tried the following, but I am tying myself in knots:
element_pixels = element.get_pixels()
b = np.all(element_pixels == [0, 0, 0], axis=-1)
black_pixels_mask = np.dstack([b,b,b])
image_pixels[element.position[0]:element.position[0]+element.height, element.position[1]:element.position[1]+element.width, :][black_pixels_mask] = element_pixels
This looks to be correctly generating a mask but I can't figure out how to use it. I get the following error:
image_pixels[element.position[0]:element.position[0]+element.height, element.position[1]:element.position[1]+element.width, :][black_pixels_mask] = element_pixels
TypeError: NumPy boolean array indexing assignment requires a 0 or 1-dimensional input, input has 3 dimensions
The masking kind-of works (i.e. runs without exceptions) if I replace the final = element_pixels with a constant, but I'm struggling to extrapolate this to a solution.
Extra detail of sizes
element_pixels.shape=(40, 40,3)
image_pixels.shape=(100, 100,3)
image_pixels[element.position[0]:element.position[0]+element.height, element.position[1]:element.position[1]+element.width, :].shape = (40,40,3)
A MRE in 2d
This captures what I'm trying to do without the complexity of the extra dimension.
import numpy as np
bg = np.ones((10,10))*0.5
img = np.concatenate([np.zeros((5,1)),np.ones((5,1))], axis=1)
mask = img == 0
# copy the *non-zero* pixel values of img to a particular location in bg
bg[5:10,5:7][mask] = img # this throws exception
print(bg)
I discovered after some experimentation that the (perhaps obvious in hindsight) answer is the you have to apply the mask to both sides.
So taking my MRE:
import numpy as np
bg = np.ones((10,10))*0.5
img = np.concatenate([np.zeros((5,1)),np.ones((5,1))], axis=1)
mask = img > 0
bg[5:10,5:7][mask] = img[mask]
print(bg)
Or going back to my original code, the only line that changes is:
image_pixels[element.position[0]:element.position[0]+element.height, element.position[1]:element.position[1]+element.width, :][~black_pixels_mask] = element_pixels[~black_pixels_mask]
Well you can use a 2d mask on a 3d array. So something like this will replace all black pixels of img with those of background.
img = np.random.randint(0, 2, (10, 10, 3))
background = np.random.randint(0, 2, (10, 10, 3))
mask = np.all(img == [0,0,0], axis=2)
img[mask] = background[img]
I'm not sure I understand what is in image_pixels but I think you can do something similar.
I want to make a simple Program which outputs a video as an Webcam, but the Cam wants a RGBA Numpy Array but I only have RGB from the video. How can I convert the 3 dimensional array to 4 dimensions?
You're actually not converting a 3-dimensional array to a 4-dimensional array. You're changing the size of one of the dimensions from three to four.
Lets say you have a NxMx3 image. You then need to:
temp = np.zeros((N, M, 4))
temp[:,:,0:3] = image
temp[:,:,3] = whatever default alpha you choose to use.
Generalize as you see fit.
Assuming your existing array is shaped (xsize, ysize, 3) and you want to create alpha as a 4th entry all filled with 1, you should be able to do something like
alpha = np.ones((*rgb.shape[0:2], 1))
rgba = np.concatenate((rgb, alpha), axis=2)
If you wanted a different uniform alpha value you could use np.full with that value instead of np.ones, but normally when converting RGB to RGBA you want fully opaque.
You can np.dstack your original im with np.ones(im.shape[:2])
new_im = np.dstack((im, np.ones(im.shape[:2])))
update: this is equivalent to #hobbs solution np.concatenate(..., axis=2)
Maybe try something like these: (import numpy as np)
arr # shape (n_bands, y_pixels, x_pixels)
swapped = np.moveaxis(arr, 0, 2) # shape (y_pixels, x_pixels, n_bands)
arr4d = np.expand_dims(swapped, 0) # shape (1, y_pixels, x_pixels, n_bands)
I have an aerial image:
I was able to get a binary image of the riverbed of the river part:
After applying a distance transform and some segmentation techniques I was able to get a binary image of the mean riverline:
My question is: how to overlay the white pixels from the riverline so that they're on "top" of the original image?
HereĀ“s an example:
This is a very simple way to solve your problem. But it works.
import cv2
original = cv2.imread('original.png') # Orignal image
mask = cv2.imread('line.png') # binary mask image
result = original.copy()
for i in range(original.shape[0]):
for j in range(original.shape[1]):
result[i, j] = [255, 255, 255] if mask[i, j][0] == 255 else result[i, j]
cv2.imwrite('result.png', result) # saves modified image to result.png
Result
Let's assume your images are numpy arrays called img and mask. Let's also assume that img has shape (M, N, 3), while mask has shape (M, N). Finally, let's assume that img is off dtype np.uint8 while mask is of type np.bool_. If the last assumption isn't true, start with
mask = mask.astype(bool)
Now you can set your river channel to 255 directly:
img[mask, :] = 255
If img were a single grayscale image without a third dimension, as in your last example, you would just remove the : from the index expression above. In fact, you could write it to work for any number of dimensions with
img[mask, ...] = 255
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