Imagine the Image below to be a (64,64) 2D Numpy array. How do I make this Numpy array which has zeros (0) as a boundary to pick out a smaller region (Highlighted in Green). It does not matter which region it picks (as long as it is on the upper-middle-right region of the array). The final result should look like the image on the right where every other value is 0 apart from the ones highlighted in Green?
I don't know if I understand problem but you can create array 64,64 with zeros and copy green region from previous array.
To make it more visible I use array (10,10)
import numpy as np
#SIZE = 64
SIZE = 10
old_arr = np.random.randint(10, size=(SIZE, SIZE))
new_arr = np.zeros((SIZE, SIZE))
new_arr[3:5,4:6] = old_arr[3:5,4:6]
print(old_arr)
print(new_arr)
Related
I'm dealing with lots of image files - particularly with tissue samples. Often when you magnify the image and divide the image into tiles there are "blank" tiles. I need to identify these "blank" tiles and remove them. Unfortunately, these are not all one homogenous color, but you can see in my examples, I have one real tile (the obvious one) and the other three are "blank" (in quotes here because to the visual eye they are empty, but from a pixel perspective it's not a uniform value). What's the best way in Python (using Pillow?) to determine that these 3 are blank?
You can maybe try something with numpy (or check the standard déviation or count the number of unique values)
Standard déviation of empty img should be close to zero:
(to adapt)
import numpy as np
image = Image.open('img.jpeg').convert('LA')
# convert image to numpy array
data = asarray(image)
np.reshape(data, (-1,1))
std_dev=np.std(data)
if std_dev<1:
check img
With unique count: (to adapt)
image = Image.open('img.jpeg').convert('LA')
# convert image to numpy array
data = asarray(image)
np.reshape(data, (-1,1))
u, count_unique = np.unique(data, unique_counts =True)
if count_unique.size< 10:
check img
What I am trying to achieve is similar to photoshop/gimp's eyedropper tool: take a round sample of a given area in an image and return the average colour of that circular sample.
The simplest method I have found is to take a 'regular' square sample, mask it as a circle, then reduce it to 1 pixel, but this is very CPU-demanding (especially when repeated millions of times).
A more mathematically complex method is to take a square area and average only the pixels that fall within a circular area within that sample, but determining what pixel is or isn't within that circle, repeated, is CPU-demanding as well.
Is there a more succinct, less-CPU-demanding means to achieve this?
Here's a little example of skimage.draw.circle() which doesn't actually draw a circle but gives you the coordinates of points within a circle which you can use to index Numpy arrays with.
#!/usr/bin/env python3
import numpy as np
from skimage.io import imsave
from skimage.draw import circle
# Make rectangular canvas of mid-grey
w, h = 200, 100
img = np.full((h, w), 128, dtype=np.uint8)
# Get coordinates of points within a central circle
Ycoords, Xcoords = circle(h//2, w//2, 45)
# Make all points in circle=200, i.e. fill circle with 200
img[Ycoords, Xcoords] = 200
# Get mean of points in circle
print(img[Ycoords, Xcoords].mean()) # prints 200.0
# DEBUG: Save image for checking
imsave('result.png',img)
I'm sure that there's a more succinct way to go about it, but:
import math
import numpy as np
import imageio as ioimg # as scipy's i/o function is now depreciated
from skimage.draw import circle
import matplotlib.pyplot as plt
# base sample dimensions (rest below calculated on this).
# Must be an odd number.
wh = 49
# tmp - this placement will be programmed later
dp = 500
#load work image (from same work directory)
img = ioimg.imread('830.jpg')
# convert to numpy array (droppying the alpha while we're at it)
np_img = np.array(img)[:,:,:3]
# take sample of resulting array
sample = np_img[dp:wh+dp, dp:wh+dp]
#==============
# set up numpy circle mask
## this mask will be multiplied against each RGB layer in extracted sample area
# set up basic square array
sample_mask = np.zeros((wh, wh), dtype=np.uint8)
# set up circle centre coords and radius values
xy, r = math.floor(wh/2), math.ceil(wh/2)
# use these values to populate circle area with ones
rr, cc = circle(xy, xy, r)
sample_mask[rr, cc] = 1
# add axis to make array multiplication possible (do I have to do this)
sample_mask = sample_mask[:, :, np.newaxis]
result = sample * sample_mask
# count number of nonzero values (this will be our median divisor)
nz = np.count_nonzero(sample_mask)
sample_color = []
for c in range(result.shape[2]):
sample_color.append(int(round(np.sum(result[:,:,c])/nz)))
print(sample_color) # will return array like [225, 205, 170]
plt.imshow(result, interpolation='nearest')
plt.show()
Perhaps asking this question here wasn't necessary (it has been a while since I've python-ed, and was hoping that some new library had been developed for this since), but I hope this can be a reference for others who have the same goal.
This operation will be performed for every pixel in the image (sometimes millions of times) for thousands of images (scanned pages), so therein are my performance issue worries, but thanks to numpy, this code is pretty quick.
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.
I have a big RGB image as a numpy array,i want to set all pixel that has R=0,G=0,B=0 to R=255,G=0,B=0.
what is the fastest way?
i tried:
for pix in result:
if np.all(np.logical_and(pix[0]==pix[1],pix[2]==0,pix[2]==pix[1])):
pix [0] = 255
but in this way i don't have a single pixel. there is a similar way that it is not to iterate the index?
Here is a vectorized solution. Your image is basically an w by h by 3(colors) array. We can make use of the broadcasting rules that are not easy to grasp but are very powerful.
Basically, we compare the whole array to a 3 vector with the values that you are looking for. Due to the broadcasting rules Numpy will then compare each pixel to that three vector and tell you if it matched (so in this specific case, if the red, green and blue matched). You will end up with an boolean array of trues and falses of the same size as the image.
now we only want to find the pixels where all three colors matched. For that we use the "all" method, which is true, if all values of an array are true. If we apply that to a certain axis -- in this case the color axis -- we get an w by h array that is true, wherever all the colors matched.
Now we can apply this 2D boolean mask back to our original w by h by 3 array and get the pixels that match our color. we can now reassign them -- again with broadcasting.
Here is the example code
import numpy as np
#create a 2x2x3 image with ones
img = np.ones( (2,2,3) )
#make the off diagonal pixels into zeros
img[0,1] = [0,0,0]
img[1,0] = [0,0,0]
#find the only zeros pixels with the mask
#(of course any other color combination would work just as well)
#... and apply "all" along the color axis
mask = (img == [0.,0.,0.]).all(axis=2)
#apply the mask to overwrite the pixels
img[ mask ] = [255,0,0]
Since all values are positive or null, a simple and efficient way is:
img[img.sum(axis=2)==0,0]=255
img.sum(axis=2)==0 select good pixels in the two first dimensions, 0 the red canal in the third.
I have a sparse (100k / 20000^2) 2-D boolean numpy mask corresponding to the positions of objects.
I want to update the mask to set to True all pixels within a certain radius of a True pixel in the original mask. In other words, convolve the delta-function response with a circular aperture/kernel (in this case) response at each position.
Since the master array is large (i.e. 20000 x 20000), and there are 100k positions, I need speed and memory efficiency...
For example (see numpy create 2D mask from list of indices [+ then draw from masked array]):
import numpy
from scipy import sparse
xys=[(1,2),(3,4),(6,9),(7,3)]
master_array=numpy.ones((100,100))
coords = zip(*xys)
mask = sparse.coo_matrix((numpy.ones(len(coords[0])),coords),\
shape= master_array.shape, dtype=bool)
# Now mask all pixels within a radius r of every coordinate pair in the list
mask = cookieCutter(mask,r) # <--- I need an efficient cookieCutter function!
# Now sample the masked array
draws=numpy.random.choice(master_array[~mask.toarray()].flatten(),size=10)
Thanks!
(Follows on from numpy create 2D mask from list of indices [+ then draw from masked array])
Special case of a single position: How to apply a disc shaped mask to a numpy array?
Scikit-Image has a dilation function, which would serve your purpose.