프로젝트

일반

사용자정보

통계
| 브랜치(Branch): | 개정판:

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))
클립보드 이미지 추가 (최대 크기: 500 MB)