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'])
Related
I want to automatically remove the background from images using OpenCV. The code I use generally works well, but for some images with similar background color to the foreground (e.g. white images with gray shadows on gray background), the code also removes that section of the image.
For example, for this image:
target image to be processed
I get the following result:
image result
This is the code I use (source):
img = cv2.imread(imgpath)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (3,3), 0)
thresh = cv2.threshold(blur, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
thresh = 255 - thresh
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, borderType=cv2.BORDER_CONSTANT, borderValue=0)
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
sorted_contour = sorted(contours, key=cv2.contourArea, reverse=True)
contour = np.zeros_like(gray)
cv2.drawContours(contour, [sorted_contour[0]], 0, 255, -1)
if len(sorted_contour) > 1:
cv2.drawContours(contour, [sorted_contour[1]], 0, 255, -1)
blur = cv2.GaussianBlur(contour, (5,5), sigmaX=0, sigmaY=0, borderType = cv2.BORDER_DEFAULT)
mask = skimage.exposure.rescale_intensity(blur, in_range=(127.5,255), out_range=(0,255))
result = cv2.cvtColor(img, cv2.COLOR_BGR2BGRA)
result[:,:,3] = mask
Any idea on how to modify the code so that it includes the "holes" in the dress, or to "patch up" the mask automatically?
Thanks in advance.
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()
I'm working on sample task where I need to highlight unusual marks on any mobile devices. I'm trying with opencv python. But, I'm not getting actual contors for the unusual marks.
Input image is like:
And output image is expected as below:
I'm trying something like below, but it didn't work.
import cv2
from matplotlib import pyplot as plt
blurValue = 15
img_path = "input.jpg"
# reading the image
image = cv2.imread(img_path)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blur = cv2.GaussianBlur(gray, (blurValue, blurValue), 0)
edged = cv2.Canny(image, 100, 255)
#applying closing function
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
closed = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, kernel)
lower = np.array([4, 20, 93])
upper = np.array([83, 79, 166])
# hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# blur = cv2.GaussianBlur(hsv, (blurValue, blurValue), 0)
mask = cv2.inRange(closed, lower, upper)
result_1 = cv2.bitwise_and(frame, frame, mask = mask)
cnts = cv2.findContours(result_1.copy(), cv2.RETR_TREE, 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.02 * peri, True)
cv2.drawContours(image, [approx], -1, (0, 255, 0), 2)
plt.imshow(image)
plt.title("image")
plt.show()
Any help will be appreciated. Thank you.
My suggestion would be to use adaptive thresholding and filter on area (and possibly other characteristics). Here is my code and results using Python OpenCV.
Input:
import cv2
import numpy as np
# read image
img = cv2.imread("iphone.jpg")
# convert img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# apply gaussian blur
blur = cv2.GaussianBlur(gray, (29,29), 0)
# do adaptive threshold on gray image
thresh = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 51, 3)
# apply morphology open then close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (17, 17))
open = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
close = cv2.morphologyEx(open, cv2.MORPH_CLOSE, kernel)
# Get contours
cnts = cv2.findContours(close, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
result = img.copy()
for c in cnts:
area = cv2.contourArea(c)
if area < 10000 and area > 5000:
cv2.drawContours(result, [c], -1, (0, 255, 0), 2)
# write results to disk
cv2.imwrite("iphone_thresh.jpg", thresh)
cv2.imwrite("iphone_close.jpg", close)
cv2.imwrite("iphone_markings.jpg", result)
# display it
cv2.imshow("IMAGE", img)
cv2.imshow("THRESHOLD", thresh)
cv2.imshow("CLOSED", close)
cv2.imshow("RESULT", result)
cv2.waitKey(0)
Thresholded Image:
Morphology Processed Image:
Final Result:
I would also suggest that you align the image with a known clean iPhone image and create a mask of the camera and logo, etc., markings so that you can filter the results to exclude those (and perhaps even the border of the camera outline).
I want to detect apples by thresholding. For this, I've converted the image to HSV then and calculated the lower and upper limit for InRange() function. Getting a binary mask out of it. Since apples will be touching each others, I'm using watershed algorithm to separate them.
The input image looks like this:
After InRange() operation and erosion, the gray image looks like this:
Applying the watershed algorithm, the output looks like this:
The problem with it is that the bottom left apples are wrongly detected. There are only 2 apples and three contours are shown and also the circle of one of them is way too much bigger. Any help ?
Here is my code,
import cv2
import numpy as np
import imutils
from scipy import ndimage
from skimage.feature import peak_local_max
from skimage.morphology import watershed
img = cv2.imread('4.jpg')
img = imutils.resize(img, width=640)
# img = cv2.pyrMeanShiftFiltering(img, 21, 51)
hsv = cv2.cvtColor(img,cv2.COLOR_BGR2HSV)
lower_1 = np.array([0,50,20])
upper_1 = np.array([80,255,255])
mask1 = cv2.inRange(hsv, lower_1, upper_1)
lower_2 = np.array([160,50,20])
upper_2 = np.array([179,255,255])
mask2 = cv2.inRange(hsv, lower_2, upper_2)
gray = mask1+mask2
kernel = np.ones((7,7),np.uint8)
gray = cv2.erode(gray,kernel,iterations = 1)
# gray = cv2.morphologyEx(gray, cv2.MORPH_OPEN, kernel)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
D = ndimage.distance_transform_edt(thresh)
localMax = peak_local_max(D, indices=False, min_distance=20,
labels=thresh)
markers = ndimage.label(localMax, structure=np.ones((3, 3)))[0]
labels = watershed(-D, markers, mask=thresh)
print("[INFO] {} unique segments found".format(len(np.unique(labels)) - 1))
for label in np.unique(labels):
if label == 0:
continue
mask = np.zeros(gray.shape, dtype="uint8")
mask[labels == label] = 255
cnts = cv2.findContours(mask.copy(), cv2.RETR_EXTERNAL,
cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key=cv2.contourArea)
((x, y), r) = cv2.minEnclosingCircle(c)
if r > 25 and r < 55:
cv2.circle(img, (int(x), int(y)), int(r), (0, 255, 0), 2)
cv2.putText(img, "{}".format(round(r)), (int(x) - 10, int(y)),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
cv2.imshow('thresh', thresh)
cv2.imshow('gray', gray)
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
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.