How to remove bell curved shape from image using opencv - python

I hope you are doing well. I want to remove the bell-curved shape in the image. I have used OpenCV. I have implemented the following code which detects the curve shape, Now How can remove that curve shape and save the new image in the folder.
Input Image 1
I want to remove the area shown in the image below
Area i want to remove
import cv2
import numpy as np
# load image as grayscale
cell1 = cv2.imread("/content/savedImage.jpg",0)
# threshold image
ret,thresh_binary = cv2.threshold(cell1,107,255,cv2.THRESH_BINARY)
# findcontours
contours, hierarchy = cv2.findContours(image =thresh_binary , mode = cv2.RETR_TREE,method = cv2.CHAIN_APPROX_SIMPLE)
# create an empty mask
mask = np.zeros(cell1.shape[:2],dtype=np.uint8)
# loop through the contours
for i,cnt in enumerate(contours):
# if the contour has no other contours inside of it
if hierarchy[0][i][2] == -1 :
# if the size of the contour is greater than a threshold
if cv2.contourArea(cnt) > 10000:
cv2.drawContours(mask,[cnt], 0, (255), -1)
fig, ax = plt.subplots(1,2)
ax[0].imshow(cell1,'gray');
ax[1].imshow(mask,'gray');
Ouput Image after the above Code
How can I process to remove that curve shape?

Here is one way to do that in Python/OpenCV.
Get the external contours. Then find the one that has the largest w/h aspect ratio. Draw that contour filled on a black background as a mask. Dilate it a little. Then invert it and use it to blacken out that region.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('the_image.jpg')
ht, wd = img.shape[:2]
# convert to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)[1]
# get external contours
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
max_aspect=0
for cntr in contours:
x,y,w,h = cv2.boundingRect(cntr)
aspect = w/h
if aspect > max_aspect:
max_aspect = aspect
max_contour = cntr
# create mask from max_contour
mask = np.zeros((ht,wd), dtype=np.uint8)
mask = cv2.drawContours(mask, [max_contour], 0, (255), -1)
# dilate mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel)
# invert mask
mask = 255 - mask
# mask out region in input
result = img.copy()
result = cv2.bitwise_and(result, result, mask=mask)
# save resulting image
cv2.imwrite('the_image_masked.png',result)
# show thresh and result
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:

Related

How can I get only the mask of an image?

Hi I am trying to get a mask of a T-shirt.
Here is what I am getting:
However, I would like to only receive the garment's shape without the print in the middle. How can I do that?
Thanks!
Here is the code:
import cv2
import numpy as np
# load image whose you want to create mask
img = cv2.imread('01430_00.jpg')
# convert to graky
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold input image as mask
mask = cv2.threshold(gray,220,220, 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)
# Show Image in Opencv Windows
cv2.imshow("Original", img)
cv2.imshow("MASK", mask)
# Save mask Image
cv2.imwrite("Mask2.jpg",mask)
cv2.waitKey(0)
cv2.destroyAllWindows()
You already have a large contour for "garment". You can fill inside of the largest contour with the following code
contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = max(contours, key = cv2.contourArea)
cv2.drawContours(mask,[cnt],0,255,-1)

Python & OpenCV: How to add lines to gridless table

