Is it possible to mask an image in Python Imaging Library (PIL)? - python

I have some traffic camera images, and I want to extract only the pixels on the road. I have used remote sensing software before where one could specify an operation like
img1 * img2 = img3
where img1 is the original image and img2 is a straight black-and-white mask. Essentially, the white parts of the image would evaluate to
img1 * 1 = img3
and the black parts would evaluate to
img1 * 0 = img3
And so one could take a slice of the image and let all of the non-important areas go to black.
Is there a way to do this using PIL? I can't find anything similar to image algebra like I'm used to seeing. I have experimented with the blend function but that just fades them together. I've read up a bit on numpy and it seems like it might be capable of it but I'd like to know for sure that there is no straightforward way of doing it in PIL before I go diving in.
Thank you.

The Image.composite method can do what you want. The first image should be a constant value representing the masked-off areas, and the second should be the original image - the third is the mask.

You can use the PIL library to mask the images. Add in the alpha parameter to img2, As you can't just paste this image over img1. Otherwise, you won't see what is underneath, you need to add an alpha value.
img2.putalpha(128) #if you put 0 it will be completly transparent, keep image opaque
Then you can mask both the images
img1.paste(im=img2, box=(0, 0), mask=img2)

Related

Merging three greyscale images into one rgb image

I have three greyscale masks generated by OpenCV that filter in three specific colors. I want to be able to quickly merge them without looping through every pixel in the image (my application requires it to run in real-time) and get an output similar to this:
I've been able to create the three masks separately, but they still need to be combined into one image, where each mask represents a different channel. The first mask would be the red channel, the second would be green, and the third blue.
Clarification: The masks are basically 1/3 of the final image I want to create. I need a way to interpolate them so that they don't end up being the same color in the output and becoming incomprehensible.
More details:
I want to avoid using lots of loops since the current filter takes 4 seconds to process a 272 by 154 image. The masks are just masks created using the cv2.inRange function.
I'm not very good with using numpy or OpenCV yet so any solution that can run reasonably fast (if it can process 15-20 fps it's totally usable) would be of great help.
As #Rotem has said, using cv2.merge to combine the three matrices into one bgr image seems to be one of the best solutions. It's really fast. Thanks!
bgr = cv2.merge((b, g, r))
I don't know how I didn't see it while reading the documentation. Oh well.
Another way I used once
def merge_images(img1, img2, img3):
img1 = cv2.cvtColor(img1, cv2.COLOR_GRAY2RGB)
img2 = cv2.cvtColor(img2, cv2.COLOR_GRAY2RGB)
img3 = cv2.cvtColor(img3, cv2.COLOR_GRAY2RGB)
img = np.concatenate((img1, img2, img3), axis=1)
return img

Smooth conversion between color image to greyscale

I would like to smoothly convert an RGB image to greyscale as a function of some continuous parameter. I have seen plenty of posts on how to convert 3-channel to 1-channel, but that would not work for me, I would like the output to still be 3-channels. Is this possible?
I would like to have a function
f(image, parameter)
that does more or less the following: if paramater is zero, the function returns the original image, and if the parameter is one it returns a greyscale image. Therefore, I would have the ability to smoothly tune the color between on and off via parameter.
If there already is a coded solution, in Python is strongly preferred.
Thanks!
It's quite easy to do with PIL/Pillow.
from PIL import Image
im = Image.open(r'c:\temp\temp.jpg')
gray = im.copy().convert('L').convert('RGB')
im2 = Image.blend(im, gray, 0.75)

Crop the object from infrared picture. Rstudio/Python

I am trying to crop only liver from this picture. I have tried everything but cannot find any useful information about it. I am using Python/Rstudio. Also after removing the unnecessary part, I am willing to get pixels/intensity of the new image. Any help would be appreciated. Please check one of the image This is somehow what I want to crop
UPDATE:
I am trying to crop the main image based on edges I got from the canny edge detector. Is there any way to crop the main image based on edges? Please check the images.
Liver Image
Canny Edge Detection
Well, if your images are static, same size, and taken from the same angle, then below simple script should suffice your needs:
import cv2
img = cv2.imread("cMyp9.png")
mask = np.where(img==255)
img2 = cv2.imread("your_next_image.png")
img2 [mask] = 255
now your img2 is a cropped version

Python: Blur specific region in an image

