I have processed an image with a skimage library's segmentation module. It produces a square numpy array with all segments labelled with unique number. Let's say we have a 400*400 image with let's say 500 segments labelled with 500 unique numbers.
I need to select 500 subarrays for each segment and save each as a separate image.
I could use a very brutal simple algorithm with a couple of for loops, but I strongly believe there's a simpler method using numpy masking methods. The problem is I cannot cope with it myself :-(
The code that generates the segments is very simple:
from skimage import future, graph, segmentation
import numpy as np
img_name = 'lion.jpg'
img = io.imread(img_name)
labels = segmentation.slic(img, compactness=10, n_segments=500)
As a result we have a 2D array labels.
My objective is to run a loop
for label in labels:
and cut subarrays for each label as a square with ones for pixels labelled and zeros for the remaining pixels.
Thanks,
Franek
Related
I am working on a watershedding-based segmentation algorithm to segment fluorescence images such as this one:
As result I obtain a Numpy array with labels for each segment. These are separated by a watershed lines, if the corresponding regions in the fluorescence image have a sufficiently large intensity-drop-off between them. For very large intensity-drop-offs they are completely separated through simple thresholding. The result for the image above is this:
My algorithm performs well for the vast majority of cases. However, it sometimes it has a slight tendency to oversegment. Such as in this case from the image above:
Since these cases will be difficult to improve by working further on the intensity-based segmentation itself (and I run the risk of breaking other things), I want to instead selectively merge adjacent segments based on the length of the watershed-line between them and the averaged maximum width of the two segments above and below.
I know what I have to do on a pixel-for-pixel basis:
Find pixels that have two different label-values in their direct neighborhood. Store these pixels separately for each segment-pair (with corresponding segment-labels).
Calculate the number of these pixels for each pair of adjacent segments to obtain the length of the watershed-line.
Calculate the maximum width (horizontally for simplicity) of the adjacent segments.
Merge the adjacent segments, if the watershed-line is longer than a given threshold-fraction (user-defined) of the averaged width of the two segments. I could do this by converting the labels to a binary mask, filling the watershed line using the stored pixels where applicable, and relabelling the binary mask.
Since in Python iterating over individual pixels is generally slow, I am unsure how to write performant code for this. Therefore I am looking for suggestions on how to implement this with Numpy and Skimage (OpenCV is also an option).
You didn't provide how you got your initial segments. Despite this, I think improving the watershed lines could solve your problem and this can be done in the watershed hierarchy framework, with the Higra package.
I specify an initial ordering of the watershed by the image complement and recompute its watershed lines with another attribute (volume).
The intensity drop and area that you describe are the volume attribute, and you can control the segmentation by its threshold in the hierarchy.
Here it is a working example:
import cv2
import numpy as np
import higra as hg
from skimage.morphology import remove_small_objects, label
import matplotlib.pyplot as plt
def main():
img_path = "fig.png"
img = cv2.imread(img_path)
img = img[:,:,0].copy()
img = img.max() - img
size = img.shape[:2]
graph = hg.get_4_adjacency_graph(size)
edge_weights = hg.weight_graph(graph, img, hg.WeightFunction.mean)
tree, altitudes = hg.quasi_flat_zone_hierarchy(graph, edge_weights)
attr = hg.attribute_volume(tree, altitudes)
saliency = hg.saliency(tree, attr)
# Take a look at this :)
# grid = hg.graph_4_adjacency_2_khalimsky(graph, saliency)
# plt.imshow(grid)
# plt.show()
attr_thold = np.mean(saliency) / 4 # arbitrary
area_thold = 500 # arbitrary
segments = hg.labelisation_horizontal_cut_from_threshold(tree, attr, attr_thold)
segments = label(remove_small_objects(segments, area_thold))
plt.imshow(segments)
plt.show()
if __name__ == "__main__":
main()
Here it is the result.
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:
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.
My goal is to display a 2D Array as a image in Python. The array doesn't contain zero elements, and therefore I would expect an image in which imshow() automatically sets the color scale according to the array values. However, when I run the code, the image is blank.
The csv file is: https://ufile.io/urk5m
import numpy as np
import matplotlib.pyplot as plt
data_ = np.loadtxt(open("new_file.csv", "rb"), delimiter=",")
plt.imshow(data_)
My result is this: https://imgur.com/jMNnF0h
Always remember, but really always, that images works on 8bit integers. Thats why there is 2^8 shades of gray and why most commmon number of CS colors is (2^8)^3= 16.7 mil. colors. 3 because there are 3 color channels - RGB, each having 256 shades.
Everybody is counting with it and mainly the image processing libraries.
Therefore ALWAYS make sure you pass correct matrix datatype into image processing functions:
image_8bit = np.uint8(data_)
plt.imshow(image_8bit)
plt.show()
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)