I have the following table:
I want to write a script that creates lines based on the natural breakages on the table text. The result would look like this:
Is there an OpenCV implementation that makes drawing these lines possible? I looked at the answers to the questions here and here, but neither worked. What is the best approach to solving this problem?
Here is one way to get the horizontal lines in Python/OpenCV by counting the number of white pixels in each row of the image to find their center y values. The vertical lines can be added by a similar process.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread("table.png")
hh, ww = img.shape[:2]
# convert to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# threshold gray image
thresh = cv2.threshold(gray, 254, 255, cv2.THRESH_BINARY)[1]
# count number of non-zero pixels in each row
count = np.count_nonzero(thresh, axis=1)
# threshold count at ww (width of image)
count_thresh = count.copy()
count_thresh[count==ww] = 255
count_thresh[count<ww] = 0
count_thresh = count_thresh.astype(np.uint8)
# get contours
contours = cv2.findContours(count_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# loop over contours and get bounding boxes and ycenter and draw horizontal line at ycenter
result = img.copy()
for cntr in contours:
x,y,w,h = cv2.boundingRect(cntr)
ycenter = y+h//2
cv2.line(result, (0,ycenter), (ww-1,ycenter), (0, 0, 0), 2)
# write results
cv2.imwrite("table_thresh.png", thresh)
cv2.imwrite("table_lines.png", result)
# display results
cv2.imshow("THRESHOLD", thresh)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
Threshold Image:
Result with lines:
ADDITION
Here is an alternate method that is slightly simpler. It averages the image down to one column rather than counting white pixels.
import cv2
import numpy as np
# read image
img = cv2.imread("table.png")
hh, ww = img.shape[:2]
# convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# average gray image to one column
column = cv2.resize(gray, (1,hh), interpolation = cv2.INTER_AREA)
# threshold on white
thresh = cv2.threshold(column, 254, 255, cv2.THRESH_BINARY)[1]
# get contours
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
# loop over contours and get bounding boxes and ycenter and draw horizontal line at ycenter
result = img.copy()
for cntr in contours:
x,y,w,h = cv2.boundingRect(cntr)
ycenter = y+h//2
cv2.line(result, (0,ycenter), (ww-1,ycenter), (0, 0, 0), 2)
# write results
cv2.imwrite("table_lines2.png", result)
# display results
cv2.imshow("RESULT", result)
cv2.waitKey(0)
Result:

OpenCV Segmentation of Largest contour in Breast Mammograms

This might be a bit too "general" question, but how do I perform GRAYSCALE image segmentation and keep the largest contour? I am trying to remove background noise (i.e. labels) from breast mammograms, but I am not successful. Here is the original image:
First, I applied AGCWD algorithm (based on paper "Efficient Contrast Enhancement Using Adaptive Gamma Correction With Weighting Distribution") in order to get better contrast of the image pixels, like so:
Afterwards, I tried executing following steps:
Image segmentation using OpenCV's KMeans clustering algorithm:
enhanced_image_cpy = enhanced_image.copy()
reshaped_image = np.float32(enhanced_image_cpy.reshape(-1, 1))
number_of_clusters = 10
stop_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 100, 0.1)
ret, labels, clusters = cv2.kmeans(reshaped_image, number_of_clusters, None, stop_criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
clusters = np.uint8(clusters)
Canny Edge Detection:
removed_cluster = 1
canny_image = np.copy(enhanced_image_cpy).reshape((-1, 1))
canny_image[labels.flatten() == removed_cluster] = [0]
canny_image = cv2.Canny(canny_image,100,200).reshape(enhanced_image_cpy.shape)
show_images([canny_image])
Find and Draw Contours:
initial_contours_image = np.copy(canny_image)
initial_contours_image_bgr = cv2.cvtColor(initial_contours_image, cv2.COLOR_GRAY2BGR)
_, thresh = cv2.threshold(initial_contours_image, 50, 255, 0)
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(initial_contours_image_bgr, contours, -1, (255,0,0), cv2.CHAIN_APPROX_SIMPLE)
show_images([initial_contours_image_bgr])
Here is how image looks after I draw 44004 contours:
I am not sure how can I get one BIG contour, instead of 44004 small ones. Any ideas how to fix my approach, or possibly any ideas on using alternative approach to get rid of label in top right corner.
Thanks in advance!
Here is one way to do that in Python OpenCV
Read the image
Threshold and invert so the borders are black
Remove the borders of the image as follows (so as to make it easier to get the relevant contours later):
Count the number of non-zero pixels in each column and find the first and last column that have counts greater than 0
Count the number of non-zero pixels in each row and find the first and last row that have counts greater than 0
Crop the image to remove the borders
Crop thresh1 and invert to make thresh2
Get the external contours from thresh2
Find the largest contour and draw as white filled on a black background as a mask
Make all pixels in the cropped image black where the mask is black
Save the results -
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('xray3.png')
# convert to gray
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# threshold and invert
thresh1 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY)[1]
thresh1 = 255 - thresh1
# remove borders
# count number of white pixels in columns as new 1D array
count_cols = np.count_nonzero(thresh1, axis=0)
# get first and last x coordinate where black
first_x = np.where(count_cols>0)[0][0]
last_x = np.where(count_cols>0)[0][-1]
print(first_x,last_x)
# count number of white pixels in rows as new 1D array
count_rows = np.count_nonzero(thresh1, axis=1)
# get first and last y coordinate where black
first_y = np.where(count_rows>0)[0][0]
last_y = np.where(count_rows>0)[0][-1]
print(first_y,last_y)
# crop image
crop = img[first_y:last_y+1, first_x:last_x+1]
# crop thresh1 and invert
thresh2 = thresh1[first_y:last_y+1, first_x:last_x+1]
thresh2 = 255 - thresh2
# get external contours and keep largest one
contours = cv2.findContours(thresh2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
# make mask from contour
mask = np.zeros_like(thresh2 , dtype=np.uint8)
cv2.drawContours(mask, [big_contour], 0, 255, -1)
# make crop black everywhere except where largest contour is white in mask
result = crop.copy()
result[mask==0] = (0,0,0)
# write result to disk
cv2.imwrite("xray3_thresh1.jpg", thresh1)
cv2.imwrite("xray3_crop.jpg", crop)
cv2.imwrite("xray3_thresh2.jpg", thresh2)
cv2.imwrite("xray3_mask.jpg", mask)
cv2.imwrite("xray3_result.png", result)
# display it
cv2.imshow("thresh1", thresh1)
cv2.imshow("crop", crop)
cv2.imshow("thresh2", thresh2)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
Threshold 1 image:
Cropped image:
Threshold 2 image:
Mask image:
Result:

How to fill a contour plot mask and retain data inside the contour and blackout the rest

I have an image like so,
By using the following code,
import numpy as np
import cv2
# load the image
image = cv2.imread("frame50.jpg", 1)
#color boundaries [B, G, R]
lower = [0, 3, 30]
upper = [30, 117, 253]
# create NumPy arrays from the boundaries
lower = np.array(lower, dtype="uint8")
upper = np.array(upper, dtype="uint8")
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(image, lower, upper)
output = cv2.bitwise_and(image, image, mask=mask)
ret,thresh = cv2.threshold(mask, 50, 255, 0)
if (int(cv2.__version__[0]) > 3):
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
else:
im2, contours, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
if len(contours) != 0:
# find the biggest countour (c) by the area
c = max(contours, key = cv2.contourArea)
x,y,w,h = cv2.boundingRect(c)
ROI = image[y:y+h, x:x+w]
cv2.imshow('ROI',ROI)
cv2.imwrite('ROI.png',ROI)
cv2.waitKey(0)
I get the following cropped image,
I would like to retain all data within the orange hand-drawn boundary including the boundary itself and blackout the rest.
In the image above the data outside the orange boundary is still there. I do not want that. How do i fill the mask which is like this now so that i can retain data inside the orange boundary,
I would still like to retain other properties like the rectangular bounding box. I don't want anything else to change. How do I go about this?
Thanks.
As you desire (in your comments to my previous answer) to have the outer region to be black rather than transparent, you can do that as follows in Python/OpenCV with a couple of lines changed to multiply the mask by the input rather than put the mask into the alpha channel.
Input:
import numpy as np
import cv2
# load the image
image = cv2.imread("frame50.jpg")
#color boundaries [B, G, R]
lower = (0, 70, 210)
upper = (50, 130, 255)
# threshold on orange color
thresh = cv2.inRange(image, lower, upper)
# get largest contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
x,y,w,h = cv2.boundingRect(big_contour)
# draw filled contour on black background
mask = np.zeros_like(image)
cv2.drawContours(mask, [big_contour], 0, (255,255,255), -1)
# apply mask to input image
new_image = cv2.bitwise_and(image, mask)
# crop
ROI = new_image[y:y+h, x:x+w]
# save result
cv2.imwrite('frame50_thresh.jpg',thresh)
cv2.imwrite('frame50_mask.jpg',mask)
cv2.imwrite('frame50_new_image2.jpg',new_image)
cv2.imwrite('frame50_roi2.jpg',ROI)
# show images
cv2.imshow('thresh',thresh)
cv2.imshow('mask',mask)
cv2.imshow('new_image',new_image)
cv2.imshow('ROI',ROI)
cv2.waitKey(0)
new_image after applying the mask:
cropped roi image:
Here is one way to do that in Python/OpenCV.
Read the input
Threshold on the orange color
Find the (largest) contour and get its bounding box
Draw a white filled contour on a black background as a mask
Create a copy of the input with an alpha channel
Copy the mask into the alpha channel
Crop the ROI
Save the results
Input:
import numpy as np
import cv2
# load the image
image = cv2.imread("frame50.jpg")
#color boundaries [B, G, R]
lower = (0, 70, 210)
upper = (50, 130, 255)
# threshold on orange color
thresh = cv2.inRange(image, lower, upper)
# get largest contour
contours = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
big_contour = max(contours, key=cv2.contourArea)
x,y,w,h = cv2.boundingRect(big_contour)
# draw filled contour on black background
mask = np.zeros_like(image)
cv2.drawContours(mask, [big_contour], 0, (255,255,255), -1)
# put mask into alpha channel of input
new_image = cv2.cvtColor(image, cv2.COLOR_BGR2BGRA)
new_image[:,:,3] = mask[:,:,0]
# crop
ROI = new_image[y:y+h, x:x+w]
# save result
cv2.imwrite('frame50_thresh.jpg',thresh)
cv2.imwrite('frame50_mask.jpg',mask)
cv2.imwrite('frame50_new_image.jpg',new_image)
cv2.imwrite('frame50_roi.png',ROI)
# show images
cv2.imshow('thresh',thresh)
cv2.imshow('mask',mask)
cv2.imshow('new_image',new_image)
cv2.imshow('ROI',ROI)
cv2.waitKey(0)
Threshold image:
Mask Image:
New image with mask in alpha channel:
Cropped ROI

How to remove the object marked by the biggest contour from an image and save it as a separate image?

Our objective is to classify plants in a field based on its leaves. We have trained our model on segmented images (these images only have a leaf and black background). But the live feed from the camera will look like this:
So our idea is to find the biggest contour, separate the leaf marked by it and give it a black background.
This is kinda what we are trying to achieve (except the small leaf popping in):
Our approach was to draw a bounding box around the leaf and form a new separate frame. This is our code:
def nothing(useless=None):
pass
cv2.namedWindow("Mask")
cap = cv2.VideoCapture(0)
cv2.createTrackbar('R_l','Mask',26,255,nothing)
cv2.createTrackbar('G_l','Mask',46,255,nothing)
cv2.createTrackbar('B_l','Mask',68,255,nothing)
cv2.createTrackbar('R_h','Mask',108,255,nothing)
cv2.createTrackbar('G_h','Mask',138,255,nothing)
cv2.createTrackbar('B_h','Mask',155,255,nothing)
while True:
R_l = cv2.getTrackbarPos('R_l', 'Mask')
G_l = cv2.getTrackbarPos('G_l', 'Mask')
B_l = cv2.getTrackbarPos('B_l', 'Mask')
R_h = cv2.getTrackbarPos('R_h', 'Mask')
G_h = cv2.getTrackbarPos('G_h', 'Mask')
B_h = cv2.getTrackbarPos('B_h', 'Mask')
_,frame = cap.read()
blurred_frame = cv2.blur(frame,(5,5),0)
hsv_frame = cv2.cvtColor(blurred_frame,cv2.COLOR_BGR2HSV)
low_green = np.array([R_l, G_l, B_l])
high_green = np.array([R_h, G_h, B_h])
green_mask = cv2.inRange(hsv_frame, low_green, high_green)
green = cv2.bitwise_and(frame, frame, mask=green_mask)
contours,_ = cv2.findContours(green_mask,cv2.RETR_TREE,cv2.CHAIN_APPROX_NONE)
try:
sorted_ = sorted(contours,key=cv2.contourArea,reverse=True)
biggest = sorted_[0]
cv2.drawContours(frame,biggest,-1,(255,0,0),1)
except :
pass
#kernel = np.zeros(frame.shape(), np.uint8)
x,y,w,h=cv2.boundingRect(biggest)
roi= frame[y:y+h, x:x+w]
blurred_frame1 = cv2.blur(roi,(5,5),0)
hsv_frame1 = cv2.cvtColor(blurred_frame1,cv2.COLOR_BGR2HSV)
low_green1 = np.array([R_l, G_l, B_l])
high_green1 = np.array([R_h, G_h, B_h])
green_mask1 = cv2.inRange(hsv_frame1, low_green, high_green)
green1= cv2.bitwise_and(roi,roi, mask=green_mask1)
cv2.imshow("frame",frame)
cv2.imshow("Mask",green1)
key = cv2.waitKey(1)
if key == 27:
break
cap.release()
cv2.destroyAllWindows()
How can we prepare the desired image?
You're on the right track. I suggest using HSV color thresholding with a lower/upper threshold to isolate the green leaves. To determine the lower/upper HSV color threshold ranges, I used the HSV color thresholder script from a previous answer. This will give us a binary mask. From here we perform morphological operations to smooth the image and remove noise. Next we find contours and sort using contour area. We extract the largest contour, draw this onto a blank mask, then bitwise-and to get color. From here we find the bounding rectangle coordinates on the mask then crop the ROI from the color image using Numpy slicing. Here's the result
Code
import numpy as np
import cv2
# Read image, create blank masks, color threshold
image = cv2.imread('1.jpg')
blank_mask = np.zeros(image.shape, dtype=np.uint8)
original = image.copy()
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lower = np.array([0, 18, 0])
upper = np.array([88, 255, 139])
mask = cv2.inRange(hsv, lower, upper)
# Perform morphological operations
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations=1)
# Find contours and filter for largest contour
# Draw largest contour onto a blank mask then bitwise-and
cnts = cv2.findContours(close, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
cv2.fillPoly(blank_mask, [cnts], (255,255,255))
blank_mask = cv2.cvtColor(blank_mask, cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(original,original,mask=blank_mask)
# Crop ROI from result
x,y,w,h = cv2.boundingRect(blank_mask)
ROI = result[y:y+h, x:x+w]
cv2.imshow('result', result)
cv2.imshow('ROI', ROI)
cv2.waitKey()

Categories

Resources