Given a Numpy array (actually a 3 channels image) I need to map a function on it, only where a triplette (aka RGB pixel) satisfies a predefined condition. All the rest should be kept untouched.
I know how to set a constant value when a pixel meets a certain condition, but I don't know how to apply a function having as parameter the value of such pixel.
For instance, the following example allows to set to 128 all the pixels that have all the channels greater than 128:
import numpy as np
L = 128
img = np.random.randint(0, 255, (5, 5, 3))
img[(img > L).all(axis=2)] = np.array([128, 128, 128])
But, what about if I have to set a value dependent on the current value of the pixel ?
The following code of course does not work:
import numpy as np
def smart_function(v):
return v//2
L = 128
img = np.random.randint(0, 255, (5, 5, 3))
img[(img > L).all(axis=2)] = smart_function(img)
I also tried with vectorize with no success:
import numpy as np
def smart_function(v):
return v//2
vf = np.vectorize(smart_function)
L = 128
img = np.random.randint(0, 255, (5, 5, 3))
img[(img > L).all(axis=2)] = vf(img)
Edit
To explain better my request, this is the expected behaviour written in plain Python. Obviously this code is very slow, so unusable, but it gives the idea:
for y in range(img.shape[0]):
for x in range(img.shape[1]):
pixel = img[y, x]
if pixel[0] > L and pixel[1] > L and pixel[2] > L:
img[y, x] = smart_function(pixel)
You could use frompyfunc with at:
import numpy as np
xs = np.random.randn(5, 5)
def f(x):
return np.round(x)
f = np.frompyfunc(f, nin=1, nout=1)
f.at(xs, xs > 0)
Related
lets assume we have a tensor representing an image of the shape (910, 270, 1) which assigned a number (some index) to each pixel with width=910 and height=270.
We also have a numpy array of size (N, 3) which maps a 3-tuple to an index.
I now want to create a new numpy array of shape (920, 270, 3) which has a 3-tuple based on the original tensor index and the mapping-3-tuple-numpy array. How do I do this assignment without for loops and other consuming iterations?
This would look simething like:
color_image = np.zeros((self._w, self._h, 3), dtype=np.int32)
self._colors = np.array(N,3) # this is already present
indexed_image = torch.tensor(920,270,1) # this is already present
#how do I assign it to this numpy array?
color_image[indexed_image.w, indexed_image.h] = self._colors[indexed_image.flatten()]
Assuming you have _colors, and indexed_image. Something that ressembles to:
>>> indexed_image = torch.randint(0, 10, (920, 270, 1))
>>> _colors = np.random.randint(0, 255, (N, 3))
A common way of converting a dense map to a RGB map is to loop over the label set:
>>> _colors = torch.FloatTensor(_colors)
>>> rgb = torch.zeros(indexed_image.shape[:-1] + (3,))
>>> for lbl in range(N):
... rgb[lbl == indexed_image[...,0]] = _colors[lbl]
I have the following code:
import numpy as np
def fill(arr1, arr2, arr3, arr4, thresh= 0.5):
out_arr = np.zeros(arr1.shape)
for i in range(0,len(arr1)):
arr1[i] = np.where(np.abs(arr1[i])<=thresh,np.nan,arr1[i])
mask = np.isnan(arr1[i])
arr1[i] = np.nan_to_num(arr1[i])
merged1 = (arr2[i]*mask)+arr1[i]
merged2 = np.where(np.abs(merged1)<=thresh,np.nan,merged1)
mask = np.isnan(merged2)
merged2 = np.nan_to_num(merged2)
merged3 = (arr3[i]*mask)+merged2
merged3 = np.where(np.abs(merged3)<=thresh,np.nan,merged3)
mask = np.isnan(merged3)
merged3 = np.nan_to_num(merged3)
merged4 = (arr4[i]*mask)+merged3
out_arr[i] = merged4
return(out_arr)
arr1 = np.random.rand(10, 10, 10)
arr2 = np.random.rand(10, 10, 10)
arr3 = np.random.rand(10, 10, 10)
arr4 = np.random.rand(10, 10, 10)
arr = fill(arr1, arr2, arr3, arr4, 0.5)
I wonder if there is a more efficient way of doing this maybe with masked arrays? Basically what I am doing is to replace values below the threshold in each layer of the 3D array with the next array, and this over 4 arrays. How would this look like for n arrays?
Thanks!
Your function can be simplified in several ways. In terms of efficiency, the most significant aspect is that you do not need to iterate over the first dimension, you can operate on the whole arrays directly. Besides that, you can refactor the replacement logic to something much simpler, and use a a loop to avoid repeating the same code over and over:
import numpy as np
# Function accepts as many arrays as wanted, with at least one
# (threshold needs to be passed as keyword parameter)
def fill(arr1, *arrs, thresh=0.5):
# Output array
out_arr = arr1.copy()
for arr in arrs:
# Replace values that are still below threshold
mask = np.abs(out_arr) <= thresh
out_arr[mask] = arr[mask]
return out_arr
Since thresh needs to be passed as keyword parameter in this function, you would call it as:
arr = fill(arr1, arr2, arr3, arr4, thresh=0.5)
The code works well but is very slow. How can I vectorize the color substitution to avoid usage of Python for loop?
processed_image = np.empty(initial_image.shape)
for i, j in np.ndindex(initial_image.shape[:2]):
l_, a, b = initial_image[i, j, :]
idx = mapping[a + 128, b + 128]
a, b = new_colors[tuple(idx)]
processed_image[i, j] = l_, a, b
I have an image initial_image in CIELAB space as numpy array of shape (some height, some width, 3). I need to produce a corrected image by changing a and b color components of image using mapping. mapping is a numpy array of shape (255, 255, 2). It gives me indices which can be used to get corrected a and b colors from new_colors. new_colors is of shape (table height, table width, 2).
Solutions that use scikit-image will also be helpful.
You can use advanced indexing:
# chain the two maps
chained = new_colors[(*np.moveaxis(mapping, 2, 0),)]
# split color channels
c1, *c23 = np.moveaxis(initial_image, 2, 0)
# add 128
c23 = *map(np.add, c23, (128, 128)),
# apply chained map
processed_image_2 = np.concatenate([c1[..., None], chained[c23]], axis=2)
This is my first nontrivial use of numpy, and I'm having some trouble in one spot.
So, I have colors, a (xsize + 2, ysize + 2, 3) ndarray, and newlife, a (xsize + 2, ysize + 2) ndarray of booleans. I want to add a random value between -5 and 5 to all three values in colors at all positions where newlife is true. In other words newlife maps 2D vectors to whether or not I want to add a random value to the color in colors at that position.
I've tried a million variations on this:
colors[np.nonzero(newlife)] += (np.random.random_sample((xsize + 2,ysize + 2, 3)) * 10 - 5)
but I keep getting stuff like
ValueError: operands could not be broadcast together with shapes (589,3) (130,42,3) (589,3)
How do I do this?
I think this does what you want:
# example data
colors = np.random.randint(0, 100, (5,4,3))
newlife = np.random.randint(0, 2, (5,4), bool)
# create values to add, then mask with newlife
to_add = np.random.randint(-5,6, (5,4,3))
to_add[~newlife] = 0
# modify in place
colors += to_add
This changes the colors in-place assuming uint8 dtype. Both assumptions are not essential:
import numpy as np
n_x, n_y = 2, 2
colors = np.random.randint(5, 251, (n_x+2, n_y+2, 3), dtype=np.uint8)
mask = np.random.randint(0, 2, (n_x+2, n_y+2), dtype=bool)
n_change = np.count_nonzero(mask)
print(colors)
print(mask)
colors[mask] += np.random.randint(-5, 6, (n_change, 3), dtype=np.int8).view(np.uint8)
print(colors)
The easiest way of understanding this is to look at the shape of colors[mask].
I am a beginner in opencv-python. I want to get all the X and Y coordinates of for the region of the interest mentioned in the code and store it in an array. Can anyone give me an idea on how to proceed? I was able to run the code, but is not showing any results.
Image for detecting all the X and Y coordinates
The sample code i wrote is written below,
import cv2
import numpy as np
import matplotlib.pyplot as plt
import imutils
img = cv2.imread("/home/harikrishnan/Desktop/image.jpg",0)
img1 = imutils.resize(img)
img2 = img1[197:373,181:300] #roi of the image
ans = []
for y in range(0, img2.shape[0]): #looping through each rows
for x in range(0, img2.shape[1]): #looping through each column
if img2[y, x] != 0:
ans = ans + [[x, y]]
ans = np.array(ans)
print ans
In your code you are using a for loop which is time consuming. You could rather make use of the fast and agile numpy library.
import cv2
import numpy as np
import matplotlib.pyplot as plt
import imutils
img = cv2.imread("/home/harikrishnan/Desktop/image.jpg",0)
img1 = imutils.resize(img)
img2 = img1[197:373,181:300] #roi of the image
indices = np.where(img2!= [0])
coordinates = zip(indices[0], indices[1])
I used the numpy.where() method to retrieve a tuple indices of two arrays where the first array contains the x-coordinates of the white points and the second array contains the y-coordinates of the white pixels.
indices returns:
(array([ 1, 1, 2, ..., 637, 638, 638], dtype=int64),
array([292, 298, 292, ..., 52, 49, 52], dtype=int64))
I then used the zip() method to get a list of tuples containing those points.
Printing coordinates gives me a list of coordinates with edges:
[(1, 292), (1, 298), (2, 292), .....(8, 289), (8, 295), (9, 289), (9, 295), (10, 288)]