I'm trying to create a program that will take a long time to explain here, so I'm gonna tell you guys the part that I need help with.
Here I need to detect a rectangle(which will be a license plate in our example). It does the recognition almost perfectly but I want it more precise. Here is the example image I used.
As you can see, It does a fairly good job at finding it but I want to take the rounded corners into consideration too.
Here is the source code
import numpy as np
import cv2
import imutils
def find_edges(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (3, 3), 0)
edged = cv2.Canny(image=gray, threshold1=100, threshold2=200)
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 4:
screenCnt = approx
break
return screenCnt
image = cv2.imread('img/plate.jpeg')
cnt = find_edges(image)
cv2.drawContours(image, [cnt], -1, (0, 255, 0), 2)
cv2.imshow('Outline', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Is there any way that I can achieve this and how so, or am I tryharding too much on this?
Edit: Adding sample image. Sorry for not including before, my fault.
CodePudding user response:
First of all, in your find_edges
function, I replaced the line screenCnt = approx
with screenCnt = c
, in order to keep all the coordinates in the resulting detected contour:
def find_edges(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (3, 3), 0)
edged = cv2.Canny(image=gray, threshold1=100, threshold2=200)
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 4:
screenCnt = c
break
return screenCnt
Then, I defined a function, get_opposites
, that will take in a contour, and return two coordinates from the contour that are farthest apart from each others:
def get_opposites(cnt):
current_max = 0
c = None
for a, b in combinations(cnt, 2):
current_distance = np.linalg.norm(a - b)
if current_distance > current_max:
current_max = current_distance
c = a, b
return c
Next, I separate the contour detected from the image (using the find_edges
function you defined my alteration) into two part; the first part containing the top-left bottom-right quarter of the contour, and the second part containing the top-right bottom-left quarter of the contour:
image = cv2.imread('img/plate.jpeg')
cnt = find_edges(image)
xs = cnt[..., 0]
ys = cnt[..., 1]
x_mid = (np.amin(xs) np.amax(xs)) // 2
y_mid = (np.amin(ys) np.amax(ys)) // 2
tl_br = cnt[((ys < y_mid) & (xs < x_mid)) | ((ys > y_mid) & (xs > x_mid))]
tr_bl = cnt[((ys > y_mid) & (xs < x_mid)) | ((ys < y_mid) & (xs > x_mid))]
Finally, I use the `` function to get two coordinates from each part, and place them into a numpy array to be drawn onto the image:
p1, p3 = get_opposites(tl_br)
p2, p4 = get_opposites(tr_bl)
cv2.polylines(image, np.array([[p1, p2, p3, p4]], np.int32), True, (0, 255, 0), 2)
cv2.imshow('Outline', image)
cv2.waitKey(0)
cv2.destroyAllWindows()
Output:
All together:
import numpy as np
import cv2
import imutils
from itertools import combinations
def find_edges(image):
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (3, 3), 0)
edged = cv2.Canny(image=gray, threshold1=100, threshold2=200)
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
for c in cnts:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 4:
screenCnt = c
break
return screenCnt
def get_opposites(cnt):
current_max = 0
c = None
for a, b in combinations(cnt, 2):
current_distance = np.linalg.norm(a - b)
if current_distance > current_max:
current_max = current_distance
c = a, b
return c
image = cv2.imread('img/plate.jpeg')
cnt = find_edges(image)
xs = cnt[..., 0]
ys = cnt[..., 1]
x_mid = (np.amin(xs) np.amax(xs)) // 2
y_mid = (np.amin(ys) np.amax(ys)) // 2
tl_br = cnt[((ys < y_mid) & (xs < x_mid)) | ((ys > y_mid) & (xs > x_mid))]
tr_bl = cnt[((ys > y_mid) & (xs < x_mid)) | ((ys < y_mid) & (xs > x_mid))]
p1, p3 = get_opposites(tl_br)
p2, p4 = get_opposites(tr_bl)
cv2.polylines(image, np.array([[p1, p2, p3, p4]], np.int32), True, (0, 255, 0), 2)
cv2.imshow('Outline', image)
cv2.waitKey(0)
cv2.destroyAllWindows()