I'm trying to blur around specific regions in a 2D image (the data is an array of size m x n).
The points are specified by an m x n mask. cv2 and scikit avaiable.
I tried:
Simply applying blur filters to the masked image. But that isn't not working.
Extracting the points to blur by np.nan the rest, blurring and reassembling. Also not working, because the blur obviously needs the surrounding points to work correctly.
Any ideas?
Cheers
What was the result in the first case? It sounds like a good approach. What did you expect and what you get?
You can also try something like that:
Either create a copy of a whole image or just slightly bigger ROI (to include samples that will be used for blurring)
Apply blur on the created image
Apply masks on two images (from original image take everything except ROI and from blurred image take ROI)
Add two masked images
If you want more smooth transition make sure that masks aren't binary. You can smooth them using another blurring (blur one mask and create the second one by calculating: mask2 = 1 - mask1. By doing so you will be sure that weights always add up to one).

Check for areas that are too thin in an image

I am trying to validate black and white images (more of a clipart images - not photos) for an engraving machine.
One of the major things I need to take into consideration is the size of areas (or width of lines) since the machine can't handle lines that are too thin - so I need to find areas that are thinner than a given threshold.
Take this image for example:
The strings of the harp might be too thin to engrave.
I am reading about Matlab and OpenCV but image processing is an area I am learning about for the first time.
I am a Java / C# developer so implementation done with one of those languages will be best for me but any direction will be greatly appreciated.
A solution using matlab utilizing image morphological operations:
Define the minimal thickness of allowed area, for example, minThick=4
BW = imread('http://i.stack.imgur.com/oXKep.jpg');
BW = BW(:,:,1) < 128; %// convert image to binary mask
se = strel('disk', minTick/2, 0); %// define a disk element
eBW = imerode( BW, se ); %// "chop" half thickness from mask
deBW = imdilate( eBW, se ); %// dilate the eroded mask
Eroding and dilating should leave regions that are thicker than minThick unchanged, but it will remove the thin areas
invalidArea = BW & ~deBW; %// pixels that are in BW but not in deBW
Resulting with:
You can read more about imdilate and imerode in the linked documentation.
This is primarily for self-containment, but this is the equivalent code to what #Shai has performed in Python. I used the numpy and OpenCV packages from Python. The equivalent code to doing it in Python would simply be this:
import numpy as np # Import numpy package
import cv2 # Import OpenCV package
orig = cv2.imread('oXKep.jpg') # Read in image from disk
BW = orig[:,:,2] < 128 # Threshold below 128 to invert image
minThick = 5 # Define minimum thickness
se = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (minThick,minThick)) # define a disk element
finalBW = 255*cv2.morphologyEx(BW.astype('uint8'), cv2.MORPH_OPEN, se) # "chop" half thickness from mask and dilate the eroded mask
# Find invalid area
invalidArea = 255*np.logical_and(BW, np.logical_not(finalBW)).astype('uint8')
# Show original image
cv2.imshow('Original', orig)
# Show opened result
cv2.imshow('Final', finalBW)
# Show invalid lines
cv2.imshow('Invalid Area', invalidArea)
# Wait for user input then close windows
cv2.waitKey(0)
cv2.destroyAllWindows()
A few intricacies that I need to point out:
OpenCV's imread function reads in colour channels in reverse order with respect to MATLAB. Specifically, the channels are read in with a blue-green-red order. This means that the first channel is blue, the second channel green and third channel red. In MATLAB, these are read in proper RGB order. Because this is a grayscale image, the RGB components are the same so it really doesn't matter which channel you use. However, in order to be consistent with Shai's method, the red channel is being accessed and so we need to access the last channel of the image through OpenCV.
The disk structuring element in MATLAB with a structure number of 0 is essentially a diamond shape. However, because OpenCV does not have this structuring element built-in, and I wanted to produce the minimum amount of code possible to get something going, the closest thing I could use was the elliptical shaped structuring element.
In order for the structuring element to be symmetric, you need to make sure that the size is odd, so I changed the size from 4 to 5 from Shai's example.
In order to show an image using OpenCV Python, the image must be at least an unsigned 8-bit integer type. Binary images for display using OpenCV are not supported, and so I artificially made the binary images uint8 and multiplied the values by 255 before displaying them.
You can combine the erosion and dilation operations into one operation using morphological opening. Opening seeks to remove thin lines or disconnect objects that are thinly connected but maintain the shape of the original more larger objects. This is the the point of eroding first so that you can remove these lines but you will shrink the objects a bit in terms of the area, then dilating after so that you can restore the shapes back to their original size (mostly). I exploited that by performing a morphological opening via cv2.morphologyEx.
This is what I get:

Categories