OpenCV: Remove doubled contours on outlines of shapes without using RETR_EXTERNAL - python

Open CV will register both an inner and an outer contour for an outline of a polygon.
Running with the test code below
import cv2
import numpy as np
def extract_contours():
path = 'test.png'
blank = np.zeros((184,184,3), np.uint8)
blank[:] = (255,255,255)
raw = cv2.imread(path, cv2.IMREAD_UNCHANGED)
raw = 255-raw
img = cv2.cvtColor(raw, cv2.COLOR_BGR2GRAY)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print(len(contours))
for cnt in contours:
area = cv2.contourArea(cnt)
if area > 400:
approx = cv2.approxPolyDP(cnt, 0.009 * cv2.arcLength(cnt, True), True)
cv2.drawContours(blank, [approx], 0, (0, 0, 255), 1)
cv2.imwrite('contours.png', blank)
extract_contours()
On the image
will yield two sets of contours on the outer and inner edge as shown in
Is there any fast way to collapse the two sets of contours into a single contour, preferably the average of the two? Using I am fairly new to CV2 and computer vision in general so I don't know a lot of the tricks. I would rather not use RETR_EXTERNAL since I do not want to miss out on any nested shapes.

You can use the hierarchy variable you defined (when calling the cv2.findContours method) to determine whether a contour is on the exterior of the outline or the interior:
import cv2
import numpy as np
def extract_contours():
path = 'test.png'
blank = np.zeros((184, 184, 3), np.uint8)
blank[:] = (255, 255, 255)
raw = cv2.imread(path, cv2.IMREAD_UNCHANGED)
raw = 255 - raw
img = cv2.cvtColor(raw, cv2.COLOR_BGR2GRAY)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
for cnt, hrc in zip(contours, hierarchy[0]):
area = cv2.contourArea(cnt)
if area > 400:
approx = cv2.approxPolyDP(cnt, 0.009 * cv2.arcLength(cnt, True), True)
if hrc[2] < 0:
cv2.drawContours(blank, [approx], 0, (0, 0, 255), 1)
elif hrc[3] < 0:
cv2.drawContours(blank, [approx], 0, (0, 255, 0), 1)
cv2.imwrite('contours.png', blank)
extract_contours()
Resulting image:
Drawing the contour in between the exterior and interior contours:
import cv2
import numpy as np
def extract_contours():
path = 'test.png'
blank = np.zeros((184, 184, 3), np.uint8)
blank[:] = (255, 255, 255)
raw = cv2.imread(path, cv2.IMREAD_UNCHANGED)
raw = 255 - raw
img = cv2.cvtColor(raw, cv2.COLOR_BGR2GRAY)
contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
exte = None
inte = None
for cnt, hrc in zip(contours, hierarchy[0]):
area = cv2.contourArea(cnt)
if area > 400:
approx = cv2.approxPolyDP(cnt, 0.009 * cv2.arcLength(cnt, True), True)
if hrc[2] < 0:
exte = approx.squeeze()
elif hrc[3] < 0:
inte = approx.squeeze()
exte = exte[np.lexsort(exte.T)]
inte = inte[np.lexsort(inte.T)]
box = cv2.convexHull((exte[exte[:, 0].argsort()] + inte[inte[:, 0].argsort()]) // 2)
cv2.drawContours(blank, [box], -1, (0, 0, 255), 1)
cv2.imwrite('contours.png', blank)
extract_contours()
Resulting image:

Related

OpenCV Contouring hierarchy

How do I return the contours of the the inner square. Here is my code: I need the contours of the inner square so that I can use the corner points to image warp that area.I have used black tape to help detects the edges. enter image description here
def getBase(img, minarea=100, filter=0, draw=False):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
mask = cv2.inRange(gray, 0, 50)
result = cv2.bitwise_not(gray, gray, mask=mask)
cv2.imshow("result", result)
_, contours, hierarchy = cv2.findContours(
mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE
)
finalContours = []
for i in contours:
area = cv2.contourArea(i)
if area > minarea:
peri = cv2.arcLength(i, True)
approx = cv2.approxPolyDP(i, 0.02 * peri, True)
bbox = cv2.boundingRect(approx)
if filter > 0:
if len(approx) == filter:
finalContours.append((len(approx), area, approx, bbox, i))
else:
finalContours.append((len(approx), area, approx, bbox, i))
finalContours = sorted(finalContours, key=lambda x: x[1], reverse=True)
if draw:
for con in finalContours:
cv2.drawContours(img, con[4], -1, (0, 0, 255), 3)
return img, finalContours
Here is the solution to detect inner rectangle and extract its left corner point, height and width.
import cv2
import numpy as np
def is_rect_contour(contour):
peri = cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, 0.01 * peri, True)
if len(approx) == 4:
return True
else:
return False
image = cv2.imread("./rect.jpg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (7, 7), 0)
thresh = cv2.threshold(blurred, 40, 255, cv2.THRESH_BINARY_INV)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_CCOMP, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0]
refined_contours = []
for cnt in cnts:
if is_rect_contour(cnt):
refined_contours.append(cnt)
inner_rect_cnt = min(refined_contours, key=cv2.contourArea)
(x, y, w, h) = cv2.boundingRect(inner_rect_cnt)
print("x: {}, y:{}, widhth:{}, height:{}".format(x, y, w, h))
cv2.drawContours(image, [inner_rect_cnt], -1, (0, 255, 0), 2)
cv2.imshow('Contours', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output:
x: 425, y:126, widhth:1104, height:720

How to remove shadow of moving object from image using opencv (python)?

I am trying to do background subtraction using MOG2, It was working fine, but when there is deep shadow of a moving object then the shadow is considered as foreground object and I don't want that shadow as foreground object (I'm running MOG2 for 13 images). How can I remove these shadow so that it does not come in foreground?
Here is a sample image...
original img
image after applying MOG2
here is my sample code...
import os
import numpy as np
import cv2
import glob
import imutils
i=0
bg_flag = 0
image_list = []
bgs_list = []
#bgsfinal function
def detection(image_list):
global i
global bg_flag
bgs3_img = None
backsub = cv2.createBackgroundSubtractorMOG2(128, cv2.THRESH_BINARY, 1)
print("start2")
for k in range(len(image_list)):
frame = image_list[k]
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imwrite('./gray/'+str(k)+'.jpg', frame)
#blur = cv2.medianBlur(frame, 21)
blur = frame
bgs_list.append(blur)
for bg in range(len(bgs_list)):
rects = []
#start_time = time.time()
frame_blur = bgs_list[bg]
img = image_list[bg].copy()
s_frame = image_list[bg]
new_frame = s_frame.copy()
fgmask = backsub.apply(frame_blur)
cv2.imwrite("./bgs/"+str(i)+".jpg", fgmask)
fgmask[fgmask==127] = 0
cv2.imwrite("./dilate/"+str(i)+".jpg", fgmask)
thresh = cv2.threshold(fgmask, 128, 255, cv2.THRESH_BINARY)[1]
thresh = cv2.erode(thresh, None, iterations = 1)
thresh = cv2.dilate(thresh, None, iterations=1)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
for c in cnts:
#M = cv2.moments(c)
A = cv2.contourArea(c)
(x, y, w, h) = cv2.boundingRect(c)
cv2.rectangle(new_frame, (x, y), (x + w, y + h), (0,0, 255), 1)
cv2.putText(new_frame, str(A), (x - 10, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1)
cv2.imwrite("./area/"+str(i)+".jpg", new_frame)
cv2.rectangle(thresh, (x, y), (x + w, y + h), (255,255, 255), 1)
cv2.putText(thresh, str(A), (x - 10, y - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)
cv2.imwrite("./area_bgs/"+str(i)+".jpg", thresh)
i+=1
print("Done!")
#this folder contains 13 continuous images
images = glob.glob('./inci4/*.jpg')
for j in range(len(images)):
img = cv2.imread(images[j])
img = cv2.resize(img, (360, 640))
image_list.append(img)
detection(image_list)

Unable to clip and save the ROI/bounding box in opencv python

Im trying to save only the rectangular ROI region from a video file into images. But the entire image is getting saved with the RED rectangular ROI box on it. What am I doing wrong here ?
I tried saving rect_img but thats giving error "!_img.empty() in function 'imwrite'" ,
and not saving any images at all.
The upper_left and bottom_right coordinates are for a 1920 X 1080p video, you wil have to adjust is as per your video resolution.
import cv2
from matplotlib import pyplot as plt
import imutils
import numpy as np
import pytesseract
cam_capture = cv2.VideoCapture('1080_EDIT.webm')
upper_left = (1400, 700)
bottom_right = (700, 1000)
ctr=1 #filename counter
while True:
_, image_frame = cam_capture.read()
ctr+=1
#Rectangle marker
r = cv2.rectangle(image_frame, upper_left, bottom_right, (100, 50, 200), 5)
rect_img = image_frame[upper_left[1] : bottom_right[1], upper_left[0] : bottom_right[0]]
cv2.imwrite("rect"+str(ctr)+".jpg",r)
#print (rect_img)
#img=cv2.imread(rect_img)
gray = cv2.cvtColor(r, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 13, 15, 15)
edged = cv2.Canny(gray, 30, 200)
contours = cv2.findContours(edged.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
contours = imutils.grab_contours(contours)
contours = sorted(contours, key = cv2.contourArea, reverse = True)[:10]
screenCnt = None
for c in contours:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.018 * peri, True)
if len(approx) == 4:
screenCnt = approx
break
if screenCnt is None:
detected = 0
print ("No contour detected")
else:
detected = 1
if detected == 1:
cv2.drawContours(r, [screenCnt], -1, (0, 0, 255), 3)
cv2.imshow("image", image_frame)
if cv2.waitKey(1) % 256 == 27 :
break
cam_capture.release()
cv2.destroyAllWindows()
Solved it by
roi=r[700:1000,700:1400]
cv2.imwrite("rect"+str(ctr)+".jpg",roi)

OpenCV trackbar assertion failed error

I want to do foreground segmentation using opencv. I have written code that has trackbars so that I can find optimal parameters for the segmentation. When the code is executed it works, the images are shown with contours marked and etc. But when I try to move bars on the track bar. I'm getting the following error.
OpenCV(3.4.1) Error: Assertion failed (size.width>0 && size.height>0) in imshow, file /feedstock_root/build_artefacts/opencv_1523502125490/work/opencv-3.4.1/modules/highgui/src/window.cpp, line 356
Traceback (most recent call last):
File "forgroundsegmentation.py", line 17, in foreground_segment
cv2.imshow('brightness preprocess', tmp)
This is my code.
import cv2
import numpy as np
winName = "ForeGround Segment"
def foreground_segment(src, a=None,b=None, useEqualize=1, blurSize=21, th1=None, brightness=None):
winName = "ForeGround Segment"
tmp = brightness
if (blurSize >= 3):
blurSize += (1 - blurSize % 2)
tmp = cv2.GaussianBlur(tmp, (blurSize, blurSize), 0)
if (useEqualize):
tmp = cv2.equalizeHist(tmp)
cv2.imshow('brightness preprocess', tmp)
#cv2.imwrite('../BrightnessPreprocess.png', tmp)
ret, tmp = cv2.threshold(tmp, th1, 255, cv2.THRESH_BINARY_INV)
cv2.imshow(winName, tmp)
im2, contours, hierarchy = cv2.findContours(tmp, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
dst = src
#print(contours)
maxDim = 0
largest = -1
for i in range(len(contours)):
cv2.drawContours(dst, contours, largest, (0, 0, 255), 1)
dim = len(contours[i])
if (dim > maxDim):
maxDim = dim
largest = i
img_mask = np.zeros(src.shape, np.uint8)
if (largest >= 0):
theImg = contours[largest]
cv2.polylines(dst, theImg, True, (0, 255,0), 2)
cv2.drawContours(img_mask, contours, largest, 255, -1)
cv2.imshow("Result Mask", img_mask)
cv2.imshow("Result Contour", dst)
#cv2.imwrite("../img_mask.png", img_mask)
#cv2.imwrite("../result.png", dst)
if __name__ == '__main__':
src = cv2.imread('frontal.png')
print(src.shape)
src = cv2.resize(src, (int(src.shape[0]/3), int(src.shape[1]/3)), interpolation = cv2.INTER_AREA)
#src = cv2.resize(src, (500, 500), interpolation = cv2.INTER_AREA)
cv2.imshow(winName, src)
dst = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
hsv_planes = cv2.split(dst)
brightness = hsv_planes[2]
useEqualize = 1
blurSize = 21
th1 = int(33.0 * 255 / 100)
cv2.createTrackbar("Equalize", winName, useEqualize, 1, foreground_segment)
cv2.createTrackbar("Blur Sigma", winName, blurSize, 100, foreground_segment)
cv2.createTrackbar("Threshold", winName, th1, 255, foreground_segment)
foreground_segment(src ,0, 0, useEqualize, blurSize, th1, brightness=brightness)
cv2.waitKey(0)
cv2.destroyAllWindows
I think I'm doing everything correctly. Can someone point me out where I'm going wrong.
First off, building a segmentation tool of this type is a great idea!!
The code you have provided only creates the trackbars using cv2.createTrackbar(). In order to use them you need to implement cv2.getTrackbarPos() to get different values as you slide the trackbar.
I have made modified the same code:
import cv2
import numpy as np
winName = "ForeGround Segment"
def foreground_segment(im, brightness, useEqualize=1, blurSize=21, th1=None):
winName = "ForeGround Segment"
tmp = brightness
if (blurSize >= 3):
blurSize += (1 - blurSize % 2)
tmp = cv2.GaussianBlur(tmp, (blurSize, blurSize), 0)
if (useEqualize):
tmp = cv2.equalizeHist(tmp)
cv2.imshow('brightness preprocess', tmp)
#cv2.imwrite('../BrightnessPreprocess.png', tmp)
ret, tmp = cv2.threshold(tmp, th1, 255, cv2.THRESH_BINARY_INV)
cv2.imshow('threshold', tmp)
im2, contours, hierarchy = cv2.findContours(tmp, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
dst = im.copy()
#print(contours)
maxDim = 0
largest = -1
for i in range(len(contours)):
cv2.drawContours(dst, contours, largest, (0, 0, 255), 1)
dim = len(contours[i])
if (dim > maxDim):
maxDim = dim
largest = i
img_mask = np.zeros(src.shape, np.uint8)
if (largest >= 0):
theImg = contours[largest]
cv2.polylines(dst, theImg, True, (0, 255,0), 2)
cv2.drawContours(img_mask, contours, largest, 255, -1)
cv2.imshow("Result Mask", img_mask)
cv2.imshow("Result Contour", dst)
#cv2.imwrite("../img_mask.png", img_mask)
#cv2.imwrite("../result.png", dst)
if __name__ == '__main__':
src = cv2.imread('frontal.png')
src = cv2.resize(src, (int(src.shape[0]/3), int(src.shape[1]/3)), interpolation = cv2.INTER_AREA)
cv2.imshow(winName, src)
dst = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
hsv_planes = cv2.split(dst)
brightness = hsv_planes[2]
useEqualize = 1
blurSize = 21
th1 = int(33.0 * 255 / 100)
cv2.createTrackbar("Equalize", winName, useEqualize, 1, foreground_segment)
cv2.createTrackbar("Blur Sigma", winName, blurSize, 100, foreground_segment)
cv2.createTrackbar("Threshold", winName, th1, 255, foreground_segment)
while(1):
#--- Using cv2.getTrackbarPos() to get values from the slider ---
useEqualize = cv2.getTrackbarPos('Equalize', winName)
blurSize = cv2.getTrackbarPos('Blur Sigma', winName)
th1 = cv2.getTrackbarPos('Threshold', winName)
foreground_segment(src, brightness, useEqualize, blurSize, th1)
#--- Press Q to quit ---
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cv2.destroyAllWindows()

shape and centroid of desired color for following code

im a beginner to opencv python. I am trying to detect the shape, as well as the centroid of the colored object (detected object within the color range) on this code. PLEASE HELP.Thanks in advance.
CODE:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import cv2, math
import numpy as np
class ColourTracker:
def __init__(self):
cv2.namedWindow("ColourTrackerWindow", cv2.CV_WINDOW_AUTOSIZE)
self.capture = cv2.VideoCapture(0)
self.scale_down = 4
def run(self):
while True:
f, orig_img = self.capture.read()
#orig_img = cv2.flip(orig_img, 1)
#img = cv2.GaussianBlur(orig_img, (5,5), 0)
#laplacian = cv2.Laplacian(orig_img,cv2.CV_64F)
#sobelx = cv2.Sobel(orig_img,cv2.CV_64F,1,0,ksize=5)
#sobely = cv2.Sobel(orig_img,cv2.CV_64F,0,1,ksize=5)
img = cv2.cvtColor(orig_img, cv2.COLOR_BGR2HSV)
img = cv2.resize(img, (len(orig_img[0]) / self.scale_down, len(orig_img) / self.scale_down))
boundaries = [([0, 150, 0], [5, 255, 255])]#,([50, 140, 10], [255, 255, 255]),([10, 150, 180], [255, 255, 255])]
for (lower, upper) in boundaries:
lower = np.array(lower,np.uint8)
upper = np.array(upper,np.uint8)
binary = cv2.inRange(img, lower, upper)
dilation = np.ones((15, 15), "uint8")
binary = cv2.dilate(binary, dilation)
#edge = cv2.Canny(red_binary,200,300,apertureSize = 3)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
max_area = 0
largest_contour = None
for idx, contour in enumerate(contours):
area = cv2.contourArea(contour)
if area > max_area:
max_area = area
largest_contour = contour
for cnt in largest_contour:
approx = cv2.approxPolyDP(cnt,0.01*cv2.arcLength(cnt,True),True)
print len(approx)
if len(approx)==14:
print "circle"
#cv2.drawContours(orig_img,[cnt], 0, (0, 0, 255), 2)
if not largest_contour == None:
moment = cv2.moments(largest_contour)
if moment["m00"] > 1000 / self.scale_down:
rect = cv2.minAreaRect(largest_contour)
rect = ((rect[0][0] * self.scale_down, rect[0][1] * self.scale_down), (rect[1][0] * self.scale_down, rect[1][1] * self.scale_down), rect[2])
#box = cv2.cv.BoxPoints(rect)
#box = np.int0(box)
#cv2.drawContours(img,[cnt],0,255,-1)
cv2.drawContours(orig_img,[cnt], 0, (0, 0, 255), 2)
cv2.imshow("ColourTrackerWindow", orig_img)
if cv2.waitKey(20) == 27:
cv2.destroyWindow("ColourTrackerWindow")
self.capture.release()
break
if __name__ == "__main__":
colour_tracker = ColourTracker()
colour_tracker.run()
:
code:
` #!/usr/bin/env python
-- coding: utf-8 --
import cv2, math
import numpy as np
class ColourTracker:
def init(self):
cv2.namedWindow("ColourTrackerWindow", cv2.CV_WINDOW_AUTOSIZE)
self.capture = cv2.VideoCapture(1)
self.scale_down = 4
def run(self):
while True:
f, orig_img = self.capture.read()
#orig_img = cv2.flip(orig_img, 1)
img = cv2.GaussianBlur(orig_img, (5,5), 0)
img = cv2.cvtColor(orig_img, cv2.COLOR_BGR2HSV)
img = cv2.resize(img, (len(orig_img[0]) / self.scale_down, len(orig_img) / self.scale_down))
boundaries = [([0, 150, 150], [5, 255, 255]),
([40, 80, 10], [255, 255, 255]),
([190, 150, 100], [255, 255, 255])]
for (lower, upper) in boundaries:
lower = np.array(lower,np.uint8)
upper = np.array(upper,np.uint8)
binary = cv2.inRange(img, lower, upper)
dilation = np.ones((15, 15), "uint8")
binary = cv2.dilate(binary, dilation)
canny = cv2.Canny(binary,100,200)
contours, hierarchy = cv2.findContours(binary, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
max_area = 0
largest_contour = None
for idx, contour in enumerate(contours):
area = cv2.contourArea(contour)
if area > max_area:
max_area = area
largest_contour = contour
if not largest_contour == None:
moment = cv2.moments(largest_contour)
if moment["m00"] > 1000 / self.scale_down:
rect = cv2.minAreaRect(largest_contour)
rect = ((rect[0][0] * self.scale_down, rect[0][1] * self.scale_down), (rect[1][0] * self.scale_down, rect[1][1] * self.scale_down), rect[2])
box = cv2.cv.BoxPoints(rect)
box = np.int0(box)
cv2.drawContours(orig_img,[box], 0, (0, 0, 255), 2)
cv2.imshow("ColourTrackerWindow", orig_img)
cv2.imshow("SHAPE", canny)
if cv2.waitKey(20) == 27:
cv2.destroyWindow("ColourTrackerWindow")
self.capture.release()
break
if name == "main":
colour_tracker = ColourTracker()
colour_tracker.run()`'

Categories