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