(Python 3) Mapping objects to colors in an image according to their size - python-3.x

Say, after a some segmentation, you have the following image:
It doesn't look polished, I know, but after some morphological operations it will turn exactly into what I need it for.
My question is: is there any scikit-image / OpenCV / other library's method which would take this image as input, would calculate objects' size and return a mapped/labeled result as follows: All objects which are bigger than average size - colored in red, the rest - in blue?
I've used scipy.ndimage.label() to get total objects' amount and number of pixels per object, and then calculated the average object size in the array.

Related

Load .gif and retrieve physical dimensions

I am trying to load a .gif file and find the physical dimensions of entities in the file.
i.e I want to find the volume occupied by each cell in the 3D volume.
gif source
One could do the following to get the frames in GIF.(code ref.)
from PIL import Image, ImageSequence
img = Image.open(filename)
frames = []
for frame in ImageSequence.Iterator(img):
a = np.array(frame.convert('RGB').getdata(), dtype=np.uint8)
a = a.reshape(frame.size[1], frame.size[0],3)
frames.append(Picture(a))
return frames
I am not sure what has to be done next.
Could someone please offer some suggestions?
To put it simply you can NOT find the exact volume occupied by each cell in the 3D volume. It is impossible without the 3D object raw data. (just one example of this that there are multiple cells inside the object that you cant see clearly with human eye, so you cant get their data from a picture)
You maybe able to make a complicated algorithm that can get a rough estimation of the volume of the whole object, but it will be very difficult and the accuracy will be low, because there are multiple factors (for example you cant predect if the object is hollow or if it has holes inside it)
As Jabbar mentions, you won't be able to get an exact value, but with some computer vision processing, you should be able to get the voxel dimensions, and if you know the scale of the image, you should be able to scale that value to a physical volume.
First you need to run edge detection and some kind of blob detection to get the individual cells.
Generate a segmentation label for each slice. This is a 2D uint32 array which has a unique number for each entity (cell) you want the volume of.
With your per-layer segmentation labels, you need to correlate the label IDs of the same cell across multiple slices. This will probably be the hardest part, but it's probably ok if it isn't perfect.
Once you have a segmentation mask for each cell in each frame, you can generate a 3D segmentation mask - a 3D array for each cell, which is a boolean mask (True where the cell volume is, False elsewhere)
Sum that array to get the volume (in voxels) of the cell
Scale your voxel volume by the ratio of pixel width and slice depth to physical distance.

How to reduce an unknown size data into a fixed size data? Please read details

Example:
Given n number of images marked 1 to n where n is unknown, I can calculate a property of every image which is a scalar quantity. Now I have to represent this property of all images in a fixed size vector (say 5 or 10).
One naive approach can be this vector- [avg max min std_deviation]
And I also want to include the effect of relative positions of those images.
What your are looking for is called feature extraction.
There are many techniques for the same, for images:
For your purpose try:
PCA
Auto-encoders
Convolutional Auto-encoders, 1 & 2
You could also look into conventional (old) methods like SIFT, HOG, Edge Detection, but they all will need an extra step for making them to a smaller-fixed size.

How to average a stack of images together using Pillow? [duplicate]

