How to mask the largest object of one kind in opencv python - python

Want to fetch only the largest Object of each kind.
Here, the aim is to visualize only RGB objects one of each kind. But it is fetching objects too. I found the best way of detecting the largest object by biggest contour but other items of similar kind are also show here i want to ignore them.
import cv2
import numpy as np
# Webcamera no 0 is used to capture the frames
cap = cv2.VideoCapture(0)
#print(cv2.__version__)
# This drives the program into an infinite loop.
while(1):
# Captures the live stream frame-by-frame
_, frame = cap.read()
blurred_frame=cv2.GaussianBlur(frame,(5,5),100)
# Converts images from BGR to HSV
hsv = cv2.cvtColor(blurred_frame, cv2.COLOR_BGR2HSV)
#print(hsv)
lower_red = np.array([0,50,50])
upper_red = np.array([10,255,255])
lower_green = np.array([65,60,60])
upper_green = np.array([80,255,255])
lower_blue = np.array([100,150,0])
upper_blue = np.array([140,255,255])
# Here we are defining range of bluecolor in HSV
# This creates a mask of blue coloured
# objects found in the frame.
mask = cv2.inRange(hsv, lower_red, upper_red)
mask = cv2.erode(mask, None, iterations=3)
mask = cv2.dilate(mask, None, iterations=5)
mask1 = cv2.inRange(hsv, lower_green, upper_green)
mask1 = cv2.erode(mask1, None, iterations=3)
mask1 = cv2.dilate(mask1, None, iterations=5)
mask2 = cv2.inRange(hsv, lower_blue, upper_blue)
mask2 = cv2.erode(mask2, None, iterations=3)
mask2 = cv2.dilate(mask2, None, iterations=5)
#ret,thresh = cv2.threshold(mask, 0, 255, 0)
contours, hierarchy=cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours1, hierarchy = cv2.findContours(mask1, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours2, hierarchy = cv2.findContours(mask2, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#print(contours)
mask_rg=cv2.bitwise_or(mask,mask1)
mask_res=cv2.bitwise_or(mask2,mask_rg)
res = cv2.bitwise_and(frame,frame, mask= mask_res)
#cv2.drawContours(frame, contours, -1, (0,0,255), 3)
if len(contours)!=0:
contour_sizes = [(cv2.contourArea(contour), contour) for contour in contours]
biggest_contour = max(contour_sizes, key=lambda x: x[0])[1]
for c in contours:
area=cv2.contourArea(c)
if area>50:
cv2.drawContours(res, biggest_contour,-5, (0,0,255), 10)
if len(contours1)!=0:
contour_sizes1 = [(cv2.contourArea(contour), contour) for contour in contours1]
biggest_contour1 = max(contour_sizes1, key=lambda x: x[0])[1]
for c in contours1:
area=cv2.contourArea(c)
if area>50:
cv2.drawContours(res, biggest_contour1,-5, (0,255,0), 10)
if len(contours2)!=0:
contour_sizes2 = [(cv2.contourArea(contour), contour) for contour in contours2]
biggest_contour2 = max(contour_sizes2, key=lambda x: x[0])[1]
for c in contours1:
area=cv2.contourArea(c)
if area>50:
cv2.drawContours(res, biggest_contour2,-5, (255,0,0), 10)
# The bitwise and of the frame and mask is done so
# that only the blue coloured objects are highlighted
# and stored in res
cv2.imshow('frame',frame)
#cv2.imshow('mask',mask)
cv2.imshow('res',res)
# This displays the frame, mask
# and res which we created in 3 separate windows.
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
# Destroys all of the HighGUI windows.
cv2.destroyAllWindows()
# release the captured frame
cap.release()
enter image description here
I don't want 2 blue color objects only one should be seen which is detected by contours.
Please help me out.

Related

Detect thick black lines in image with OpenCV

I have the following image of a lego board with some bricks on it
Now I am trying to detect the thick black lines (connecting the white squares) with OpenCV. I have already experimented a lot with HoughLinesP, converted the image to gray or b/w before, applied blur, ...
Nonthing led to usable results.
# Read image
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Resize Image
img = cv2.resize(img, (0,0), fx=0.25, fy=0.25)
# Initialize output
out = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# Median blurring to get rid of the noise; invert image
img = cv2.medianBlur(img, 5)
# Adaptive Treshold
bw = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,15,8)
# HoughLinesP
linesP = cv2.HoughLinesP(bw, 500, np.pi / 180, 50, None, 50, 10)
# Draw Lines
if linesP is not None:
for i in range(0, len(linesP)):
l = linesP[i][0]
cv2.line(out, (l[0], l[1]), (l[2], l[3]), (0,0,255), 3, cv2.LINE_AA)
The adaptive treshold lets you see edges quite well, but with HoughLinesP you don't get anything usable out of it
What am I doing wrong?
Thanks, both #fmw42 and #jeru-luke for your great solutions to this problem! I liked isolating / masking the green board, so I combined both:
import cv2
import numpy as np
img = cv2.imread("image.jpg")
scale_percent = 50 # percent of original size
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
# resize image
img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_component = lab[:,:,1]
# binary threshold the a-channel
th = cv2.threshold(a_component,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
# numpy black
black = np.zeros((img.shape[0],img.shape[1]),np.uint8)
# function to obtain the largest contour in given image after filling it
def get_region(image):
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
mask = cv2.drawContours(black,[c],0,255, -1)
return mask
mask = get_region(th)
# turning the region outside the green block white
green_block = cv2.bitwise_and(img, img, mask = mask)
green_block[black==0]=(255,255,255)
# median blur
median = cv2.medianBlur(green_block, 5)
# threshold on black
lower = (0,0,0)
upper = (15,15,15)
thresh = cv2.inRange(median, lower, upper)
# apply morphology open and close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (29,29))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# filter contours on area
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = green_block.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 2)
# view result
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Here I am presenting a repeated segmentation approach using color.
This answer is based on the usage of LAB color space
1. Isolating the green lego block
img = cv2.imread(image_path)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_component = lab[:,:,1]
# binary threshold the a-channel
th = cv2.threshold(a_component,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
th
# function to obtain the largest contour in given image after filling it
def get_region(image):
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
black = np.zeros((image.shape[0], image.shape[1]), np.uint8)
mask = cv2.drawContours(black,[c],0,255, -1)
return mask
mask = get_region(th)
mask
# turning the region outside the green block white
green_block = cv2.bitwise_and(img, img, mask = mask)
green_block[black==0]=(255,255,255)
green_block
2. Segmenting the road
To get an approximate region of the road, I subtracted the mask and th.
cv2.subtract() performs arithmetic subtraction, where cv2 will take care of negative values.
road = cv2.subtract(mask,th)
# `road` contains some unwanted spots/contours which are removed using the function "get_region"
only_road = get_region(road)
only_road
Masking only the road segment with the original image gives
road_colored = cv2.bitwise_and(img, img, mask = only_road)
road_colored[only_road==0]=(255,255,255)
road_colored
From the above image only the black regions (road) are present, which is easy to segment:
# converting to grayscale and applying threshold
th2 = cv2.threshold(road_colored[:,:,1],127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
# using portion of the code from fmw42's answer, to get contours above certain area
contours = cv2.findContours(th2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 4)
result
Note:
To clean up the end result, you can apply morphological operations on th2 before drawing contours.
Here is one way to do that in Python/OpenCV.
Read the image
Apply median blur
Threshold on black color using cv2.inRange()
Apply morphology to clean it up
Get contours and filter on area
Draw contours on input
Save the result
Input:
import cv2
import numpy as np
# read image
img = cv2.imread('black_lines.jpg')
# median blur
median = cv2.medianBlur(img, 5)
# threshold on black
lower = (0,0,0)
upper = (15,15,15)
thresh = cv2.inRange(median, lower, upper)
# apply morphology open and close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (29,29))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# filter contours on area
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 2)
# save result
cv2.imwrite("black_lines_threshold.jpg", thresh)
cv2.imwrite("black_lines_morphology.jpg", morph)
cv2.imwrite("black_lines_result.jpg", result)
# view result
cv2.imshow("threshold", thresh)
cv2.imshow("morphology", morph)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Threshold image:
Morphology image:
Result:

How to extract oval contours from an image and save into different variables?

I need to extract the 12 oval shapes from the image and store them in separate variables say 1 to 12.
The original image was as follows
Original Image:
Output image:
Can someone help me extract all those oval shapes into different variables ?
my code is
import cv2
import numpy as np
path = r'/home/parallels/Desktop/Opencv/data/test.JPG'
i = cv2.imread(path, -1)
img_rgb = cv2.resize(i, (1280,720))
cv2.namedWindow("Original Image",cv2.WINDOW_NORMAL)
img = cv2.cvtColor(img_rgb, cv2.COLOR_RGB2HSV)
img = cv2.bilateralFilter(img,9,105,105)
r,g,b=cv2.split(img)
equalize1= cv2.equalizeHist(r)
equalize2= cv2.equalizeHist(g)
equalize3= cv2.equalizeHist(b)
equalize=cv2.merge((r,g,b))
equalize = cv2.cvtColor(equalize,cv2.COLOR_RGB2GRAY)
ret,thresh_image = cv2.threshold(equalize,0,255,cv2.THRESH_OTSU+cv2.THRESH_BINARY)
equalize= cv2.equalizeHist(thresh_image)
canny_image = cv2.Canny(equalize,250,255)
canny_image = cv2.convertScaleAbs(canny_image)
kernel = np.ones((3,3), np.uint8)
dilated_image = cv2.dilate(canny_image,kernel,iterations=1)
contours, hierarchy = cv2.findContours(dilated_image, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours= sorted(contours, key = cv2.contourArea, reverse = True)[:10]
c=contours[0]
print(cv2.contourArea(c))
final = cv2.drawContours(img, [c], -1, (255,0, 0), 3)
mask = np.zeros(img_rgb.shape,np.uint8)
new_image = cv2.drawContours(mask,[c],0,255,-1,)
new_image = cv2.bitwise_and(img_rgb, img_rgb, mask=equalize)
cv2.namedWindow("new",cv2.WINDOW_NORMAL)
cv2.imshow("new",new_image)
cv2.waitKey(0)
You're on the right track. After obtaining your binary image, you can perform contour area + aspect ratio filtering. We can sort the contours in order from left-to-right using imutils.contours.sort_contours(). We find contours then filter using cv2.contourArea
and aspect ratio with cv2.approxPolyDP + cv2.arcLength. If they pass this filter, we draw the contours and append it to a oval list to keep track of the contours. Here's the results:
Filtered mask
Results
Isolated ovals
Output from oval list
Oval contours: 12
Code
import cv2
import numpy as np
from imutils import contours
# Load image, resize, convert to HSV, bilaterial filter
image = cv2.imread('1.jpg')
resize = cv2.resize(image, (1280,720))
original = resize.copy()
mask = np.zeros(resize.shape[:2], dtype=np.uint8)
hsv = cv2.cvtColor(resize, cv2.COLOR_RGB2HSV)
hsv = cv2.bilateralFilter(hsv,9,105,105)
# Split into channels and equalize
r,g,b=cv2.split(hsv)
equalize1 = cv2.equalizeHist(r)
equalize2 = cv2.equalizeHist(g)
equalize3 = cv2.equalizeHist(b)
equalize = cv2.merge((r,g,b))
equalize = cv2.cvtColor(equalize,cv2.COLOR_RGB2GRAY)
# Blur and threshold for binary image
blur = cv2.GaussianBlur(equalize, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1]
# Find contours, sort from left-to-right
# Filter using contour area and aspect ratio filtering
ovals = []
num = 0
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
(cnts, _) = contours.sort_contours(cnts, method="left-to-right")
for c in cnts:
area = cv2.contourArea(c)
x,y,w,h = cv2.boundingRect(c)
ar = w / float(h)
if area > 1000 and ar < .8:
cv2.drawContours(resize, [c], -1, (36,255,12), 3)
cv2.drawContours(mask, [c], -1, (255,255,255), -1)
cv2.putText(resize, str(num), (x,y - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (36,55,12), 2)
ovals.append(c)
num += 1
result = cv2.bitwise_and(original, original, mask=mask)
result[mask==0] = (255,255,255)
print('Oval contours: {}'.format(len(ovals)))
cv2.imshow('equalize', equalize)
cv2.imshow('thresh', thresh)
cv2.imshow('resize', resize)
cv2.imshow('result', result)
cv2.imshow('mask', mask)
cv2.waitKey()

How to detect black shaped contour on photo with OpenCV-Python

I am trying to detect black shape on photo like this.
So far i have picture with shape but still on there is many lines and noise and from that i cannot use the findContours() because it's also mark the line. Can You give me some advice or help with this task. I will be so grateful for help!
Original image
Binary image
import cv2
import numpy as np
import imutils
def color_seg(choice):
if choice == 'blue':
lower_hue = np.array([100,30,30])
upper_hue = np.array([150,148,255])
elif choice == 'white':
lower_hue = np.array([0,0,0])
upper_hue = np.array([0,0,255])
elif choice == 'black':
lower_hue = np.array([0,0,0])
upper_hue = np.array([50,50,100])
return lower_hue, upper_hue
# Take each frame
frame = cv2.imread('11.jpg')
#frame = cv2.imread('images/road_1.jpg')
frame = imutils.resize(frame, height = 500)
chosen_color = 'black'
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of a color in HSV
lower_hue, upper_hue = color_seg(chosen_color)
# Threshold the HSV image to get only blue colors
mask = cv2.inRange(hsv, lower_hue, upper_hue)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(mask,kernel,iterations = 10)
erosion = cv2.filter2D(mask,-1,kernel)
erosion = cv2.GaussianBlur(mask,(5,5),cv2.BORDER_DEFAULT)
cv2.imshow('frame',frame)
cv2.imshow('mask',mask)
cv2.waitKey(0)
You're on the right track. After obtaining your binary image you need to perform morphological operations to filter out noise and isolate the object. Afterwards, we can find contours then filter using contour approximation and contour area. We draw the detected region onto a blank mask then bitwise-and with the original image. Here's the steps:
Binary image
Morphological operations
Detected region in green
Isolated result after bitwise operations
Code
import numpy as np
import cv2
# Color threshold
image = cv2.imread('1.jpg')
original = image.copy()
blank = np.zeros(image.shape, dtype=np.uint8)
blur = cv2.GaussianBlur(image, (7,7), 0)
hsv = cv2.cvtColor(blur, cv2.COLOR_BGR2HSV)
lower = np.array([0, 0, 0])
upper = np.array([179, 93, 97])
mask = cv2.inRange(hsv, lower, upper)
# Morph operations
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7,7))
opening = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
close = cv2.morphologyEx(opening, cv2.MORPH_CLOSE, kernel, iterations=2)
# Find contours and filter using contour approximation + contour area
cnts = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * peri, True)
area = cv2.contourArea(c)
if len(approx) > 3 and area > 1000:
cv2.drawContours(image, [c], -1, (36,255,12), -1)
cv2.drawContours(blank, [c], -1, (255,255,255), -1)
# Bitwise-and for result
blank = cv2.cvtColor(blank, cv2.COLOR_BGR2GRAY)
result = cv2.bitwise_and(original,original,mask=blank)
result[blank==0] = (255,255,255)
cv2.imshow('mask', mask)
cv2.imshow('opening', opening)
cv2.imshow('close', close)
cv2.imshow('result', result)
cv2.imshow('image', image)
cv2.waitKey()

Removing contours from an image

I have contours which i want to delete from the image, What is the best way to do it ?
image = cv2.imread(path)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
retr , thresh = cv2.threshold(gray_image, 190, 255, cv2.THRESH_BINARY_INV)
contours, hier = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for c in contours:
if cv2.contourArea(c) > 20:
x, y, w, h = cv2.boundingRect(c)
##### how to continue from here ?
Create an empty mask in the size of the image:
mask = np.zeros(image.shape[:2], dtype=image.dtype)
Next draw all the contours / boundingrect you want to keep on this mask:
cv2.drawContours(mask, [cnt], 0, (255), -1)
Alternatively you can instead draw the contours you don't want and inverse the mask (this may be more suitable in some situations):
mask= cv2.bitwise_not(mask)
Use the mask on the main image:
result = cv2.bitwise_and(image,image, mask= mask)
Edit: added code after comment.
I assumed this is about the image in your other question, so I applied the code to that image.
Result:
Code:
import numpy as np
import cv2
# load image
image = cv2.imread('image.png')
# create grayscale
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# perform threshold
retr , thresh = cv2.threshold(gray_image, 190, 255, cv2.THRESH_BINARY_INV)
# find contours
ret, contours, hier = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# create emtpy mask
mask = np.zeros(image.shape[:2], dtype=image.dtype)
# draw all contours larger than 20 on the mask
for c in contours:
if cv2.contourArea(c) > 20:
x, y, w, h = cv2.boundingRect(c)
cv2.drawContours(mask, [c], 0, (255), -1)
# apply the mask to the original image
result = cv2.bitwise_and(image,image, mask= mask)
#show image
cv2.imshow("Result", result)
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

OpenCV finds the middle of a red contour (Python)

My idea is to filter my body with color-keying. If I wear a red t shirt I would like to find the center of the red tshirt with the contours. Is there a good way for this?
Here is my code:
import numpy as np
import cv2
import imutils
cap = cv2.VideoCapture(0)
while True:
# Take each frame
_, frame = cap.read()
frame = imutils.resize(frame, width = 400)
# Convert BGR to HSV
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# define range of red color in HSV
lower_red = np.array([0,50,50])
upper_red = np.array([10,255,255])
lower_skin = np.array([0, 48, 80])
upper_skin = np.array([20, 255, 255])
skin_mask = cv2.inRange(hsv, lower_skin, upper_skin)
# Threshold the HSV image to get only red colors
red_mask = cv2.inRange(hsv, lower_red, upper_red)
mask = red_mask + skin_mask
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
mask = cv2.erode(mask, kernel, iterations = 2)
mask = cv2.dilate(mask, kernel, iterations = 2)
mask = cv2.GaussianBlur(mask, (3, 3), 0)
skin = cv2.bitwise_and(frame, frame, mask = mask)
gray = cv2.cvtColor(skin, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
# # loop over the contours
for c in cnts:
cv2.drawContours(skin, [c], -1, (0, 255, 0), 2)
cv2.imshow("Image", skin)
if cv2.waitKey(1) & 0xFF == ord("q"):
break
cv2.destroyAllWindows()
So at the moment I only draw contours of the Tshirt and the skin. But I would like to find the center of the red t shirt contour. Is this possible? Do I have to filter red on its own and do not add it to the skin mask? Maybe my current code is not able to do this, I would be happy for proposes! :)
Find t-shirt using your red mask and cv2.findContours and find its center using cv2.moments(contour) as described in docs:
for cnt in contours:
center_x = int(cnt['m10']/cnt['m00'])
center_y = int(cnt['m01']/cnt['m00'])

Categories