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