hytos / DTI_PID / DTI_PID / LineDetector.py @ 17cbda7d
이력 | 보기 | 이력해설 | 다운로드 (28.1 KB)
1 |
import sys |
---|---|
2 |
import cv2 |
3 |
import numpy as np |
4 |
import math |
5 |
import shapely |
6 |
|
7 |
|
8 |
class LineDetector(): |
9 |
def __init__(self, image): |
10 |
try:
|
11 |
thresh = 127
|
12 |
self._image = image
|
13 |
self.width = self._image.shape[::-1][0] if self._image is not None else None |
14 |
self.height = self._image.shape[::-1][1] if self._image is not None else None |
15 |
self.Result = np.zeros((self.width, self.height, 3), np.uint8) if self._image is not None else None |
16 |
except Exception as ex: |
17 |
print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
18 |
sys.exc_info()[-1].tb_lineno))
|
19 |
|
20 |
'''
|
21 |
@brief dot product of given two vectors
|
22 |
@author humkyung
|
23 |
@date 2018.04.14
|
24 |
'''
|
25 |
|
26 |
def dotProduct(self, lhs, rhs): |
27 |
return sum([lhs[i] * rhs[i] for i in range(len(lhs))]) |
28 |
|
29 |
'''
|
30 |
@brief get length of given vector
|
31 |
@author humkyung
|
32 |
@date 2018.04.14
|
33 |
'''
|
34 |
|
35 |
def getLength(self, lhs): |
36 |
return math.sqrt(self.dotProduct(lhs, lhs)) |
37 |
|
38 |
'''
|
39 |
@brief get angle between given two vectors
|
40 |
@author humkyung
|
41 |
@date 2018.04.14
|
42 |
@history 2018.05.29 Jeongwoo Add try-exception
|
43 |
@TODO : Need to modify case - Exception
|
44 |
'''
|
45 |
|
46 |
def getAngle(self, lhs, rhs): |
47 |
try:
|
48 |
return math.acos(self.dotProduct(lhs, rhs) / (self.getLength(lhs) * self.getLength(rhs))) |
49 |
except Exception as ex: |
50 |
return sys.float_info.max
|
51 |
|
52 |
"""
|
53 |
@brief get distance between given two points
|
54 |
@author humkyung
|
55 |
@date 2018.04.13
|
56 |
"""
|
57 |
|
58 |
def distanceTo(self, lhs, rhs): |
59 |
dx = rhs[0] - lhs[0] |
60 |
dy = rhs[1] - lhs[1] |
61 |
return math.sqrt(dx * dx + dy * dy)
|
62 |
|
63 |
'''
|
64 |
@brief rotate given point about origin by angle
|
65 |
@author humkyung
|
66 |
@date 2018.04.12
|
67 |
'''
|
68 |
|
69 |
def rotatePoint(self, point, origin, angle): |
70 |
dx = point[0] - origin[0] |
71 |
dy = point[1] - origin[1] |
72 |
|
73 |
qx = origin[0] + math.cos(angle) * dx - math.sin(angle) * dy
|
74 |
qy = origin[1] + math.sin(angle) * dx + math.cos(angle) * dy
|
75 |
return [qx, qy]
|
76 |
|
77 |
'''
|
78 |
@brief check if given two lines are connected
|
79 |
@author humkyung
|
80 |
@date 2018.04.11
|
81 |
'''
|
82 |
|
83 |
def isConnected(self, lhs, rhs, toler=0): |
84 |
length = [] |
85 |
|
86 |
try:
|
87 |
# check if share one point
|
88 |
dx = (lhs[0][0] - rhs[0][0]) |
89 |
dy = (lhs[0][1] - rhs[0][1]) |
90 |
length.append(math.sqrt(dx * dx + dy * dy)) |
91 |
dx = (lhs[1][0] - rhs[0][0]) |
92 |
dy = (lhs[1][1] - rhs[0][1]) |
93 |
length.append(math.sqrt(dx * dx + dy * dy)) |
94 |
dx = (lhs[0][0] - rhs[1][0]) |
95 |
dy = (lhs[0][1] - rhs[1][1]) |
96 |
length.append(math.sqrt(dx * dx + dy * dy)) |
97 |
dx = (lhs[1][0] - rhs[1][0]) |
98 |
dy = (lhs[1][1] - rhs[1][1]) |
99 |
length.append(math.sqrt(dx * dx + dy * dy)) |
100 |
matches = [len for len in length if len < toler] |
101 |
# up to here
|
102 |
|
103 |
return (len(matches) == 1) |
104 |
except Exception as ex: |
105 |
print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
106 |
sys.exc_info()[-1].tb_lineno))
|
107 |
|
108 |
return False |
109 |
|
110 |
"""
|
111 |
@brief check if given two lines lie on one line and share one point
|
112 |
@author humkyung
|
113 |
@date 2018.04.11
|
114 |
"""
|
115 |
|
116 |
def isCollinear(self, lhs, rhs, toler): |
117 |
try:
|
118 |
# print(lhs)
|
119 |
# print(rhs)
|
120 |
if self.isConnected(lhs, rhs, toler): |
121 |
dx1 = lhs[1][0] - lhs[0][0] |
122 |
dy1 = lhs[1][1] - lhs[0][1] |
123 |
length = math.sqrt(dx1 * dx1 + dy1 * dy1) |
124 |
dx1 /= length |
125 |
dy1 /= length |
126 |
dx2 = rhs[1][0] - rhs[0][0] |
127 |
dy2 = rhs[1][1] - rhs[0][1] |
128 |
length = math.sqrt(dx2 * dx2 + dy2 * dy2) |
129 |
dx2 /= length |
130 |
dy2 /= length |
131 |
|
132 |
dx = math.fabs(dx1) - math.fabs(dx2) |
133 |
dy = math.fabs(dy1) - math.fabs(dy2) |
134 |
|
135 |
return (math.sqrt(dx * dx + dy * dy) < 0.00001) |
136 |
except Exception as ex: |
137 |
print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
138 |
sys.exc_info()[-1].tb_lineno))
|
139 |
|
140 |
return False |
141 |
|
142 |
'''
|
143 |
@brief connect given lines
|
144 |
@author humkyung
|
145 |
@date 2018.04.11
|
146 |
@history 2018.05.16 Jeongwoo Connect Lines with long distance points
|
147 |
'''
|
148 |
|
149 |
def connectLines(self, lines, toler): |
150 |
res = [] |
151 |
|
152 |
pts = [] |
153 |
# print(lines)
|
154 |
maxthickness = 0
|
155 |
for line in lines: |
156 |
pts.append(line[0])
|
157 |
pts.append(line[1])
|
158 |
maxthickness = line[2] if maxthickness < line[2] else maxthickness |
159 |
|
160 |
maxLength = None
|
161 |
for lpt in pts: |
162 |
for rpt in pts: |
163 |
if lpt != rpt:
|
164 |
dx = rpt[0] - lpt[0] |
165 |
dy = rpt[1] - lpt[1] |
166 |
length = math.sqrt(dx * dx + dy * dy) |
167 |
if maxLength is None or maxLength < length: |
168 |
maxLength = length |
169 |
res.clear() |
170 |
res.append(lpt) |
171 |
res.append(rpt) |
172 |
res.append(maxthickness) |
173 |
# print(res)
|
174 |
return res
|
175 |
|
176 |
'''
|
177 |
@brief adjust start point
|
178 |
@author humkyung
|
179 |
@date 2018.06.21
|
180 |
@history humkyung 2018.07.31 return -1 thickness tuple if near point is not black
|
181 |
'''
|
182 |
|
183 |
def adjustStartPoint(self, pt, dir): |
184 |
from AppDocData import AppDocData |
185 |
docData = AppDocData.instance() |
186 |
windowSize = docData.getSlidingWindowSize() |
187 |
|
188 |
try:
|
189 |
white = 255
|
190 |
black = 0
|
191 |
|
192 |
norm = [-dir[1], dir[0]] |
193 |
if black == self._image[pt[1] + round(dir[1] * windowSize[1]), pt[0] + round(dir[0] * windowSize[1])]: |
194 |
_pt = [pt[0] + round(dir[0] * windowSize[1]), pt[1] + round(dir[1] * windowSize[1])] |
195 |
else:
|
196 |
found = False
|
197 |
for step in [1, 2, -1, -2]: |
198 |
if black == self._image[pt[1] + round(dir[1] * windowSize[1] + norm[1] * step), pt[0] + round( |
199 |
dir[0] * windowSize[1] + norm[0] * step)]: |
200 |
_pt = [pt[0] + round(dir[0] * windowSize[1] + norm[0] * step), |
201 |
pt[1] + round(dir[1] * windowSize[1] + norm[1] * step)] |
202 |
found = True
|
203 |
break
|
204 |
|
205 |
if not found: return (pt, -1) |
206 |
|
207 |
color = black |
208 |
lhs = [_pt[0], _pt[1]] |
209 |
lhscount = 1
|
210 |
while color != white:
|
211 |
lhs[0] = _pt[0] + round(norm[0] * lhscount) |
212 |
lhs[1] = _pt[1] + round(norm[1] * lhscount) |
213 |
color = self._image[lhs[1], lhs[0]] |
214 |
lhscount += 1
|
215 |
|
216 |
color = black |
217 |
rhs = [_pt[0], _pt[1]] |
218 |
rhscount = 1
|
219 |
while color != white:
|
220 |
rhs[0] = _pt[0] - round(norm[0] * rhscount) |
221 |
rhs[1] = _pt[1] - round(norm[1] * rhscount) |
222 |
color = self._image[rhs[1], rhs[0]] |
223 |
rhscount += 1
|
224 |
|
225 |
size = lhscount + rhscount |
226 |
offset = round((lhscount - rhscount) * 0.5) if size < windowSize[1] else 0 |
227 |
# print(size)
|
228 |
return (
|
229 |
[pt[0] + norm[0] * offset, pt[1] + norm[1] * offset], |
230 |
size) # if size > windowSize[1] else windowSize[1])
|
231 |
except Exception as ex: |
232 |
from App import App |
233 |
from AppDocData import MessageType |
234 |
|
235 |
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
236 |
sys.exc_info()[-1].tb_lineno)
|
237 |
App.mainWnd().addMessage.emit(MessageType.Error, message) if not 'is out of bounds' in message else '' |
238 |
return (pt, -1) |
239 |
|
240 |
return (pt, windowSize[1]) |
241 |
|
242 |
'''
|
243 |
@brief symbol을 기준으로 양쪽으로 이미지에서 직선을 검출한다.
|
244 |
@author humkyung
|
245 |
@date 2018.04.??
|
246 |
@history humkyung 2018.04.11 merge lines which are collinear or connected
|
247 |
humkyung 2018.04.12 rotate connection points by symbol angle
|
248 |
humkyung 2018.04.12 use connection points without rotating(already rotated)
|
249 |
humkyung 2018.09.01 use connector's direction for detecting line
|
250 |
'''
|
251 |
|
252 |
def detectConnectedLine(self, symbol, offsetX, offsetY): |
253 |
res = [] |
254 |
|
255 |
try:
|
256 |
pool = [] |
257 |
if (0 == symbol.angle) or (1.57 == symbol.angle) or (3.14 == symbol.angle) or (4.71 == symbol.angle): |
258 |
for connector in symbol.connectors: |
259 |
## get direction of connector
|
260 |
direction = connector.dir() |
261 |
if direction is None: |
262 |
dx = connector.sceneConnectPoint[0] - symbol.origin[0] |
263 |
dy = connector.sceneConnectPoint[1] - symbol.origin[1] |
264 |
else:
|
265 |
dx, dy = direction[0], direction[1] |
266 |
# print('dx : ' + str(dx) + ', dy : ' + str(dy))
|
267 |
## up to here
|
268 |
|
269 |
length = math.sqrt(dx * dx + dy * dy) |
270 |
if length > 0: |
271 |
dx /= length |
272 |
dy /= length |
273 |
|
274 |
if abs(dx) < 0.1: # vertical line |
275 |
dir = [0, 1 if dy > 0 else -1] |
276 |
pt = [round(connector.sceneConnectPoint[0] - offsetX), |
277 |
round(connector.sceneConnectPoint[1] - offsetY)] |
278 |
pt, thickness = self.adjustStartPoint(pt, dir) |
279 |
if thickness != -1: pool.append([dir, pt, thickness, True if not pool else False]) |
280 |
# print("v")
|
281 |
elif abs(dy) < 0.1: # horizontal line |
282 |
dir = [1 if dx > 0 else -1, 0] |
283 |
pt = [round(connector.sceneConnectPoint[0] - offsetX), |
284 |
round(connector.sceneConnectPoint[1] - offsetY)] |
285 |
pt, thickness = self.adjustStartPoint(pt, dir) |
286 |
if thickness != -1: pool.append([dir, pt, thickness, True if not pool else False]) |
287 |
# print("h")
|
288 |
# print(thickness)
|
289 |
# print(pool)
|
290 |
|
291 |
# print(len(pool))
|
292 |
while len(pool) > 0: |
293 |
dir, pt, thickness, forward = pool.pop()
|
294 |
line = self.detectLine(pt, dir, thickness, forward) |
295 |
if line is not None: |
296 |
line.append(thickness) |
297 |
# print(line)
|
298 |
res.append(line if forward else [line[1], line[0], thickness]) |
299 |
if ([1, 0] == dir) or ([-1, 0] == dir): # turn up/down |
300 |
connectedPt = [line[1][0], pt[1]] |
301 |
pt, thickness = self.adjustStartPoint(connectedPt, [0, 1]) |
302 |
if thickness != -1: pool.append([[0, 1], pt, thickness, forward]) |
303 |
pt, thickness = self.adjustStartPoint(connectedPt, [0, -1]) |
304 |
if thickness != -1: pool.append([[0, -1], pt, thickness, not forward]) |
305 |
elif ([0, 1] == dir) or ([0, -1] == dir): # turn left/right |
306 |
connectedPt = [pt[0], line[1][1]] |
307 |
pt, thickness = self.adjustStartPoint(connectedPt, [1, 0]) |
308 |
if thickness != -1: pool.append([[1, 0], pt, thickness, forward]) |
309 |
pt, thickness = self.adjustStartPoint(connectedPt, [-1, 0]) |
310 |
if thickness != -1: pool.append([[-1, 0], pt, thickness, not forward]) |
311 |
# print(res)
|
312 |
return res
|
313 |
except Exception as ex: |
314 |
from App import App |
315 |
from AppDocData import MessageType |
316 |
|
317 |
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
318 |
sys.exc_info()[-1].tb_lineno)
|
319 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
320 |
|
321 |
'''
|
322 |
@breif merge lines(1.connect collinear lines 2.connect lines)
|
323 |
@author humkyung
|
324 |
@date 2018.04.11
|
325 |
@history humkyung 2018.04.12 continue loop if line is not in connectedLines
|
326 |
Jeongwoo 2018.05.16 Make comments if-statement
|
327 |
'''
|
328 |
|
329 |
def mergeLines(self, connectedLines, toler): |
330 |
try:
|
331 |
for line in connectedLines[:]: |
332 |
if line not in connectedLines: continue |
333 |
matches = [param for param in connectedLines if |
334 |
(line != param) and self.isCollinear(line, param, toler)] |
335 |
if len(matches) > 0: |
336 |
# print(len(matches))
|
337 |
matches.append(line) |
338 |
# if len(matches) > 2: matches = matches[0:2] # pick last two objects
|
339 |
mergedLine = self.connectLines(matches, toler)
|
340 |
if mergedLine is not None and len(mergedLine) > 0: |
341 |
connectedLines.append(mergedLine) |
342 |
for match in matches: connectedLines.remove(match) |
343 |
except Exception as ex: |
344 |
from App import App |
345 |
from AppDocData import MessageType |
346 |
|
347 |
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
348 |
sys.exc_info()[-1].tb_lineno)
|
349 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
350 |
|
351 |
'''
|
352 |
@brief connect line to nearest connection point of symbol
|
353 |
@author humkyung
|
354 |
@date 2018.04.13
|
355 |
'''
|
356 |
|
357 |
def connectLineToSymbol(self, line, symbol, toler): |
358 |
startPt = list(line.startPoint())
|
359 |
distStart = [(self.distanceTo(startPt, (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])), |
360 |
(connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])) for connector in |
361 |
symbol.connectors] |
362 |
distStart.sort() |
363 |
|
364 |
endPt = list(line.endPoint())
|
365 |
distEnd = [(self.distanceTo(endPt, (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])), |
366 |
(connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])) for connector in |
367 |
symbol.connectors] |
368 |
distEnd.sort() |
369 |
|
370 |
if distStart[0][0] < distEnd[0][0]: |
371 |
dx = distStart[0][1][0] - startPt[0] |
372 |
dy = distStart[0][1][1] - startPt[1] |
373 |
dir = (endPt[0] - startPt[0], endPt[1] - startPt[1]) |
374 |
if abs(dir[0]) > abs(dir[1]): |
375 |
endPt[1] += dy
|
376 |
else:
|
377 |
endPt[0] += dx
|
378 |
|
379 |
res = line.connect_if_possible(symbol, toler) |
380 |
if res:
|
381 |
line.set_line([res[1], res[2]]) |
382 |
else:
|
383 |
dx = distEnd[0][1][0] - endPt[0] |
384 |
dy = distEnd[0][1][1] - endPt[1] |
385 |
dir = (endPt[0] - startPt[0], endPt[1] - startPt[1]) |
386 |
if abs(dir[0]) > abs(dir[1]): |
387 |
startPt[1] += dy
|
388 |
else:
|
389 |
startPt[0] += dx
|
390 |
|
391 |
res = line.connect_if_possible(symbol, toler) |
392 |
if res:
|
393 |
line.set_line([res[1], res[2]]) |
394 |
|
395 |
'''
|
396 |
@brief extend line to intersection point
|
397 |
@author humkyung
|
398 |
@date 2018.04.14
|
399 |
'''
|
400 |
|
401 |
def connectLineToLine(self, lhs, rhs, toler=20): |
402 |
try:
|
403 |
res = lhs.connect_if_possible(rhs, toler) |
404 |
if not res: |
405 |
return
|
406 |
|
407 |
lhs_start = list(lhs.startPoint())
|
408 |
lhs_end = list(lhs.endPoint())
|
409 |
rhs_start = list(rhs.startPoint())
|
410 |
rhs_end = list(rhs.endPoint())
|
411 |
|
412 |
dx = rhs_end[0] - rhs_start[0] |
413 |
dy = rhs_end[1] - rhs_start[1] |
414 |
length = math.sqrt(dx * dx + dy * dy) |
415 |
if length == 0: return |
416 |
dx /= length |
417 |
dy /= length |
418 |
rightLine = [(rhs_start[0] - dx * toler, rhs_start[1] - dy * toler), |
419 |
(rhs_end[0] + dx * toler, rhs_end[1] + dy * toler)] |
420 |
shapelyRightLine = shapely.geometry.LineString(rightLine) |
421 |
|
422 |
dx = lhs_end[0] - lhs_start[0] |
423 |
dy = lhs_end[1] - lhs_start[1] |
424 |
length = math.sqrt(dx * dx + dy * dy) |
425 |
if length == 0: return |
426 |
dx /= length |
427 |
dy /= length |
428 |
line = [(lhs_start[0] - dx * toler, lhs_start[1] - dy * toler), |
429 |
(lhs_end[0] + dx * toler, lhs_end[1] + dy * toler)] |
430 |
shapelyLine = shapely.geometry.LineString(line) |
431 |
|
432 |
pt = shapelyRightLine.intersection(shapelyLine) |
433 |
if (pt is not None) and (type(pt) == shapely.geometry.point.Point): |
434 |
if lhs.is_external_point(pt):
|
435 |
if self.distanceTo(lhs_start, (pt.x, pt.y)) < self.distanceTo(lhs_end, (pt.x, pt.y)): |
436 |
lhs_start[0] = pt.x
|
437 |
lhs_start[1] = pt.y
|
438 |
else:
|
439 |
lhs_end[0] = pt.x
|
440 |
lhs_end[1] = pt.y
|
441 |
|
442 |
lhs.set_line([lhs_start, lhs_end]) |
443 |
else:
|
444 |
if self.distanceTo(lhs_start, (pt.x, pt.y)) < toler: |
445 |
lhs_start[0] = pt.x
|
446 |
lhs_start[1] = pt.y
|
447 |
elif self.distanceTo(lhs_end, (pt.x, pt.y)) < toler: |
448 |
lhs_end[0] = pt.x
|
449 |
lhs_end[1] = pt.y
|
450 |
|
451 |
lhs.set_line([lhs_start, lhs_end]) |
452 |
|
453 |
if rhs.is_external_point(pt):
|
454 |
if self.distanceTo(rhs_start, (pt.x, pt.y)) < self.distanceTo(rhs_end, (pt.x, pt.y)): |
455 |
rhs_start[0] = pt.x
|
456 |
rhs_start[1] = pt.y
|
457 |
else:
|
458 |
rhs_end[0] = pt.x
|
459 |
rhs_end[1] = pt.y
|
460 |
|
461 |
rhs.set_line([rhs_start, rhs_end]) |
462 |
|
463 |
else:
|
464 |
if self.distanceTo(rhs_start, (pt.x, pt.y)) < toler: |
465 |
rhs_start[0] = pt.x
|
466 |
rhs_start[1] = pt.y
|
467 |
elif self.distanceTo(rhs_end, (pt.x, pt.y)) < toler: |
468 |
rhs_end[0] = pt.x
|
469 |
rhs_end[1] = pt.y
|
470 |
|
471 |
rhs.set_line([rhs_start, rhs_end]) |
472 |
except Exception as ex: |
473 |
from App import App |
474 |
from AppDocData import MessageType |
475 |
|
476 |
message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
477 |
sys.exc_info()[-1].tb_lineno)
|
478 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
479 |
|
480 |
'''
|
481 |
@brief detech line thickness
|
482 |
@author humkyung
|
483 |
@date 2018.05.18
|
484 |
'''
|
485 |
|
486 |
def detectLineThickness(self, pt, dir): |
487 |
import math |
488 |
from AppDocData import AppDocData |
489 |
docData = AppDocData.instance() |
490 |
windowSize = docData.getSlidingWindowSize() |
491 |
|
492 |
white = 255
|
493 |
black = 0
|
494 |
color = white |
495 |
|
496 |
norm = [-dir[1], dir[0]] |
497 |
if black == self._image[pt[1] + round(dir[1] * windowSize[1]), pt[0] + round(dir[0] * windowSize[1])]: |
498 |
_pt = [pt[0] + round(dir[0] * windowSize[1]), pt[1] + round(dir[1] * windowSize[1])] |
499 |
else:
|
500 |
return windowSize[1] |
501 |
|
502 |
color = black |
503 |
lhs = [pt[0], pt[1]] |
504 |
count = 1
|
505 |
while color != white:
|
506 |
lhs[0] = pt[0] + round(norm[0] * count) |
507 |
lhs[1] = pt[1] + round(norm[1] * count) |
508 |
color = self._image[lhs[1], lhs[0]] |
509 |
count += 1
|
510 |
|
511 |
color = black |
512 |
rhs = [pt[0], pt[1]] |
513 |
count = 1
|
514 |
while color != white:
|
515 |
rhs[0] = pt[0] - round(norm[0] * count) |
516 |
rhs[1] = pt[1] - round(norm[1] * count) |
517 |
color = self._image[rhs[1], rhs[0]] |
518 |
count += 1
|
519 |
|
520 |
dx = rhs[0] - lhs[0] |
521 |
dy = rhs[1] - lhs[1] |
522 |
return math.sqrt(dx * dx + dy * dy)
|
523 |
|
524 |
def detectLine(self, pt, dir, thickness, forward): |
525 |
"""detect a line along given direction"""
|
526 |
from AppDocData import AppDocData |
527 |
|
528 |
lineLengthConfigs = AppDocData.instance().getConfigs('Small Line Minimum Length', 'Min Length') |
529 |
lineMinLength = int(lineLengthConfigs[0].value) if 1 == len(lineLengthConfigs) else 10 |
530 |
try:
|
531 |
white = [255]
|
532 |
windowSize = AppDocData.instance().getSlidingWindowSize() |
533 |
xHalf = round(windowSize[0] * 0.5) |
534 |
yHalf = round(windowSize[1] * 0.5) |
535 |
|
536 |
if ([1, 0] == dir): |
537 |
image = self._image[(pt[1] - yHalf):(pt[1] + yHalf), pt[0]:self.width] |
538 |
imgWidth, imgHeight = image.shape[::-1]
|
539 |
index = 0
|
540 |
for i in range(imgWidth - windowSize[0]): |
541 |
window = image[0:windowSize[1], i:i + windowSize[0]] |
542 |
if (white == window[0:windowSize[1], 0:windowSize[0]]).all(): break |
543 |
index += 1
|
544 |
if index > lineMinLength:
|
545 |
# self._image[(pt[1]-yHalf):(pt[1]+yHalf), pt[0]:(pt[0]+i)] = white
|
546 |
cv2.line(self._image, (pt[0], pt[1]), (pt[0] + i, pt[1]), 255, thickness) |
547 |
return [[pt[0], pt[1]], [pt[0] + i - round(thickness * 0.5), pt[1]]] |
548 |
elif ([-1, 0] == dir): |
549 |
image = self._image[(pt[1] - yHalf):(pt[1] + yHalf), 0:pt[0]] |
550 |
imgWidth, imgHeight = image.shape[::-1]
|
551 |
index = 0
|
552 |
for i in range(imgWidth - windowSize[0], -1, -1): |
553 |
window = image[0:windowSize[1], i:i + windowSize[0]] |
554 |
if (white == window[0:windowSize[1], 0:windowSize[0]]).all(): break |
555 |
index += 1
|
556 |
if index > lineMinLength:
|
557 |
# self._image[int(pt[1]-yHalf):int(pt[1]+yHalf), (i+windowSize[0]+yHalf):pt[0]] = white
|
558 |
cv2.line(self._image, (i + windowSize[0], pt[1]), (pt[0], pt[1]), 255, thickness) |
559 |
return [[pt[0], pt[1]], [i + windowSize[0] + round(thickness * 0.5), pt[1]]] |
560 |
elif ([0, 1] == dir): |
561 |
windowSize.reverse() |
562 |
xHalf = round(windowSize[0] * 0.5) |
563 |
yHalf = round(windowSize[1] * 0.5) |
564 |
|
565 |
image = self._image[pt[1]:self.height, int(pt[0] - xHalf):int(pt[0] + xHalf)] |
566 |
imgWidth, imgHeight = image.shape[::-1]
|
567 |
index = 0
|
568 |
for i in range(imgHeight - windowSize[1]): |
569 |
window = image[i:i + windowSize[1], 0:windowSize[0]] |
570 |
if (white == window[0:windowSize[1], 0:windowSize[0]]).all(): break |
571 |
index += 1
|
572 |
if index > lineMinLength:
|
573 |
# self._image[(pt[1]):(pt[1]+i), (pt[0]-xHalf):(pt[0]+xHalf)] = white
|
574 |
cv2.line(self._image, (pt[0], pt[1]), (pt[0], pt[1] + i), 255, thickness) |
575 |
return [[pt[0], pt[1]], [pt[0], pt[1] + i - round(thickness * 0.5)]] |
576 |
elif ([0, -1] == dir): |
577 |
windowSize.reverse() |
578 |
xHalf = round(windowSize[0] * 0.5) |
579 |
yHalf = round(windowSize[1] * 0.5) |
580 |
|
581 |
image = self._image[0:pt[1], (pt[0] - xHalf):(pt[0] + xHalf)] |
582 |
imgWidth, imgHeight = image.shape[::-1]
|
583 |
index = 0
|
584 |
for i in range(imgHeight - windowSize[1], -1, -1): |
585 |
window = image[i:i + windowSize[1], 0:windowSize[0]] |
586 |
if (white == window[0:windowSize[1], 0:windowSize[0]]).all(): break |
587 |
index += 1
|
588 |
if index > lineMinLength:
|
589 |
# self._image[(i+windowSize[1]):pt[1], (pt[0]-xHalf):(pt[0]+xHalf)] = white
|
590 |
cv2.line(self._image, (pt[0], i + windowSize[1]), (pt[0], pt[1]), 255, thickness) |
591 |
return [[pt[0], pt[1]], [pt[0], i + windowSize[1] + round(thickness * 0.5)]] |
592 |
except Exception as ex: |
593 |
from App import App |
594 |
from AppDocData import MessageType |
595 |
|
596 |
message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename, |
597 |
sys.exc_info()[-1].tb_lineno)
|
598 |
App.mainWnd().addMessage.emit(MessageType.Error, message) |
599 |
|
600 |
return None |
601 |
|
602 |
def detectLineWithoutSymbol(self, area): # path, offsetX, offsetY): |
603 |
'''
|
604 |
@brief detect remain line after detection using symbol info
|
605 |
@author euisung
|
606 |
@date 2019.03.28
|
607 |
'''
|
608 |
import os |
609 |
from AppDocData import AppDocData |
610 |
from HoughBundler import HoughBundler |
611 |
|
612 |
docData = AppDocData.instance() |
613 |
img = area.img |
614 |
|
615 |
if True: |
616 |
imgNot = np.ones(self._image.shape, np.uint8) * 255 |
617 |
imgNot = cv2.bitwise_xor(self._image, imgNot)
|
618 |
imgNot = cv2.erode(imgNot, np.ones((2, 2), np.uint8)) |
619 |
|
620 |
contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
621 |
if len(contours) is 0: |
622 |
return []
|
623 |
|
624 |
smallContours = [] |
625 |
minimumSize = docData.getConfigs('Filter', 'MinimumSize') * 3 |
626 |
lineLengthConfigs = docData.getConfigs('Small Line Minimum Length', 'Min Length') |
627 |
lineMinLength = int(lineLengthConfigs[0].value) if 1 == len(lineLengthConfigs) else 30 |
628 |
for contour in contours: |
629 |
[x, y, w, h] = cv2.boundingRect(contour) |
630 |
|
631 |
# remove too small one
|
632 |
if len(minimumSize) is 1: |
633 |
if (w * h < int(minimumSize[0].value) * int(minimumSize[0].value)): |
634 |
smallContours.append(contour) |
635 |
imgNotRemoveSmall = cv2.drawContours(imgNot, smallContours, -1, 0, -1) |
636 |
|
637 |
# detect line
|
638 |
edged = cv2.Canny(imgNotRemoveSmall, 100, 200) |
639 |
|
640 |
rate = 25
|
641 |
lines = cv2.HoughLinesP(image=edged, rho=1, theta=np.pi / 180, threshold=rate, |
642 |
minLineLength=lineMinLength * 3, maxLineGap=25) |
643 |
if lines is None: |
644 |
return []
|
645 |
|
646 |
houghBundler = HoughBundler().process_lines(lines, None)
|
647 |
|
648 |
remainLines = [] |
649 |
for line in houghBundler: |
650 |
remainLines.append([[line[0][0], line[0][1]], [line[1][0], line[1][1]]]) |
651 |
|
652 |
# border line check
|
653 |
borderRate = 0.07
|
654 |
borderY, borderX = round(imgNot.shape[0] * borderRate), round(imgNot.shape[1] * borderRate) |
655 |
|
656 |
for index in range(len(remainLines) - 1, -1, -1): |
657 |
if remainLines[index][0][0] < borderX and remainLines[index][1][0] < borderX: |
658 |
remainLines.pop(index) |
659 |
elif remainLines[index][0][0] > self._image.shape[1] - borderX and remainLines[index][1][0] > \ |
660 |
self._image.shape[1] - borderX: |
661 |
remainLines.pop(index) |
662 |
elif remainLines[index][0][1] < borderY and remainLines[index][1][1] < borderY: |
663 |
remainLines.pop(index) |
664 |
elif remainLines[index][0][1] > self._image.shape[0] - borderY and remainLines[index][1][1] > \ |
665 |
self._image.shape[0] - borderY: |
666 |
remainLines.pop(index) |
667 |
|
668 |
return remainLines
|
669 |
|
670 |
'''
|
671 |
@brief save the result of image
|
672 |
@author humkyung
|
673 |
@date 2018.04.14
|
674 |
'''
|
675 |
|
676 |
def saveImage(self): |
677 |
import datetime |
678 |
from AppDocData import AppDocData |
679 |
|
680 |
try:
|
681 |
nowDate = datetime.datetime.now() |
682 |
path = AppDocData.instance().getCurrentProject().getTempPath() + '/{}_{}_line_detector.png'.format(
|
683 |
nowDate.strftime('%Y-%m-%d'), nowDate.strftime('%H %M %S')) |
684 |
cv2.imwrite(path, self._image)
|
685 |
except Exception as ex: |
686 |
print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, |
687 |
sys.exc_info()[-1].tb_lineno))
|