For example, I have 100 pictures whose resolution is the same, and I want to merge them into one picture. For the final picture, the RGB value of each pixel is the average of the 100 pictures' at that position. I know the getdata function can work in this situation, but is there a simpler and faster way to do this in PIL(Python Image Library)?
Let's assume that your images are all .png files and they are all stored in the current working directory. The python code below will do what you want. As Ignacio suggests, using numpy along with PIL is the key here. You just need to be a little bit careful about switching between integer and float arrays when building your average pixel intensities.
import os, numpy, PIL
from PIL import Image
# Access all PNG files in directory
allfiles=os.listdir(os.getcwd())
imlist=[filename for filename in allfiles if filename[-4:] in [".png",".PNG"]]
# Assuming all images are the same size, get dimensions of first image
w,h=Image.open(imlist[0]).size
N=len(imlist)
# Create a numpy array of floats to store the average (assume RGB images)
arr=numpy.zeros((h,w,3),numpy.float)
# Build up average pixel intensities, casting each image as an array of floats
for im in imlist:
imarr=numpy.array(Image.open(im),dtype=numpy.float)
arr=arr+imarr/N
# Round values in array and cast as 8-bit integer
arr=numpy.array(numpy.round(arr),dtype=numpy.uint8)
# Generate, save and preview final image
out=Image.fromarray(arr,mode="RGB")
out.save("Average.png")
out.show()
The image below was generated from a sequence of HD video frames using the code above.
I find it difficult to imagine a situation where memory is an issue here, but in the (unlikely) event that you absolutely cannot afford to create the array of floats required for my original answer, you could use PIL's blend function, as suggested by #mHurley as follows:
# Alternative method using PIL blend function
avg=Image.open(imlist[0])
for i in xrange(1,N):
img=Image.open(imlist[i])
avg=Image.blend(avg,img,1.0/float(i+1))
avg.save("Blend.png")
avg.show()
You could derive the correct sequence of alpha values, starting with the definition from PIL's blend function:
out = image1 * (1.0 - alpha) + image2 * alpha
Think about applying that function recursively to a vector of numbers (rather than images) to get the mean of the vector. For a vector of length N, you would need N-1 blending operations, with N-1 different values of alpha.
However, it's probably easier to think intuitively about the operations. At each step you want the avg image to contain equal proportions of the source images from earlier steps. When blending the first and second source images, alpha should be 1/2 to ensure equal proportions. When blending the third with the the average of the first two, you would like the new image to be made up of 1/3 of the third image, with the remainder made up of the average of the previous images (current value of avg), and so on.
In principle this new answer, based on blending, should be fine. However I don't know exactly how the blend function works. This makes me worry about how the pixel values are rounded after each iteration.
The image below was generated from 288 source images using the code from my original answer:
On the other hand, this image was generated by repeatedly applying PIL's blend function to the same 288 images:
I hope you can see that the outputs from the two algorithms are noticeably different. I expect this is because of accumulation of small rounding errors during repeated application of Image.blend
I strongly recommend my original answer over this alternative.
One can also use numpy mean function for averaging. The code looks better and works faster.
Here the comparison of timing and results for 700 noisy grayscale images of faces:
def average_img_1(imlist):
# Assuming all images are the same size, get dimensions of first image
w,h=Image.open(imlist[0]).size
N=len(imlist)
# Create a numpy array of floats to store the average (assume RGB images)
arr=np.zeros((h,w),np.float)
# Build up average pixel intensities, casting each image as an array of floats
for im in imlist:
imarr=np.array(Image.open(im),dtype=np.float)
arr=arr+imarr/N
out = Image.fromarray(arr)
return out
def average_img_2(imlist):
# Alternative method using PIL blend function
N = len(imlist)
avg=Image.open(imlist[0])
for i in xrange(1,N):
img=Image.open(imlist[i])
avg=Image.blend(avg,img,1.0/float(i+1))
return avg
def average_img_3(imlist):
# Alternative method using numpy mean function
images = np.array([np.array(Image.open(fname)) for fname in imlist])
arr = np.array(np.mean(images, axis=(0)), dtype=np.uint8)
out = Image.fromarray(arr)
return out
average_img_1()
100 loops, best of 3: 362 ms per loop
average_img_2()
100 loops, best of 3: 340 ms per loop
average_img_3()
100 loops, best of 3: 311 ms per loop
BTW, the results of averaging are quite different. I think the first method lose information during averaging. And the second one has some artifacts.
average_img_1
average_img_2
average_img_3
in case anybody is interested in a blueprint numpy solution (I was actually looking for it), here's the code:
mean_frame = np.mean(([frame for frame in frames]), axis=0)
I would consider creating an array of x by y integers all starting at (0, 0, 0) and then for each pixel in each file add the RGB value in, divide all the values by the number of images and then create the image from that - you will probably find that numpy can help.
I ran into MemoryErrors when trying the method in the accepted answer. I found a way to optimize that seems to produce the same result. Basically, you blend one image at a time, instead of adding them all up and dividing.
N=len(images_to_blend)
avg = Image.open(images_to_blend[0])
for im in images_to_blend: #assuming your list is filenames, not images
img = Image.open(im)
avg = Image.blend(avg, img, 1/N)
avg.save(blah)
This does two things, you don't have to have two very dense copies of the image while you're turning the image into an array, and you don't have to use 64-bit floats at all. You get similarly high precision, with smaller numbers. The results APPEAR to be the same, though I'd appreciate if someone checked my math.

