how to remove background of images in python - python

I have a dataset that contains full width human images I want to remove all the backgrounds in those Images and just leave the full width person,
my questions:
is there any python code that does that ?
and do I need to specify each time the coordinate of the person object?

Here is one way using Python/OpenCV.
Read the input
Convert to gray
Threshold and invert as a mask
Optionally apply morphology to clean up any extraneous spots
Anti-alias the edges
Convert a copy of the input to BGRA and insert the mask as the alpha channel
Save the results
Input:
import cv2
import numpy as np
# load image
img = cv2.imread('person.png')
# convert to graky
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold input image as mask
mask = cv2.threshold(gray, 250, 255, cv2.THRESH_BINARY)[1]
# negate mask
mask = 255 - mask
# apply morphology to remove isolated extraneous noise
# use borderconstant of black since foreground touches the edges
kernel = np.ones((3,3), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# anti-alias the mask -- blur then stretch
# blur alpha channel
mask = cv2.GaussianBlur(mask, (0,0), sigmaX=2, sigmaY=2, borderType = cv2.BORDER_DEFAULT)
# linear stretch so that 127.5 goes to 0, but 255 stays 255
mask = (2*(mask.astype(np.float32))-255.0).clip(0,255).astype(np.uint8)
# put mask into alpha channel
result = img.copy()
result = cv2.cvtColor(result, cv2.COLOR_BGR2BGRA)
result[:, :, 3] = mask
# save resulting masked image
cv2.imwrite('person_transp_bckgrnd.png', result)
# display result, though it won't show transparency
cv2.imshow("INPUT", img)
cv2.imshow("GRAY", gray)
cv2.imshow("MASK", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Transparent result:

Related

How to prevent INPAINTING blocks and improve coloring

I wanted to Remove all the texts USING INPAINTING from this IMAGE. I had been trying various methods, and eventually found that I can get the results through OCR and then using thresholding MASK THE IMAGE.
processedImage = preprocess(partOFimg)
mask = np.ones(img.shape[:2], dtype="uint8") * 255
for c in cnts:
cv2.drawContours(mask, [c], -1, 0, -1)
img = cv2.inpaint(img,mask,7,cv2.INPAINT_TELEA)
Preprocess operations:
ret,thresh1 = cv2.threshold(gray, 0, 255,cv2.THRESH_OTSU|cv2.THRESH_BINARY_INV)
rect_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 3))
dilation = cv2.dilate(thresh1, rect_kernel, iterations = 1)
edged = cv2.Canny(dilation, 50, 100)
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
mask =
np.ones(img.shape[:2], dtype="uint8") * 255
When I run the above code, I here am the OUTPUT Image OUTPUT. As we can see, it is making some BLOCKS OF DIFFERENT COLOR over the IMAGE, I want to prevent that, How do I achieve this? I see that mask images are not formed well many times, and in cases when the text is white the PREPROCESSING doesn't occur properly.
How do I prevent these BLOCKS of other colours to FORM on the IMAGE?
Grayed Sub Image GRAYED
Threshold Sub IMG part: Thresholded Image
Masked Image Masked
EDIT 1:
I've managed to get this new better result by noticing that my threshold is the best mask I can get. After doing this I performed the masking process 3 different times with variable masks and inversions. I did the inpainting algorithm 3 times, it basically the other times inverse the mask, because in some cases required mask is the inversed mask. Still I think it needs improvement, If I chose a different image the results are not so good.
Python/OpenCV inpaint methods, generally, are not appropriate to your type of image. They work best on thin (scratch-like) regions, not large blocks. You really need an exemplar type method such as https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/criminisi_tip2004.pdf. But OpenCV does not have that.
However, the OpenCV methods do work here, I suspect, because you are filling with constant colors (green) and not texture. So you are best to try to get the mask of just the letters (characters), not rectangular blocks for the words. So, to show you what I mean, here is my Python/OpenCV approach.
Input:
Read the input
Threshold on the green sign
Apply morphology to close it up and keep as mask1
Apply the mask to the image to blacken out the outside of the sign
Threshold on the white in this new image and keep as mask2
Apply morphology dilate to enlarge it slightly and save as mask3
Do the inpaint
Save the results
import cv2
import numpy as np
# read input
img = cv2.imread('airport_sign.jpg')
# threshold on green sign
lower = (30,80,0)
upper = (70,120,20)
thresh = cv2.inRange(img, lower, upper)
# apply morphology close
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (135,135))
mask1 = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
# apply mask to img
img2 = img.copy()
img2[mask1==0] = (0,0,0)
# threshold on white
#gray = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
#mask2 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
lower = (120,120,120)
upper = (255,255,255)
mask2 = cv2.inRange(img2, lower, upper)
# apply morphology dilate
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
mask3 = cv2.morphologyEx(mask2, cv2.MORPH_DILATE, kernel)
# do inpainting
result1 = cv2.inpaint(img,mask3,11,cv2.INPAINT_TELEA)
result2 = cv2.inpaint(img,mask3,11,cv2.INPAINT_NS)
# save results
cv2.imwrite('airport_sign_mask.png', mask3)
cv2.imwrite('airport_sign_inpainted1.png', result1)
cv2.imwrite('airport_sign_inpainted2.png', result1)
# show results
cv2.imshow('thresh',thresh)
cv2.imshow('mask1',mask1)
cv2.imshow('img2',img2)
cv2.imshow('mask2',mask2)
cv2.imshow('mask3',mask3)
cv2.imshow('result1',result1)
cv2.imshow('result2',result2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mask 3:
Inpaint 1 (Telea):
Inpaint 2 (NS):

How to crop image based on the object radius using OpenCV?

I'm new to python. I know basics about image-preprocessing. I don't know how to remove background and crop an image using OpenCV.
Here is the processing in Python/OpenCV for your new image.
Input:
import cv2
import numpy as np
# load image as grayscale
img = cv2.imread('Diabetic-Retinopathy_G_RM_151064169.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold input image
mask = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY)[1]
# optional morphology to clean up small spots
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
# put mask into alpha channel of image
result = np.dstack((img, mask))
# save resulting masked image
cv2.imwrite('Diabetic-Retinopathy_G_RM_151064169_masked.png', result)
# display result, though it won't show transparency
cv2.imshow("mask", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result
Here is one way to do that in Python/OpenCV.
Read input
Convert to grayscale
Threshold and invert as mask
Put mask into alpha channel of input
Save result
Input:
import cv2
import numpy as np
# load image
img = cv2.imread('black_circle.png')
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold
threshold = cv2.threshold(gray,128,255,cv2.THRESH_BINARY)[1]
# invert so circle is white on black
mask = 255 - threshold
# put mask into alpha channel of image
result = np.dstack((img, mask))
# save resulting masked image
cv2.imwrite('black_circle_masked.png', result)
# display result, though it won't show transparency
cv2.imshow("MASK", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
Note: download the result to see the transparency.

Change range of colors in an image using python

I'm working on automating changing image colors using python. The image I'm using is below, i'd love to move it from red to another range of colors, say green, keeping the detail and shading if possible. I've been able to convert some of the image to a solid color, losing all detail.
The code I'm currently using is below, I can't quite figure out the correct range of red to make it work correctly, and also it only converts to a single color, again losing all detail and shade.
Any help is appreciated, thank you.
import cv2
import numpy as np
import skimage.exposure
# load image and get dimensions
img = cv2.imread("test5.jpg")
# convert to hsv
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
## mask of upper red (170,50,50) ~ (180,255,255)
## mask of lower red (0,50,50) ~ (10,255,255)
# threshold using inRange
range1 = (0,50,50)
range2 = (1,255,255)
mask = cv2.inRange(hsv,range1,range2)
mask = 255 - mask
# apply morphology opening to mask
kernel = np.ones((3,3), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_ERODE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# antialias mask
mask = cv2.GaussianBlur(mask, (0,0), sigmaX=3, sigmaY=3, borderType = cv2.BORDER_DEFAULT)
mask = skimage.exposure.rescale_intensity(mask, in_range=(127.5,255), out_range=(0,255))
result = img.copy()
result[mask==0] = (255,255,255)
# write result to disk
cv2.imwrite("test6.jpg", result)
This is one way to approach the problem in Python/OpenCV. But for red, it is very hard to do because red spans 0 hue, which also is the hue for gray and white and black, which you have in your image. The other issue you have is that skin tones has red shades, so you cannot pick too large of ranges for your colors. Also when dealing with red ranges, you need two sets, one for hues up to 180 and another for hues above 0.
Input:
import cv2
import numpy as np
# load image
img = cv2.imread('red_clothes.jpg')
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
blue_hue = 120
red_hue = 0
# diff hue (blue_hue - red_hue)
diff_hue = blue_hue - red_hue
# create mask for red color in hsv
lower1 = (150,150,150)
upper1 = (180,255,255)
mask1 = cv2.inRange(hsv, lower1, upper1)
lower2 = (0,150,150)
upper2 = (30,255,255)
mask2 = cv2.inRange(hsv, lower2, upper2)
mask = cv2.add(mask1,mask2)
mask = cv2.merge([mask,mask,mask])
# apply morphology to clean mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# modify hue channel by adding difference and modulo 180
hnew = np.mod(h + diff_hue, 180).astype(np.uint8)
# recombine channels
hsv_new = cv2.merge([hnew,s,v])
# convert back to bgr
bgr_new = cv2.cvtColor(hsv_new, cv2.COLOR_HSV2BGR)
# blend with original using mask
result = np.where(mask==(255, 255, 255), bgr_new, img)
# save output
cv2.imwrite('red_clothes_mask.png', mask)
cv2.imwrite('red_clothes_hue_shift.png', bgr_new)
cv2.imwrite('red_clothes_red2blue.png', result)
# Display various images to see the steps
cv2.imshow('mask1',mask1)
cv2.imshow('mask2',mask2)
cv2.imshow('mask',mask)
cv2.imshow('bgr_new',bgr_new)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mask:
Hue Shifted Image:
Blend between Input and Hue Shifted Image using Mask to blend:
So the result is speckled because of the black mixed with the red and from limited ranges due to skin color.
You can start with red, but the trick is to invert the image so red is now at hue 90 in OpenCV range and for example blue is at hue 30. So in Python/OpenCV, you can do the following:
Input:
import cv2
import numpy as np
# load image
img = cv2.imread('red_clothes.jpg')
# invert image
imginv = 255 - img
# convert to HSV
hsv = cv2.cvtColor(imginv, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
blueinv_hue = 30 #(=120+180/2=210-180=30)
redinv_hue = 90 #(=0+180/2=90)
# diff hue (blue_hue - red_hue)
diff_hue = blueinv_hue - redinv_hue
# create mask for redinv color in hsv
lower = (80,150,150)
upper = (100,255,255)
mask = cv2.inRange(hsv, lower, upper)
mask = cv2.merge([mask,mask,mask])
# apply morphology to clean mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# modify hue channel by adding difference and modulo 180
hnew = np.mod(h + diff_hue, 180).astype(np.uint8)
# recombine channels
hsv_new = cv2.merge([hnew,s,v])
# convert back to bgr
bgrinv_new = cv2.cvtColor(hsv_new, cv2.COLOR_HSV2BGR)
# invert
bgr_new = 255 -bgrinv_new
# blend with original using mask
result = np.where(mask==(255, 255, 255), bgr_new, img)
# save output
cv2.imwrite('red_clothes_mask.png', mask)
cv2.imwrite('red_clothes_hue_shift.png', bgr_new)
cv2.imwrite('red_clothes_red2blue.png', result)
# Display various images to see the steps
cv2.imshow('mask',mask)
cv2.imshow('bgr_new',bgr_new)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mask:
Red to Blue before masking:
Red to Blue after masking:
However, one is still limited by the fact that red is close to skin tones, so the range for red is limited.
Starting with a blue image rather than red allows one to use an expanded range for inRange() and do a better job in Python/OpenCV. Here is a change from blue to red.
Input:
import cv2
import numpy as np
# load image
img = cv2.imread('blue_clothes.jpg')
# convert to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
red_hue = 0
blue_hue = 120
# diff hue (red_hue - blue_hue)
diff_hue = red_hue - blue_hue
# create mask for blue color in hsv
lower = (100,90,90)
upper = (140,255,255)
mask = cv2.inRange(hsv, lower, upper)
mask = cv2.merge([mask,mask,mask])
# apply morphology to clean mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (9,9))
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# modify hue channel by adding difference and modulo 180
hnew = np.mod(h + diff_hue, 180).astype(np.uint8)
# recombine channels
hsv_new = cv2.merge([hnew,s,v])
# convert back to bgr
bgr_new = cv2.cvtColor(hsv_new, cv2.COLOR_HSV2BGR)
# blend with original using mask
result = np.where(mask==(255, 255, 255), bgr_new, img)
# save output
cv2.imwrite('blue_clothes_mask.png', mask)
cv2.imwrite('blue_clothes_hue_shift.png', bgr_new)
cv2.imwrite('blue_clothes_blue2red.png', result)
# Display various images to see the steps
cv2.imshow('mask',mask)
cv2.imshow('bgr_new',bgr_new)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Mask:
Blue to Red before masking:
Blue to Red after masking:

Remove letter artifacts from mammography image

I want to remove the letter artifacts "L:CC and Strin" from breast mammography using python. How could I get that done? this is my image
Here is one way to do that in Python/OpenCV.
Read the input
Convert to grayscale
Threshold
Dilate as mask
Apply mask to change white letters to black
Save the results
import cv2
import numpy as np
# read image
img = cv2.imread('mammogram_letters.png')
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# create mask
thresh = cv2.threshold(gray, 247, 255, cv2.THRESH_BINARY)[1]
# dilate mask
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
mask = cv2.morphologyEx(thresh, cv2.MORPH_DILATE, kernel)
# apply change
result = img.copy()
result[mask == 255] = (0,0,0)
# save result
cv2.imwrite("mammogram_letters_thresh.png", thresh)
cv2.imwrite("mammogram_letters_mask.png", mask)
cv2.imwrite("mammogram_letters_blackened.png", result)
# show results
cv2.imshow("THRESH", thresh)
cv2.imshow("MASK", mask)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
Threshold image:
Mask image:
Result:
You have to get pixel coordinate of the box containing test, if they are always the same my code will work.
from PIL import Image
im = Image.open('SqbIx.png')
img =im.load()
for i in range (73,116):
for j in range (36,57):
img[i,j]= (0, 0, 0)
im.save('mod.png')

OpenCV Python how to keep one color as is converting an image to Grayscale

How do I make it so everything in the image is in gray-scale except the orange cone. Using opencv python.
You can achieve your goal by using bitwise_and() function and thresholding.
Steps:
generate mask for the required region.(here thresholding is used but other methods can also be used)
extract required regions using bitwise_and (image & mask).
Add masked regions to get output.
Here's sample code:
import cv2
import numpy as np
img = cv2.imread('input.jpg')
# creating mask using thresholding over `red` channel (use better use histogram to get threshoding value)
# I have used 200 as thershoding value it can be different for different images
ret, mask = cv2.threshold(img[:, :,2], 200, 255, cv2.THRESH_BINARY)
mask3 = np.zeros_like(img)
mask3[:, :, 0] = mask
mask3[:, :, 1] = mask
mask3[:, :, 2] = mask
# extracting `orange` region using `biteise_and`
orange = cv2.bitwise_and(img, mask3)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
# extracting non-orange region
gray = cv2.bitwise_and(img, 255 - mask3)
# orange masked output
out = gray + orange
cv2.imwrite('orange.png', orange)
cv2.imwrite('gray.png', gray)
cv2.imwrite("output.png", out)
Results:
masked orange image
masked gray image
output image
Here is an alternate way to do that in Python/OpenCV.
Read the input
Threshold on color using cv2.inRange()
Apply morphology to clean it up and fill in holes as a mask
Create a grayscale version of the input
Merge the input and grayscale versions using the mask via np.where()
Save the results
Input:
import cv2
import numpy as np
img = cv2.imread("orange_cone.jpg")
# threshold on orange
lower = (0,60,200)
upper = (110,160,255)
thresh = cv2.inRange(img, lower, upper)
# apply morphology and make 3 channels as mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
mask = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.merge([mask,mask,mask])
# create 3-channel grayscale version
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
# blend img with gray using mask
result = np.where(mask==255, img, gray)
# save images
cv2.imwrite('orange_cone_thresh.jpg', thresh)
cv2.imwrite('orange_cone_mask.jpg', mask)
cv2.imwrite('orange_cone_result.jpg', result)
# Display images
cv2.imshow("thresh", thresh)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
Threshold image:
Mask image:
Merged result:

Categories