Opencv Python - Shape detection - python

How to split the two rectangles in the image. Also to extract the coordinates of the rectangle with removing the extra projections. Contour detection gives the full image as a circle, instead of splitting it into two rectangles.
Please find the input image,
detect_shapes.py
gray = cv2.cvtColor(image, 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_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]
sd = ShapeDetector()
for c in cnts:
M = cv2.moments(c)
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
shape = sd.detect(c)
c = c.astype("float")
#c *= ratio
c = c.astype("int")
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (255, 0, 0), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)
shapedetector.py
class ShapeDetector:
def __init__(self):
pass
def detect(self, c):
shape = "unidentified"
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * peri, True)
if len(approx) == 3:
shape = "triangle"
elif len(approx) == 4:
print("value of approx", approx)
(x, y, w, h) = cv2.boundingRect(approx)
ar = w / float(h)
print("value of ar",ar)
if (ar >= 0.95 and ar <= 1.05): shape = "Square"
elif (ar <= 5 and ar >= 3): shape = "Obround"
else: shape = "rectangle"
elif len(approx) == 5:
shape = "pentagon"
elif len(approx) == 2:
shape = "line"
print("value of approx", approx)
else:
shape = "circle"
print("value of approx", approx)
return shape
My required output is the below.

As Akhilesh suggested, a morphology OPEN operation will remove the line connecting the two rectangles. An excellent Python tutorial is here: https://docs.opencv.org/3.0.0/d9/d61/tutorial_py_morphological_ops.html. Use the smallest kernel size that will work for your application to prevent too much distortion. For your test image, I used a kernel size of 5.
detect_shapes.py:
import cv2
from shapedetector import ShapeDetector
image = cv2.imread('rectangles.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(blurred, 60, 255, cv2.THRESH_BINARY)[1]
ksize = 5
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (ksize,ksize))
thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
cnts = cv2.findContours(thresh.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
# ~ cnts = cnts[0] if imutils.is_cv2() else cnts[1]
cnts = cnts[1]
sd = ShapeDetector()
for c in cnts:
M = cv2.moments(c)
if M["m00"] != 0: # prevent divide by zero
cX = int((M["m10"] / M["m00"]))
cY = int((M["m01"] / M["m00"]))
shape = sd.detect(c)
c = c.astype("float")
#c *= ratio
c = c.astype("int")
cv2.drawContours(image, [c], -1, (0, 255, 0), 2)
cv2.putText(image, shape, (cX, cY), cv2.FONT_HERSHEY_SIMPLEX,
0.5, (255, 0, 0), 2)
cv2.imshow("Image", image)
cv2.waitKey(0)
shapedetector.py:
import cv2
class ShapeDetector:
def __init__(self):
pass
def detect(self, c):
shape = "unidentified"
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.04 * peri, True)
if len(approx) == 3:
shape = "triangle"
elif len(approx) == 4:
print("value of approx", approx)
(x, y, w, h) = cv2.boundingRect(approx)
ar = w / float(h)
print("value of ar",ar)
if (ar >= 0.95 and ar <= 1.05): shape = "Square"
elif (ar <= 5 and ar >= 3): shape = "Obround"
else: shape = "rectangle"
elif len(approx) == 5:
shape = "pentagon"
elif len(approx) == 2:
shape = "line"
print("value of approx", approx)
else:
shape = "circle"
print("value of approx", approx)
return shape
Output 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

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

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:

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)

How do scan an opened book with openCV and Python and take the page that is focused in?

I need to scan an opened book, in fact it is an agenda; and apply to it a four point transform to crop it a save it but it only requires 4 points and it sometimes takes 6 or 5 points and draws an incomplete rectangle
So it draws it and tries to close it... but it fails at applying the 4 point transform
Filters Applied
import cv2
import imutils
def primer_filtro(archivo):
image = cv2.imread("images/" + archivo)
image = imutils.resize(image, height=800)
# Hacer Gris y desenfoque Gaussiano
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (3, 3), 0)
# Definir bordes
edged = cv2.Canny(gray, 50, 250)
# Definir pixeles
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (7, 7))
closed = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, kernel)
# Encontrar contorno
(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Dibujar contorno
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 6:
cv2.drawContours(image, [approx], -5, (0, 0, 0), 60)
cv2.imwrite("images/find_book.jpg", image)
cv2.imshow("Final", image)
cv2.waitKey(0)
import cv2
from skimage.filters import threshold_adaptive
from pyimagesearch import imutils
from pyimagesearch.transform import four_point_transform
def segundo_filtro(archivo, original):
original = cv2.imread("images/" + original)
original = original.copy()
while True:
imagen = archivo
image = cv2.imread("images/" + imagen)
try:
ratio = image.shape[0] / 500.0
# original = image.copy()
image = imutils.resize(image, height=800)
break
except:
print "Intentando de nuevo"
# Convertir imagenes en gris
# gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = image
cv2.imshow("Immg", gray)
cv2.waitKey(0)
# Aplicar desenfoque Gaussiano
gray = cv2.GaussianBlur(gray, (5, 5), 0)
# Bordeado de imagen
bordeado = cv2.Canny(gray, 50, 450)
# bordeado = cv2.Canny(gray, 50, 250)
cv2.imshow("Bordeada", bordeado)
cv2.waitKey(0)
# Encontrar los contornos tomando en cuenta los mas grandes
(cnts, _) = cv2.findContours(bordeado.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key=cv2.contourArea, reverse=True)[:5]
# Buscar en el contorno
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# Dibujar Contorno
if len(approx) == 6:
approx[4] = approx[0]
approx[5] = approx[1]
cv2.drawContours(image, [approx], -5, (0, 0, 0), 5)
elif len(approx) == 4:
print "Tiene {0} puntos".format(len(approx))
cv2.drawContours(image, [approx], -5, (125, 0, 0), 5)
cv2.imwrite("images/lforehead.jpg", image)
# print "Este tiene {0} puntos".format(len(approx))
cv2.imshow("Contorno", image)
cv2.waitKey(0)
try:
frentear = four_point_transform(original, approx.reshape(4, 2) * ratio)
frentear = cv2.cvtColor(frentear, cv2.COLOR_BGR2GRAY)
frentear = threshold_adaptive(frentear, 251, offset=10)
frentear = frentear.astype("uint8") * 255
cv2.imwrite("images/escaneo.jpg", frentear)
cv2.imshow("Frenteado", imutils.resize(frentear, height=650))
cv2.waitKey(0)
cv2.destroyAllWindows()
except:
exit()

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