개정판 43fa2b90
fixed issue #480: Line 인식
DTI_PID/DTI_PID/AppDocData.py | ||
---|---|---|
151 | 151 |
self._colors = value |
152 | 152 |
|
153 | 153 |
def setCurrentPidSource(self, image): |
154 |
self.imgWidth, self.imgHeight = image.size |
|
155 |
|
|
154 | 156 |
self.currentPidSource = Source(image) |
155 | 157 |
|
156 | 158 |
def getCurrentPidSource(self): |
DTI_PID/DTI_PID/LineDetector.py | ||
---|---|---|
461 | 461 |
xHalf = round(windowSize[0]*0.5) |
462 | 462 |
yHalf = round(windowSize[1]*0.5) |
463 | 463 |
|
464 |
print('type({}) is {}'.format(pt, type(pt))) |
|
465 |
|
|
466 | 464 |
if ([1,0] == dir): |
467 | 465 |
image = self._image[(pt[1]-yHalf):(pt[1]+yHalf), pt[0]:self.width] |
468 | 466 |
imgWidth, imgHeight = image.shape[::-1] |
DTI_PID/DTI_PID/MainWindow.py | ||
---|---|---|
305 | 305 |
if os.path.isfile(self.path): |
306 | 306 |
self.graphicsView.useDefaultCommand() |
307 | 307 |
|
308 |
baseName = os.path.basename(self.path) |
|
309 | 308 |
docData = AppDocData.instance() |
309 |
docData.imgName = os.path.splitext(os.path.basename(self.path))[0] |
|
310 | 310 |
docData.setCurrentPidSource(Image.open(self.path)) |
311 |
self.resultTreeWidget.setCurrentPID(baseName)
|
|
311 |
self.resultTreeWidget.setCurrentPID(docData.imgName)
|
|
312 | 312 |
|
313 | 313 |
''' TEST CODE ''' |
314 | 314 |
area = docData.getArea('Drawing') |
315 | 315 |
area.img = cv2.imread(self.path, 1) |
316 | 316 |
|
317 | 317 |
## Load data on xml |
318 |
path = os.path.join(AppDocData.instance().getCurrentProject().getTempPath(), os.path.splitext(baseName)[0] + '.xml')
|
|
318 |
path = os.path.join(AppDocData.instance().getCurrentProject().getTempPath(), docData.imgName + '.xml')
|
|
319 | 319 |
if os.path.isfile(path): self.loadRecognitionResultFromXml(path) |
320 |
|
|
321 |
# DEBUG |
|
322 |
''' |
|
323 |
if __debug__: |
|
324 |
from LineDetector import LineDetector |
|
325 |
from LineNoTracer import LineNoTracer |
|
326 |
self.loadRecognitionResultFromXml('d:/Projects/DTIPID/DTI_PID/DTI_PID/SG_TEST/output/UY1-K-2005G_P1_300dpi_black.xml') |
|
327 |
|
|
328 |
# detect line |
|
329 |
connectedLines = [] |
|
330 |
|
|
331 |
img = cv2.cvtColor(cv2.imread(self.path), cv2.COLOR_BGR2GRAY) |
|
332 |
detector = LineDetector(img) |
|
333 |
symbols = [] |
|
334 |
for item in self.graphicsView.scene.items(): |
|
335 |
if type(item) is SymbolSvgItem: |
|
336 |
symbols.append(item) |
|
337 |
color = QColor(random.randrange(0,255), random.randrange(0,255), random.randrange(0,255)) |
|
338 |
res = detector.detectConnectedLine(item, 0, 0) |
|
339 |
if res is not None: |
|
340 |
for line in res: connectedLines.append(line) |
|
341 |
|
|
342 |
texts = [] |
|
343 |
for item in self.graphicsView.scene.items(): |
|
344 |
if (type(item) is QEngineeringTextItem): texts.append(item) |
|
345 |
|
|
346 |
if len(connectedLines) > 1: |
|
347 |
detector.mergeLines(connectedLines, toler=20) |
|
348 |
# connect line to symbol |
|
349 |
try: |
|
350 |
for line in connectedLines: |
|
351 |
matches = [symbol for symbol in symbols if symbol.isConnectable(line, (0,0), toler=40)] |
|
352 |
for symbol in matches: |
|
353 |
detector.connectLineToSymbol(line, (0,0), symbol) |
|
354 |
except Exception as ex: |
|
355 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
|
356 |
# up to here |
|
357 |
|
|
358 |
# connect line to line |
|
359 |
try: |
|
360 |
for line in connectedLines[:]: |
|
361 |
matches = [it for it in connectedLines if (line != it) and (not detector.isParallel(line, it))] |
|
362 |
for match in matches: |
|
363 |
detector.connectLineToLine(match, line) |
|
364 |
except Exception as ex: |
|
365 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
|
366 |
# up to here |
|
367 |
|
|
368 |
lines = [] |
|
369 |
for pts in connectedLines: |
|
370 |
processLine = QEngineeringLineItem() |
|
371 |
lines.append(processLine) |
|
372 |
for pt in pts: |
|
373 |
processLine._pol.append(QPointF(pt[0], pt[1])) |
|
374 |
processLine._path.addPolygon(processLine._pol) |
|
375 |
processLine.setPath(processLine._path) |
|
376 |
processLine.setPen(QPen(color, 5, Qt.SolidLine)) |
|
377 |
self.graphicsView.scene.addItem(processLine) |
|
378 |
detector.saveImage() |
|
379 |
|
|
380 |
# trace line no |
|
381 |
tracer = LineNoTracer(symbols, lines, texts) |
|
382 |
tracer.execute() |
|
383 |
# up to here |
|
384 |
|
|
385 |
# construct line no item |
|
386 |
docData = AppDocData.instance() |
|
387 |
for text in docData.lineNos: |
|
388 |
item = self.resultTreeWidget.addTreeItem(self.resultTreeWidget.root, text) |
|
389 |
|
|
390 |
if 1 == len(text.conns): |
|
391 |
# iterate connected items |
|
392 |
pool = [] |
|
393 |
visited = [] |
|
394 |
pool.append(text.conns[0]) |
|
395 |
while len(pool) > 0: |
|
396 |
it = pool.pop() |
|
397 |
if type(it) is SymbolSvgItem: self.resultTreeWidget.addTreeItem(item, it) |
|
398 |
visited.append(it) |
|
399 |
for conn in it.conns: |
|
400 |
if (conn is not None) and (conn not in visited): pool.append(conn) |
|
401 |
# up to here |
|
402 |
|
|
403 |
# up to here |
|
404 |
# up to here |
|
405 |
''' |
|
406 | 320 |
except Exception as ex: |
407 | 321 |
print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)) |
408 | 322 |
|
DTI_PID/DTI_PID/Shapes/QEngineeringLineItem.py | ||
---|---|---|
260 | 260 |
@history Jeongwoo 18.05.15 Add check pt's type |
261 | 261 |
Jeongwoo 18.05.16 Add length == 0 check |
262 | 262 |
''' |
263 |
def isConnectable(self, line): |
|
263 |
def isConnectable(self, line, toler=20):
|
|
264 | 264 |
import math |
265 | 265 |
|
266 | 266 |
startPt = line.startPoint() |
... | ... | |
273 | 273 |
return False |
274 | 274 |
dx /= length |
275 | 275 |
dy /= length |
276 |
extendedLine = [(startPt[0] - dx*20, startPt[1] - dy*20), (endPt[0] + dx*20, endPt[1] + dy*20)]
|
|
276 |
extendedLine = [(startPt[0] - dx*toler, startPt[1] - dy*toler), (endPt[0] + dx*toler, endPt[1] + dy*toler)]
|
|
277 | 277 |
pt = self.intersection(extendedLine) |
278 | 278 |
|
279 | 279 |
return (pt is not None) and (type(pt) == shapely.geometry.point.Point) |
280 | 280 |
|
281 | 281 |
''' |
282 |
@brief check if two lines are jointable |
|
283 |
@author humkyung |
|
284 |
@date 2018.06.26 |
|
285 |
''' |
|
286 |
def isJointable(self, line, toler=5): |
|
287 |
import math |
|
288 |
|
|
289 |
lhs = [self.startPoint(), self.endPoint()] |
|
290 |
rhs = [line.startPoint(), line.endPoint()] |
|
291 |
|
|
292 |
for pt in lhs: |
|
293 |
for _pt in rhs: |
|
294 |
dx = _pt[0] - pt[0] |
|
295 |
dy = _pt[1] - pt[1] |
|
296 |
if math.sqrt(dx*dx + dy*dy) < toler: |
|
297 |
return True |
|
298 |
|
|
299 |
return False |
|
300 |
|
|
301 |
''' |
|
282 | 302 |
@brief check if two lines are extendable |
283 | 303 |
@author humkyung |
284 | 304 |
@date 2018.06.25 |
DTI_PID/DTI_PID/Shapes/QEngineeringRunItem.py | ||
---|---|---|
111 | 111 |
while len(connectedLines) > 0: |
112 | 112 |
line = connectedLines.pop() |
113 | 113 |
flag = (line.conns[0] is not None and issubclass(type(line.conns[0]),SymbolSvgItem)) or (line.conns[1] is not None and issubclass(type(line.conns[1]),SymbolSvgItem)) |
114 |
if flag: |
|
115 |
if line.isHorizontal(): |
|
116 |
groupItems = [item for item in connectedLines if line.isExtendable(item)] |
|
117 |
elif line.isVertical(): |
|
118 |
groupItems = [item for item in connectedLines if line.isExtendable(item)] |
|
119 |
else: |
|
120 |
groupItems = [] |
|
114 |
groupItems = [item for item in connectedLines if line.isExtendable(item)] if flag else [] |
|
121 | 115 |
|
122 | 116 |
pts = [] |
123 | 117 |
pts.append(line.startPoint()) |
... | ... | |
125 | 119 |
for item in groupItems: |
126 | 120 |
pts.append(item.startPoint()) |
127 | 121 |
pts.append(item.endPoint()) |
122 |
|
|
128 | 123 |
longestPoints = self.getLongestTwoPoints(pts) |
129 | 124 |
if 2 == len(longestPoints): |
130 | 125 |
tmp = QEngineeringLineItem() |
131 | 126 |
if line.isHorizontal(): |
132 |
adjustY = (longestPoints[0][1] + longestPoints[1][1]) * 0.5
|
|
133 |
tmp._pol.append(QPointF(longestPoints[0][0], adjustY))
|
|
134 |
tmp._pol.append(QPointF(longestPoints[1][0], adjustY))
|
|
127 |
y = (longestPoints[0][1] + longestPoints[1][1]) * 0.5
|
|
128 |
tmp._pol.append(QPointF(longestPoints[0][0], y))
|
|
129 |
tmp._pol.append(QPointF(longestPoints[1][0], y))
|
|
135 | 130 |
elif line.isVertical(): |
136 |
adjustX = (longestPoints[0][0] + longestPoints[1][0]) * 0.5
|
|
137 |
tmp._pol.append(QPointF(adjustX, longestPoints[0][1]))
|
|
138 |
tmp._pol.append(QPointF(adjustX, longestPoints[1][1]))
|
|
131 |
x = (longestPoints[0][0] + longestPoints[1][0]) * 0.5
|
|
132 |
tmp._pol.append(QPointF(x, longestPoints[0][1]))
|
|
133 |
tmp._pol.append(QPointF(x, longestPoints[1][1]))
|
|
139 | 134 |
mergedLines.append(tmp) |
140 | 135 |
|
141 | 136 |
for item in groupItems: connectedLines.remove(item) |
... | ... | |
143 | 138 |
mergedLines.extend(connectedLines) |
144 | 139 |
|
145 | 140 |
head = None |
146 |
for item in mergedLines:
|
|
141 |
for line in mergedLines:
|
|
147 | 142 |
count = 0 |
148 | 143 |
for other in mergedLines: |
149 |
if item is other: continue |
|
150 |
if item.isConnectable(other): count += 1 |
|
144 |
if line is other: continue |
|
145 |
if line.isJointable(other): |
|
146 |
count += 1 |
|
147 |
|
|
151 | 148 |
if 1 == count: |
152 |
head = item
|
|
149 |
head = line
|
|
153 | 150 |
break |
154 |
|
|
151 |
|
|
152 |
docData = AppDocData.instance() |
|
155 | 153 |
if head: |
156 |
docData = AppDocData.instance() |
|
157 |
|
|
158 | 154 |
visited = [head] |
159 | 155 |
pool = [head] |
160 | 156 |
while len(pool) > 0: |
161 |
item = pool.pop()
|
|
162 |
connected = [param for param in mergedLines if param not in visited and param.isConnectable(item)]
|
|
157 |
line = pool.pop()
|
|
158 |
connected = [param for param in mergedLines if param not in visited and param.isJointable(line)]
|
|
163 | 159 |
pool.extend(connected) |
164 | 160 |
visited.extend(connected) |
165 |
node.append(item.toXml()) |
|
166 |
cv2.line(docData.imgOutput, (round(item.startPoint()[0]), round(item.startPoint()[1])), (round(item.endPoint()[0]), round(item.endPoint()[1])), 0, 1) |
|
161 |
if line.length() > 20: |
|
162 |
node.append(line.toXml()) |
|
163 |
cv2.line(docData.imgOutput, (round(line.startPoint()[0]), round(line.startPoint()[1])), (round(line.endPoint()[0]), round(line.endPoint()[1])), 0, 1) |
|
164 |
|
|
165 |
for param in connected: |
|
166 |
mergedLines.remove(param) |
|
167 |
|
|
168 |
for line in mergedLines: |
|
169 |
if line.length() > 20: |
|
170 |
node.append(line.toXml()) |
|
171 |
cv2.line(docData.imgOutput, (round(line.startPoint()[0]), round(line.startPoint()[1])), (round(line.endPoint()[0]), round(line.endPoint()[1])), 0, 1) |
|
172 |
else: |
|
173 |
pass |
|
167 | 174 |
# up to here |
168 | 175 |
|
169 | 176 |
connectedSymbols = [item for item in self.items if issubclass(type(item), SymbolSvgItem)] |
DTI_PID/DTI_PID/XmlGenerator.py | ||
---|---|---|
91 | 91 |
@date 2018.04.23 |
92 | 92 |
''' |
93 | 93 |
def writeOutputXml(pidName, pidWidth, pidHeight): |
94 |
path = os.path.join(AppDocData.instance().getCurrentProject().getOutputPath(), pidName + '.xml') |
|
95 | 94 |
try: |
95 |
path = os.path.join(AppDocData.instance().getCurrentProject().getOutputPath(), pidName + '.xml') |
|
96 |
|
|
96 | 97 |
xmlData = generateOutputXml(pidName, pidWidth, pidHeight) |
97 | 98 |
ElementTree(xmlData).write(path) |
98 | 99 |
except Exception as ex: |
내보내기 Unified diff