Brightness and contrast in color image

Does, anyone know, how I can change brightness and contrast of color image. I know about vtkImageMapToWindowLevel, but after setting level or window of image in this class, the color image becomes grayscale.
Thanks for answers;
By definition, a color image is already color mapped, and you cannot change the brightness/contrast of the image without decomposition and recomposition.
First, define a pair of numbers called brightness and contrast in whatever way you want. Normally, I'd take brightness as the maximum value, and contrast as the ratio between minimum and maximum. Similarly, if you want to use Window/Level semantics, "level" is the minimum scalar value, and window is the difference between maximum and minimum.
Next, you find the scalar range - the minimum and maximum values in your desired output image, using the brightness and contrast. If you're applying brightness/contrast, the scalar range is:
Maximum = brightness
Minimum = Maximum / contrast
Assume a color lookup table (LUT), with a series of colors at different proportional values, say, in the range of 0 to 1. Now, since we know the brightness and contrast, we can setup the LUT with the lower value (range 0) mapping to "minimum" and the upper value (range 1) mapping to "maximum". When this is done, a suitable class, like vtkImageMapToColors can take the single-component input and map it to a 3 or 4 component image.
Now, all this can happen only for a single-component image, as the color LUT classes (vtkScalarsToColors and related classes) are defined only on single-component images.
If you have access to the original one-component image, and you're using vtkImageMapToColors or some similar class, I'd suggest handling it at that stage.
If you don't, there is one way I can think of:
Extract the three channels as three different images using vtkImageExtractComponents (you'll need three instances, each with the original image as input).
Independently scale the 3 channels using vtkImageShiftScale (shift by brightness, scale by contrast)
Combine the channels back using vtkImageAppendComponents
Another possibility is to use vtkImageMagnitude, which will convert the image back to grey-scale (by taking the magnitude of the three channels together), and re-applying the color table using vtkImageMapToColors and any of the vtkScalarsToColors classes as your lookup table.
The first method is better if your image is a real photograph or something similar, where the colors are from some 3-component source, and the second would work better if your input image is already using false colors (say an image from an IR camera, or some procedurally generated fractal that's been image mapped).

Generate a value to reflect the average brightness of an image

I need to determine if an image is above a certain brightness.
Using a scale of 0 - 255 I want to generate a value within this range to reflect the image brightness.
i.e. a white image is 255 and a black image is 0.
All this needs to take place via a bash script I am building up at the moment.
I have no idea what image lib could do this for me though.
Generally, it's one of the classic problems of signal processing and there are several approaches, based on how do you define "brightness". It's generally the same for "brightness" of an image, "loudness" of a sound signal, etc.
Some ideas of what you can use as a generic "brightness" is:
Average value of all the pixels (i.e. sum up all the brightnesses of all the pixels, then divide by total amount of pixels, i.e. width * height).
Build up a histogram of brightness distribution, then choose such point x in that histogram that 98% (95%, 90%, 70% - this number can vary) of all the pixels in your image would be less bright than this x. It's called percentile.
Calculate "root mean square" (RMS) for all the pixels (sum up squares of all the pixels, divide by total amount of pixels, extract square root from this one).
There are multiple image libraries that can yield good results. The easiest one to use from a shell script is probably ImageMagick/GraphicsMagick - you can get both simple averages and do some more complex histogramming to check out percentiles if you want to.
Try ImageMagick gray stats or histogram
convert rose: -colorspace gray -format "%[fx:100*mean]%%" info:

Resources