python Cv2-Draw Contours and fill black is ignoring perimeter - python

Iam trying to remove objects from the image using the following method, however, as can be seen from outcome image there is a residual fine colored line around each object. Although i have dilated my image, the lines are still present.
Is there a way to remove those lines?
import cv2
import numpy as np
img = cv2.imread('TRY.jpg')
image_edges = cv2.GaussianBlur(img,(3,3),1) #Helps in defining the edges
image_edges=cv2.Canny(image_edges,100,200)
image_edges=cv2.dilate(image_edges,(3,3),iterations=3)
contours_draw, hierarchy = cv2.findContours(image_edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
mask = np.zeros(img.shape, np.uint8)
mask.fill(255)
for c in contours_draw:
cv2.drawContours(mask, [c], -1, (0, 0, 0), -1)
mask = cv2.cvtColor(mask, cv2.COLOR_BGR2GRAY)
res = img.copy()
res[mask == 0] = 255
cv2.imshow('img', res)
cv2.waitKey(0)
Original Image
--
Outcome Image

The outlines are still there because the threshold is too high.
Try this:
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 20, 255, 0)
contours_draw, hierarchy = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
The values you pass to the threshold function determine how inclusive or exclusive the findContours function is going to be. Put low numbers if you want a low threshold, and high numbers if you want a high one.

Related

CV2 Find Contours detecting the floor and not my target

I am trying to detect the black tape on the floor, but whenever I try to increase the threshold to stop it from detecting the ground all it does is stop detecting the tape.
from cv2 import cv2
import numpy as np
img = cv2.imread('tape4.jpeg', cv2.IMREAD_UNCHANGED)
#convert img to grey
img_grey = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#set a thresh
thresh = 100
#get threshold image
ret,thresh_img = cv2.threshold(img_grey, thresh, 255, cv2.THRESH_BINARY)
#find contours
contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#create an empty image for contours
img_contours = np.zeros(img.shape)
# draw the contours on the empty image
cv2.drawContours(img_contours, contours, -1, (0,255,0), 3)
#save image
cv2.imwrite('contours.png',img_contours)
You may use Use cv2.THRESH_BINARY_INV instead of cv2.THRESH_BINARY for finding pixels below thresh instead of pixels above thresh.
The reason you are detecting the floor, is that cv2.threshold marks (with 255 value), the pixels above thresh.
You want to mark the dark pixels below thresh.
You can compute: thresh_img = 255 - thresh_img, or use cv2.THRESH_BINARY_INV (result is the same).
I also recommend using closing morphological operation, for removing clutter.
Code for finding the tape:
import cv2
import numpy as np
img = cv2.imread('tape4.jpeg', cv2.IMREAD_UNCHANGED)
#convert img to grey
img_grey = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#set a thresh
thresh = 20 # Set thresh to very low value (the tape is almost black).
#get threshold image
# Use cv2.THRESH_BINARY_INV instead of cv2.THRESH_BINARY for finding pixels below thresh instead of pixels above thresh
ret,thresh_img = cv2.threshold(img_grey, thresh, 255, cv2.THRESH_BINARY_INV)
# Apply closing morphological operation
thresh_img = cv2.morphologyEx(thresh_img, cv2.MORPH_CLOSE, cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (21,21)));
#find contours
contours, hierarchy = cv2.findContours(thresh_img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
#create an empty image for contours
img_contours = np.zeros(img.shape)
# draw the contours on the empty image
cv2.drawContours(img_contours, contours, -1, (0,255,0), 3)
#save image
cv2.imwrite('contours.png',img_contours)
Result:
For finding the tape, you can look for the largest contour within contours:
# Get contour with maximum area
c = max(contours, key=cv2.contourArea)
img_contours = np.zeros(img.shape)
cv2.drawContours(img_contours, [c], -1, (0,255,0), 3)
Result:

Removing small black contours/unwanted contours in Python

I have been at this all day, how to we remove the small back noise in the red circle? I would need it to work on other samples of pictures like this.
The idea I used is to findContours and then add a mask with all the small black noise that is less than a certain area (trial and error).
Removing noise in red ellipse
image = cv2.imread("11_Image_after_noise_removal.png")
# copy image
img = image.copy()
imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(imgray, 127, 255, 0, cv2.THRESH_BINARY)
thresh = 255 - thresh
# Use cv2.CCOMP for two level hierarchy
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_CCOMP,
cv2.CHAIN_APPROX_SIMPLE) # Use cv2.CCOMP for two level hierarchy
cv2.drawContours(img, contours, -1, (0, 255, 0), 1)
cv2.imshow("First detection", img)
# loop through the contours
for i, cnt in enumerate(contours):
# if the contour has no other contours inside of it
if hierarchy[0][i][3] != -1: # basically look for holes
# if the size of the contour is less than a threshold (noise)
if cv2.contourArea(cnt) < 70:
# Fill the holes in the original image
cv2.drawContours(img, [cnt], 0, (0, 0, 0), -1)
# display result
# Visualize the image after the Otsu's method application
cv2.imshow("Image after noise removal", img)
cv2.waitKey(0)
cv2.destroyAllWindows().destroyAllWindows()
You might check the contour area using area = cv.contourArea(cnt) and if it is below some threshold, ignore it.
Here is the OpenCV documentations:
https://docs.opencv.org/4.3.0/dd/d49/tutorial_py_contour_features.html

