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