-
Notifications
You must be signed in to change notification settings - Fork 2
/
pyANPD.py
192 lines (139 loc) · 5.91 KB
/
pyANPD.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
import math
import cv2
import numpy as np
import time
def validate_contour(rect, img, aspect_ratio_range, area_range):
# rect = cv2.minAreaRect(contour)
img_width = img.shape[1]
# img_height = img.shape[0]
box = cv2.boxPoints(rect)
box = np.int0(box)
# X = rect[0][0]
# Y = rect[0][1]
# angle = rect[2]
width = rect[1][0]
height = rect[1][1]
# angle = (angle + 180) if width < height else (angle + 90)
output = False
if (width > 0 and height > 0) and ((width < img_width / 2.0) and (height < img_width / 2.0)):
aspect_ratio = float(width) / height if width > height else float(height) / width
if aspect_ratio_range[0] <= aspect_ratio <= aspect_ratio_range[1]:
if area_range[0] <= width*height <= area_range[1]:
box_copy = np.copy(box[1:])
point = np.copy(box[0])
dists = np.linalg.norm(point-box_copy, axis=1) # finds normalized euclidean distances between the pts
sorted_dists = np.sort(dists)
opposite_pt_index = np.where(dists == sorted_dists[1])
opposite_point = box_copy[opposite_pt_index][0]
tmp_angle = 90
if abs(point[0] - opposite_point[0]) > 0:
tmp_angle = abs(float(point[1] - opposite_point[1])) / abs(point[0] - opposite_point[0])
tmp_angle = rad_to_deg(math.atan(tmp_angle))
if tmp_angle <= 9:
output = True
return output
class PlateBuffer():
def __init__(self, size):
self.list = np.empty(shape=(size,), dtype=object)
def __iter__(self):
return iter(self.list)
def __getitem__(self, item):
return self.list[item]
# equivalent to appending the current last element to the end of this deque.
def put_no_val(self):
self.list = np.roll(self.list, -1)
self.list[-1] = self.list[-2]
def append(self, val):
self.list = np.roll(self.list, -1)
self.list[-1] = val
class Plate():
""" Rectangle of the plate """
def __init__(self, contour):
self.contour = contour
self.rect = cv2.minAreaRect(contour)
self.box = cv2.boxPoints(self.rect)
self.box = np.int0(self.box)
Xs = self.box[:,0]
Ys = self.box[:,1]
self.x1 = np.min(Xs)
self.x2 = np.max(Xs)
self.y1 = np.min(Ys)
self.y2 = np.max(Ys)
self.center = (self.x1 + self.x2) / 2, (self.y1 + self.y2) / 2
self.size = self.x2 - self.x1, self.y2 - self.y1
def distance_to(self, point):
distance = np.linalg.norm( np.subtract(self.center,point)) # Euclidean
return distance
def deg_to_rad(angle):
return angle * np.pi / 180.0
def rad_to_deg(angle):
return angle * 180 / np.pi
def enhance(img):
kernel = np.array([[-1, 0, 1], [-2, 0, 2], [1, 0, 1]])
return cv2.filter2D(img, -1, kernel)
def find_contours(raw_image, debug, kernel_scale, thrs1, thrs2, showsteps, **options):
se_shape = (16, 4)
# show_steps = False # visualize edge detection?
# if options.get('showsteps' == 'true'):
# show_steps = True
if options.get('type') == 'rect':
se_shape = (17, 4)
if options.get('type') == 'est':
se_shape = (2 * kernel_scale, 1 * kernel_scale)
elif options.get('type') == 'square':
se_shape = (7, 6)
# raw_image = cv2.imread(name,1) # Jakob's note: this method used to eat filenames
input_image = np.copy(raw_image)
gray = cv2.cvtColor(input_image, cv2.COLOR_BGR2GRAY)
#gray = enhance(gray)
#cv2.imshow('enhance', gray)
# gray = cv2.GaussianBlur(gray, (5,5), 0)
# cv2.imshow('blur', gray)
#gray = cv2.Sobel(gray, -1, 1, 0)
canny = cv2.Canny(gray, thrs1, thrs2, apertureSize=5)
if showsteps:
cv2.imshow('canny', canny);
#h,sobel = cv2.threshold(gray,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)
se = cv2.getStructuringElement(cv2.MORPH_RECT, se_shape)
gray = cv2.morphologyEx(canny, cv2.MORPH_CLOSE, se)
if showsteps:
cv2.imshow('morphologyEx', gray);
ed_img = np.copy(gray)
_, contours, _ = cv2.findContours(canny, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
font = cv2.FONT_HERSHEY_SIMPLEX
returned_plates = []
for contour in contours:
aspect_ratio_range = (2.2, 12)
area_range = (500, 18000)
if options.get('type') == 'rect':
aspect_ratio_range = (2.2, 12)
area_range = (500, 18000)
elif options.get('type') == 'square':
aspect_ratio_range = (1, 2)
area_range = (300, 8000)
##Exact aspect ratio of eu plate is 4.6 (based on 520 x 113 mm)
elif options.get('type') == 'est':
aspect_ratio_range = (4, 5)
area_range = (40, 18000)
plate = Plate(contour)
if validate_contour(plate.rect, gray, aspect_ratio_range, area_range):
angle = plate.rect[2]
if angle < -45:
angle += 90
W = plate.rect[1][0]
H = plate.rect[1][1]
# aspect_ratio = float(W) / H if W > H else float(H) / W
# center = ((x1 + x2) / 2, (y1 + y2) / 2)
# size = (x2 - x1, y2 - y1)
M = cv2.getRotationMatrix2D((plate.size[0] / 2, plate.size[1] / 2), angle, 1.0);
tmp = cv2.getRectSubPix(ed_img, plate.size, plate.center)
tmp = cv2.warpAffine(tmp, M, plate.size)
TmpW = H if H > W else W
TmpH = H if H < W else W
tmp = cv2.getRectSubPix(tmp, (int(TmpW), int(TmpH)), (plate.size[0] / 2, plate.size[1] / 2))
__, tmp = cv2.threshold(tmp, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
white_pixels = np.count_nonzero(tmp)
edge_density = float(white_pixels) / (tmp.shape[0] * tmp.shape[1])
if edge_density > 0.9:
returned_plates.append(plate)
return np.array(returned_plates)