Computer Vision: Opencv Counting small circles inside big circle

Here is the image on which i have been working on
The goal is to detect small circles inside the big one.
currently what i have done is converted the image to gray scale and applied threshold (cv2.THRESH_OTSU) to which resulted in this image
After this i have filtered out large objects using findcontours applied Morph open using elliptical shaped kernel which i found on stackoverflow
The result image is like this
Can someone guide me through the correct path on what to do and where i'm getting wrong.
Below is attached code on which i have been working on
import cv2
import numpy as np
# Load image, grayscale, Otsu's threshold
image = cv2.imread('01.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)[1]
#cv2.imwrite('thresh.jpg', thresh)
# Filter out large non-connecting objects
cnts = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
#print(area)
if area < 200 and area > 0:
cv2.drawContours(thresh,[c],0,0,-1)
# Morph open using elliptical shaped kernel
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3,3))
opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=3)
# Find circles
cnts = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
area = cv2.contourArea(c)
if area > 20 and area < 50:
((x, y), r) = cv2.minEnclosingCircle(c)
cv2.circle(image, (int(x), int(y)), int(r), (36, 255, 12), 2)
cv2.namedWindow('orig', cv2.WINDOW_NORMAL)
cv2.imshow('orig', thresh)
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
cv2.imshow('image', image)
cv2.waitKey()
Thank you!
You throw away a lot of useful information by converting your image to grayscale.
Why not use the fact that the spots you are looking for are the only thing that is red/orange?
I multiplied the saturaton channel with the red channel which gave me this image:
Now finding the white blobs becomes trivial.
Experiment with different weights for those channels, or apply thresholds first. There are many ways. Experiment with different illumination, different backgrounds until you get the ideal input for your image processing.
The main problem in your code is the flag that you are using in cv2.findContours() function.
For such a problem in which we have to find contours which can appear inside another contour(the big circle), we should not use the flag cv2.RETR_EXTERNAL, instead use cv2.RETR_TREE. Click here for detailed info..
Also, it is always better to use cv2.CHAIN_APPROX_NONE instead of cv2.CHAIN_APPROX_SIMPLE if memory is not an issue. Click here for detailed info.
Thus, the following simple code can be used to solve this problem.
import cv2
import numpy as np
Image = cv2.imread("Adg5.jpg")
GrayImage = cv2.cvtColor(Image, cv2.COLOR_BGR2GRAY)
# Applying Otsu's Thresholding
Retval, ThreshImage = cv2.threshold(GrayImage, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)
# Finding Contours in the image
Contours, Hierarchy = cv2.findContours(ThreshImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# Taking only those contours which have no child contour.
FinalContours = [Contours[i] for i in range(len(Contours)) if Hierarchy[0][i][2] == -1]
# Drawing contours
Image = cv2.drawContours(Image, FinalContours, -1, (0, 255, 0), 1)
cv2.imshow("Contours", Image)
cv2.waitKey(0)
Resulting image
In this method, a lot of noise at the boundary is also coming but the required orange points are also being detected. Now the task is to remove boundary noise.
Another method that removes boundary noise to a great extent is similar to #Piglet 's approach.
Here, I am using HSV image to segment out the orange points and then detecting them using the above approach.
import cv2
import numpy as np
Image = cv2.imread("Adg5.jpg")
HSV_Image = cv2.cvtColor(Image, cv2.COLOR_BGR2HSV)
# Extracting orange colour using HSV Image.
ThreshImage = cv2.inRange(HSV_Image, np.array([0, 81, 0]), np.array([41, 255, 255]))
# Finding Contours
Contours, Hierarchy = cv2.findContours(ThreshImage, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# Taking only those contours which have no child contour.
FinalContours = [Contours[i] for i in range(len(Contours)) if Hierarchy[0][i][2] == -1]
# Drawing Contours
Image = cv2.drawContours(Image, FinalContours, -1, (0, 255, 0), 1)
cv2.imshow("Contours", Image)
cv2.waitKey(0)
Resultant Image
I have an idea that detet small circles by sliding window. when the small cicle area occupied the sliding window area large than 90%(Inscribed circle and square), and less than 100%(avoiding sliding window move in the bigger cicle). this position is a small circle. the largest sliding windows size is the largest small cicle size. Hope some help.
in addtion, on the result of Piglet, apply k-means, which k = 2, you can get a binary image, and then use findcontours to count the small circles.

How to blur/ feather the edges of an object in an image using Opencv?

The objective is to blur the edges of a selected object in an image.
I've done the steps to obtain the contours of the object by using the following code:
image = cv2.imread('path of image')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 60, 255, cv2.THRESH_BINARY)[1]
im, contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
I am also able to plot the contour using:
cv2.drawContours(image, contours, -1, (0, 255, 0), 2)
Now I want to make use of the points stored in contours to blur / feather the edge of the object, perhaps using gaussian blur. How am I able to achieve that?
Thanks so much!
Similar to what I have mentioned here, you can do it in the following steps:
Load original image and find contours.
Blur the original image and save it in a different variable.
Create an empty mask and draw the detected contours on it.
Use np.where() method to select the pixels from the mask (contours) where you want blurred values and then replace it.
import cv2
import numpy as np
image = cv2.imread('./asdf.jpg')
blurred_img = cv2.GaussianBlur(image, (21, 21), 0)
mask = np.zeros(image.shape, np.uint8)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 60, 255, cv2.THRESH_BINARY)[2]
contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(mask, contours, -1, (255,255,255),5)
output = np.where(mask==np.array([255, 255, 255]), blurred_img, image)

