I am trying to remove this rectangular contour from the binary image of this photo.
I have tried to use this tutorial https://pyimagesearch.com/2015/02/09/removing-contours-image-using-python-opencv/ but it is not yielding the desired results and I am not very clear of what the syntax does.
thresh = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 61, 10)
cntrs, hiearchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
largest_areas = sorted(cntrs, key=cv2.contourArea)
largest_cntr = [largest_areas[-1]] #rectangular contour or largest contour
mask = np.ones(thresh.shape[:2], dtype="uint8") * 255
cv2.drawContours(mask, largest_cntr, -1, 0, -1)
thresh = cv2.bitwise_and(thresh, thresh, mask=mask)
cv2.imshow("Mask", mask)
cv2.imshow("After", thresh)
In the end I get a black-filled image. How can I remove the contour from the image properly?
Full code:
import cv2,os, glob
from imutils.perspective import four_point_transform
from imutils import contours
import numpy as np
folder_dir = os.getcwd()
print(folder_dir)
img = cv2.imread(folder_dir + "/imgur.jpg")
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY_INV, 31, 10)
cntrs, hiearchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
largest_areas = sorted(cntrs, key=cv2.contourArea)
largest_cntr = [largest_areas[-1]] #rectangular contour or largest contour
mask = np.ones(thresh.shape[:2], dtype="uint8") * 255
cv2.drawContours(mask, largest_cntr, -1, 0, -1)
thresh = cv2.bitwise_and(thresh, thresh, mask=mask)
cv2.imshow("Mask", mask)
cv2.imshow("After", thresh)
cv2.waitKey(0)
cv2.destroyAllWindows()
Related
I wrote an OpenCV program to extract the hand out of the image precisely. But is not able to get it out correctly. Below is the code and the output and the sample image which I used to test it.
import numpy as np
import cv2
# Reading image
font = cv2.FONT_HERSHEY_COMPLEX
img2 = cv2.imread('1.bmp', cv2.IMREAD_COLOR)
# Reading same image in another
# variable and converting to gray scale.
img = cv2.imread('1.bmp', cv2.IMREAD_GRAYSCALE)
# Converting image to a binary image
# ( black and white only image).
_, threshold = cv2.threshold(img, 110, 255, cv2.THRESH_BINARY)
# Detecting contours in image.
contours, _= cv2.findContours(threshold, cv2.RETR_TREE,
cv2.CHAIN_APPROX_SIMPLE)
contours1 = max(contours, key=cv2.contourArea)
# Going through every contours found in the image.
approx = cv2.approxPolyDP(contours1, 0.009 * cv2.arcLength(contours1, True), True)
# draws boundary of contours.
cv2.drawContours(img2, [approx], 0, (0, 0, 255), 5)
cv2.imshow('image2', img2)
# Exiting the window if 'q' is pressed on the keyboard.
if cv2.waitKey(0) & 0xFF == ord('q'):
cv2.destroyAllWindows()
Input image -
One of the reasons your contour is not precise is the obvious; the line where you approximated the contour. But you have also mentioned (in a comment) that lowering the approximation didn't solve the problem.
This is because you didn't blur the thresholded image, which resulted in the jagged edges. Here is an example where the thresholded image is blurred before the contour detection:
The code:
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
return cv2.threshold(img_gray, 111, 255, cv2.THRESH_BINARY)[1]
def draw_contours(img):
contours, _ = cv2.findContours(process(img), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(img, [cnt], -1, (0, 0, 255), 2)
img = cv2.imread("image.png")
draw_contours(img)
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Input image:
Output image:
Still, the contour isn't very precise. This is where the Canny edge detector comes into play:
import cv2
import numpy as np
def process(img):
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
_, thresh = cv2.threshold(img_gray, 111, 255, cv2.THRESH_BINARY)
img_blur = cv2.GaussianBlur(thresh, (5, 5), 4)
img_canny = cv2.Canny(img_blur, 0, 0)
img_dilate = cv2.dilate(img_canny, None, iterations=1)
return cv2.erode(img_dilate, None, iterations=0)
def draw_contours(img):
contours, _ = cv2.findContours(process(img), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnt = max(contours, key=cv2.contourArea)
cv2.drawContours(img, [cnt], -1, (0, 0, 255), 2)
img = cv2.imread("image.png")
draw_contours(img)
cv2.imshow("result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
how to avoid image frame counting, in counting contours?
(opencv python)
there have 6 contours with image frame.i need avoid image frame
You need to use cv2.THRESH_BINARY_INV as a parameter of threshold function.
import numpy as np
import cv2
img = cv2.imread('./tmp.png')
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
ret, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY_INV)
contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
img = cv2.drawContours(img, contours, -1, (0, 255, 0), 3)
print("num contours = {}".format(len(contours)))
cv2.imwrite("./contours.png", img)
result image:
I am trying to use distance transform to contours, but I am getting an error:
out = cv2.distanceTransform(mask, distanceType=cv2.DIST_L2, maskSize=5)
cv2.error: OpenCV(3.4.9) /Users/travis/build/skvark/opencv-python/opencv/modules/imgproc/src/distransform.cpp:724: error: (-215:Assertion failed) src.type() == CV_8UC1 in function 'distanceTransform'
And this is my code:
import cv2
import imutils
pathToThePhoto = 'labrador.jpg'
img = cv2.imread(pathToThePhoto)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 100 , 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key=cv2.contourArea)
mask = cv2.drawContours(gray, [c], -1, (0, 255, 255), 2) #Edit: Changed from img to gray
out = cv2.distanceTransform(mask, distanceType=cv2.DIST_L2, maskSize=5)
cv2.imshow("distance-transform", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
labrador.jpg:
Result after Edit:
It doesn't look like the correct result, or yes?
The issue is that the output of cv2.distanceTransform is of type np.float32.
You need to normalize out to range [0, 1] before showing out.
See OpenCV documentation:
Normalize the distance image for range = {0.0, 1.0}
so we can visualize and threshold it
cv.normalize(dist, dist, 0, 1.0, cv.NORM_MINMAX)
cv.imshow('Distance Transform Image', dist)
Here is the code:
import cv2
import imutils
pathToThePhoto = 'labrador.jpg'
img = cv2.imread(pathToThePhoto)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
thresh = cv2.threshold(gray, 100 , 255, cv2.THRESH_BINARY)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key=cv2.contourArea)
mask = cv2.drawContours(gray, [c], -1, 255, 2) #Edit: Changed from img to gray
out = cv2.distanceTransform(mask, distanceType=cv2.DIST_L2, maskSize=5)
# Normalize the distance image for range = {0.0, 1.0}
# so we can visualize and threshold it
out = cv2.normalize(out, out, 0, 1.0, cv2.NORM_MINMAX)
cv2.imshow("distance-transform", out)
cv2.waitKey(0)
cv2.destroyAllWindows()
out:
I am not sure if this is your intended result.
You are applying the Distance Transform on the image with the puppy.
mask:
I am using the following code to crop image currently
def crop_image(image):
image = cv2.imread(image)
original_img = image.copy()
hsv_img = convert_hsv(image)
lower_blue = np.array([0, 0, 120])
upper_blue = np.array([180, 38, 255])
masked_image = mask_img(hsv_img, lower_blue, upper_blue)
result = cv2.bitwise_and(image, image, mask=masked_image)
contours = cv2.findContours(masked_image.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = imutils.grab_contours(contours)
cv2.drawContours(masked_image, contours, -1, (0, 255, 0), 3)
max_area_contour = max(contours, key=cv2.contourArea)
x, y, w, h = cv2.boundingRect(max_area_contour)
cv2.rectangle(result, (x, y), (x+w, y+h), (0, 255, 0), 3)
cont_filename = generate_contours_filename()
cv2.imwrite(cont_filename, np.hstack([image, result]))
logger.info('Successfuly saved file : %s' % cont_filename)
img = image[y:y+h, x:x+w]
filename = generate_filename()
cv2.imwrite(filename, img)
logger.info('Successfully saved cropped file : %s' % filename)
return img, filename
Following are theimages before and after:
This is original image
This is resulting image
I need image that crops paper part only
Thanks in advance
Here is one way to do that in Python/Opencv.
Read the input
Convert to grayscale
Threshold
Apply morphology to clean it of small regions
Get contours and filter to keep the largest one
Get the bounding box
Draw the largest contour filled on a black background as a mask
Apply the mask to blacken out the background of the paper
Use the bounding box to crop the masked input
Save the results
Input:
import cv2
import numpy as np
# read image as grayscale
img = cv2.imread('paper.jpg')
# convert to grayscale
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# threshold
thresh = cv2.threshold(gray, 190, 255, cv2.THRESH_BINARY)[1]\
# apply morphology
kernel = np.ones((7,7), np.uint8)
morph = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
kernel = np.ones((9,9), np.uint8)
morph = cv2.morphologyEx(morph, cv2.MORPH_ERODE, kernel)
# get largest contour
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
contours = contours[0] if len(contours) == 2 else contours[1]
area_thresh = 0
for c in contours:
area = cv2.contourArea(c)
if area > area_thresh:
area_thresh = area
big_contour = c
# get bounding box
x,y,w,h = cv2.boundingRect(big_contour)
# draw filled contour on black background
mask = np.zeros_like(gray)
mask = cv2.merge([mask,mask,mask])
cv2.drawContours(mask, [big_contour], -1, (255,255,255), cv2.FILLED)
# apply mask to input
result1 = img.copy()
result1 = cv2.bitwise_and(result1, mask)
# crop result
result2 = result1[y:y+h, x:x+w]
# view result
cv2.imshow("threshold", thresh)
cv2.imshow("morph", morph)
cv2.imshow("mask", mask)
cv2.imshow("result1", result1)
cv2.imshow("result2", result2)
cv2.waitKey(0)
cv2.destroyAllWindows()
# save result
cv2.imwrite("paper_thresh.jpg", thresh)
cv2.imwrite("paper_morph.jpg", morph)
cv2.imwrite("paper_mask.jpg", mask)
cv2.imwrite("paper_result1.jpg", result1)
cv2.imwrite("paper_result2.jpg", result2)
Thresholded image:
Morphology cleaned image:
Mask image from largest contour:
Result of masking the input:
Result of cropping previous 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()