removing largest contour from an image

I have an image such as this
I am trying to detect and remove the arrow from this image so that I end up with an image that just has the text.
I tried the below approach but it isn't working
image_src = cv2.imread("roi.png")
gray = cv2.cvtColor(image_src, cv2.COLOR_BGR2GRAY)
canny=cv2.Canny(gray,50,200,3)
ret, gray = cv2.threshold(canny, 10, 255, 0)
contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
largest_area = sorted(contours, key=cv2.contourArea)[-1]
mask = np.ones(image_src.shape[:2], dtype="uint8") * 255
cv2.drawContours(mask, [largest_area], -1, 0, -1)
image = cv2.bitwise_and(image_src, image_src, mask=mask)
The above code seems to give me back the same image WITH the arrow.
How can I remove the arrow?
The following will remove the largest contour:
import numpy as np
import cv2
image_src = cv2.imread("roi.png")
gray = cv2.cvtColor(image_src, cv2.COLOR_BGR2GRAY)
ret, gray = cv2.threshold(gray, 250, 255,0)
image, contours, hierarchy = cv2.findContours(gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
mask = np.zeros(image_src.shape, np.uint8)
largest_areas = sorted(contours, key=cv2.contourArea)
cv2.drawContours(mask, [largest_areas[-2]], 0, (255,255,255,255), -1)
removed = cv2.add(image_src, mask)
cv2.imwrite("removed.png", removed)
Note, the largest contour in this case will be the whole image, so it is actually the second largest contour.

Categories

Resources