프로젝트

일반

사용자정보

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

hytos / DTI_PID / DTI_PID / LineDetector.py @ 17cbda7d

이력 | 보기 | 이력해설 | 다운로드 (28.1 KB)

1
import sys
2
import cv2
3
import numpy as np
4
import math
5
import shapely
6

    
7

    
8
class LineDetector():
9
    def __init__(self, image):
10
        try:
11
            thresh = 127
12
            self._image = image
13
            self.width = self._image.shape[::-1][0] if self._image is not None else None
14
            self.height = self._image.shape[::-1][1] if self._image is not None else None
15
            self.Result = np.zeros((self.width, self.height, 3), np.uint8) if self._image is not None else None
16
        except Exception as ex:
17
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
18
                                                       sys.exc_info()[-1].tb_lineno))
19

    
20
    '''
21
        @brief  dot product of given two vectors
22
        @author humkyung
23
        @date   2018.04.14
24
    '''
25

    
26
    def dotProduct(self, lhs, rhs):
27
        return sum([lhs[i] * rhs[i] for i in range(len(lhs))])
28

    
29
    '''
30
        @brief  get length of given vector
31
        @author humkyung
32
        @date   2018.04.14
33
    '''
34

    
35
    def getLength(self, lhs):
36
        return math.sqrt(self.dotProduct(lhs, lhs))
37

    
38
    '''
39
        @brief  get angle between given two vectors
40
        @author humkyung
41
        @date   2018.04.14
42
        @history    2018.05.29  Jeongwoo    Add try-exception
43
        @TODO : Need to modify case - Exception
44
    '''
45

    
46
    def getAngle(self, lhs, rhs):
47
        try:
48
            return math.acos(self.dotProduct(lhs, rhs) / (self.getLength(lhs) * self.getLength(rhs)))
49
        except Exception as ex:
50
            return sys.float_info.max
51

    
52
    """
53
        @brief  get distance between given two points
54
        @author humkyung
55
        @date   2018.04.13
56
    """
57

    
58
    def distanceTo(self, lhs, rhs):
59
        dx = rhs[0] - lhs[0]
60
        dy = rhs[1] - lhs[1]
61
        return math.sqrt(dx * dx + dy * dy)
62

    
63
    '''
64
        @brief  rotate given point about origin by angle
65
        @author humkyung
66
        @date   2018.04.12
67
    '''
68

    
69
    def rotatePoint(self, point, origin, angle):
70
        dx = point[0] - origin[0]
71
        dy = point[1] - origin[1]
72

    
73
        qx = origin[0] + math.cos(angle) * dx - math.sin(angle) * dy
74
        qy = origin[1] + math.sin(angle) * dx + math.cos(angle) * dy
75
        return [qx, qy]
76

    
77
    '''
78
        @brief  check if given two lines are connected
79
        @author humkyung
80
        @date   2018.04.11
81
    '''
82

    
83
    def isConnected(self, lhs, rhs, toler=0):
84
        length = []
85

    
86
        try:
87
            # check if share one point
88
            dx = (lhs[0][0] - rhs[0][0])
89
            dy = (lhs[0][1] - rhs[0][1])
90
            length.append(math.sqrt(dx * dx + dy * dy))
91
            dx = (lhs[1][0] - rhs[0][0])
92
            dy = (lhs[1][1] - rhs[0][1])
93
            length.append(math.sqrt(dx * dx + dy * dy))
94
            dx = (lhs[0][0] - rhs[1][0])
95
            dy = (lhs[0][1] - rhs[1][1])
96
            length.append(math.sqrt(dx * dx + dy * dy))
97
            dx = (lhs[1][0] - rhs[1][0])
98
            dy = (lhs[1][1] - rhs[1][1])
99
            length.append(math.sqrt(dx * dx + dy * dy))
100
            matches = [len for len in length if len < toler]
101
            # up to here
102

    
103
            return (len(matches) == 1)
104
        except Exception as ex:
105
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
106
                                                       sys.exc_info()[-1].tb_lineno))
107

    
108
        return False
109

    
110
    """
111
        @brief  check if given two lines lie on one line and share one point
112
        @author humkyung
113
        @date   2018.04.11
114
    """
115

    
116
    def isCollinear(self, lhs, rhs, toler):
117
        try:
118
            # print(lhs)
119
            # print(rhs)
120
            if self.isConnected(lhs, rhs, toler):
121
                dx1 = lhs[1][0] - lhs[0][0]
122
                dy1 = lhs[1][1] - lhs[0][1]
123
                length = math.sqrt(dx1 * dx1 + dy1 * dy1)
124
                dx1 /= length
125
                dy1 /= length
126
                dx2 = rhs[1][0] - rhs[0][0]
127
                dy2 = rhs[1][1] - rhs[0][1]
128
                length = math.sqrt(dx2 * dx2 + dy2 * dy2)
129
                dx2 /= length
130
                dy2 /= length
131

    
132
                dx = math.fabs(dx1) - math.fabs(dx2)
133
                dy = math.fabs(dy1) - math.fabs(dy2)
134

    
135
                return (math.sqrt(dx * dx + dy * dy) < 0.00001)
136
        except Exception as ex:
137
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
138
                                                       sys.exc_info()[-1].tb_lineno))
139

    
140
        return False
141

    
142
    '''
143
        @brief  connect given lines
144
        @author humkyung
145
        @date   2018.04.11
146
        @history    2018.05.16  Jeongwoo    Connect Lines with long distance points
147
    '''
148

    
149
    def connectLines(self, lines, toler):
150
        res = []
151

    
152
        pts = []
153
        # print(lines)
154
        maxthickness = 0
155
        for line in lines:
156
            pts.append(line[0])
157
            pts.append(line[1])
158
            maxthickness = line[2] if maxthickness < line[2] else maxthickness
159

    
160
        maxLength = None
161
        for lpt in pts:
162
            for rpt in pts:
163
                if lpt != rpt:
164
                    dx = rpt[0] - lpt[0]
165
                    dy = rpt[1] - lpt[1]
166
                    length = math.sqrt(dx * dx + dy * dy)
167
                    if maxLength is None or maxLength < length:
168
                        maxLength = length
169
                        res.clear()
170
                        res.append(lpt)
171
                        res.append(rpt)
172
                        res.append(maxthickness)
173
        # print(res)
174
        return res
175

    
176
    '''
177
        @brief      adjust start point
178
        @author     humkyung
179
        @date       2018.06.21
180
        @history    humkyung 2018.07.31 return -1 thickness tuple if near point is not black
181
    '''
182

    
183
    def adjustStartPoint(self, pt, dir):
184
        from AppDocData import AppDocData
185
        docData = AppDocData.instance()
186
        windowSize = docData.getSlidingWindowSize()
187

    
188
        try:
189
            white = 255
190
            black = 0
191

    
192
            norm = [-dir[1], dir[0]]
193
            if black == self._image[pt[1] + round(dir[1] * windowSize[1]), pt[0] + round(dir[0] * windowSize[1])]:
194
                _pt = [pt[0] + round(dir[0] * windowSize[1]), pt[1] + round(dir[1] * windowSize[1])]
195
            else:
196
                found = False
197
                for step in [1, 2, -1, -2]:
198
                    if black == self._image[pt[1] + round(dir[1] * windowSize[1] + norm[1] * step), pt[0] + round(
199
                            dir[0] * windowSize[1] + norm[0] * step)]:
200
                        _pt = [pt[0] + round(dir[0] * windowSize[1] + norm[0] * step),
201
                               pt[1] + round(dir[1] * windowSize[1] + norm[1] * step)]
202
                        found = True
203
                        break
204

    
205
                if not found: return (pt, -1)
206

    
207
            color = black
208
            lhs = [_pt[0], _pt[1]]
209
            lhscount = 1
210
            while color != white:
211
                lhs[0] = _pt[0] + round(norm[0] * lhscount)
212
                lhs[1] = _pt[1] + round(norm[1] * lhscount)
213
                color = self._image[lhs[1], lhs[0]]
214
                lhscount += 1
215

    
216
            color = black
217
            rhs = [_pt[0], _pt[1]]
218
            rhscount = 1
219
            while color != white:
220
                rhs[0] = _pt[0] - round(norm[0] * rhscount)
221
                rhs[1] = _pt[1] - round(norm[1] * rhscount)
222
                color = self._image[rhs[1], rhs[0]]
223
                rhscount += 1
224

    
225
            size = lhscount + rhscount
226
            offset = round((lhscount - rhscount) * 0.5) if size < windowSize[1] else 0
227
            # print(size)
228
            return (
229
                [pt[0] + norm[0] * offset, pt[1] + norm[1] * offset],
230
                size)  # if size > windowSize[1] else windowSize[1])
231
        except Exception as ex:
232
            from App import App
233
            from AppDocData import MessageType
234

    
235
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
236
                                                           sys.exc_info()[-1].tb_lineno)
237
            App.mainWnd().addMessage.emit(MessageType.Error, message) if not 'is out of bounds' in message else ''
238
            return (pt, -1)
239

    
240
        return (pt, windowSize[1])
241

    
242
    '''
243
        @brief      symbol을 기준으로 양쪽으로 이미지에서 직선을 검출한다.
244
        @author     humkyung
245
        @date       2018.04.??
246
        @history    humkyung 2018.04.11 merge lines which are collinear or connected    
247
                    humkyung 2018.04.12 rotate connection points by symbol angle
248
                    humkyung 2018.04.12 use connection points without rotating(already rotated)
249
                    humkyung 2018.09.01 use connector's direction for detecting line
250
    '''
251

    
252
    def detectConnectedLine(self, symbol, offsetX, offsetY):
253
        res = []
254

    
255
        try:
256
            pool = []
257
            if (0 == symbol.angle) or (1.57 == symbol.angle) or (3.14 == symbol.angle) or (4.71 == symbol.angle):
258
                for connector in symbol.connectors:
259
                    ## get direction of connector
260
                    direction = connector.dir()
261
                    if direction is None:
262
                        dx = connector.sceneConnectPoint[0] - symbol.origin[0]
263
                        dy = connector.sceneConnectPoint[1] - symbol.origin[1]
264
                    else:
265
                        dx, dy = direction[0], direction[1]
266
                    # print('dx : ' + str(dx) + ', dy : ' + str(dy))
267
                    ## up to here
268

    
269
                    length = math.sqrt(dx * dx + dy * dy)
270
                    if length > 0:
271
                        dx /= length
272
                        dy /= length
273

    
274
                        if abs(dx) < 0.1:  # vertical line
275
                            dir = [0, 1 if dy > 0 else -1]
276
                            pt = [round(connector.sceneConnectPoint[0] - offsetX),
277
                                  round(connector.sceneConnectPoint[1] - offsetY)]
278
                            pt, thickness = self.adjustStartPoint(pt, dir)
279
                            if thickness != -1: pool.append([dir, pt, thickness, True if not pool else False])
280
                            # print("v")
281
                        elif abs(dy) < 0.1:  # horizontal line
282
                            dir = [1 if dx > 0 else -1, 0]
283
                            pt = [round(connector.sceneConnectPoint[0] - offsetX),
284
                                  round(connector.sceneConnectPoint[1] - offsetY)]
285
                            pt, thickness = self.adjustStartPoint(pt, dir)
286
                            if thickness != -1: pool.append([dir, pt, thickness, True if not pool else False])
287
                            # print("h")
288
                    # print(thickness)
289
                # print(pool)
290

    
291
            # print(len(pool))
292
            while len(pool) > 0:
293
                dir, pt, thickness, forward = pool.pop()
294
                line = self.detectLine(pt, dir, thickness, forward)
295
                if line is not None:
296
                    line.append(thickness)
297
                    # print(line)
298
                    res.append(line if forward else [line[1], line[0], thickness])
299
                    if ([1, 0] == dir) or ([-1, 0] == dir):  # turn up/down
300
                        connectedPt = [line[1][0], pt[1]]
301
                        pt, thickness = self.adjustStartPoint(connectedPt, [0, 1])
302
                        if thickness != -1: pool.append([[0, 1], pt, thickness, forward])
303
                        pt, thickness = self.adjustStartPoint(connectedPt, [0, -1])
304
                        if thickness != -1: pool.append([[0, -1], pt, thickness, not forward])
305
                    elif ([0, 1] == dir) or ([0, -1] == dir):  # turn left/right
306
                        connectedPt = [pt[0], line[1][1]]
307
                        pt, thickness = self.adjustStartPoint(connectedPt, [1, 0])
308
                        if thickness != -1: pool.append([[1, 0], pt, thickness, forward])
309
                        pt, thickness = self.adjustStartPoint(connectedPt, [-1, 0])
310
                        if thickness != -1: pool.append([[-1, 0], pt, thickness, not forward])
311
            # print(res)
312
            return res
313
        except Exception as ex:
314
            from App import App
315
            from AppDocData import MessageType
316

    
317
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
318
                                                           sys.exc_info()[-1].tb_lineno)
319
            App.mainWnd().addMessage.emit(MessageType.Error, message)
320

    
321
    '''
322
        @breif      merge lines(1.connect collinear lines 2.connect lines)
323
        @author     humkyung
324
        @date       2018.04.11
325
        @history    humkyung 2018.04.12 continue loop if line is not in connectedLines
326
                    Jeongwoo 2018.05.16 Make comments if-statement
327
    '''
328

    
329
    def mergeLines(self, connectedLines, toler):
330
        try:
331
            for line in connectedLines[:]:
332
                if line not in connectedLines: continue
333
                matches = [param for param in connectedLines if
334
                           (line != param) and self.isCollinear(line, param, toler)]
335
                if len(matches) > 0:
336
                    # print(len(matches))
337
                    matches.append(line)
338
                    # if len(matches) > 2: matches = matches[0:2] # pick last two objects
339
                    mergedLine = self.connectLines(matches, toler)
340
                    if mergedLine is not None and len(mergedLine) > 0:
341
                        connectedLines.append(mergedLine)
342
                        for match in matches: connectedLines.remove(match)
343
        except Exception as ex:
344
            from App import App
345
            from AppDocData import MessageType
346

    
347
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
348
                                                           sys.exc_info()[-1].tb_lineno)
349
            App.mainWnd().addMessage.emit(MessageType.Error, message)
350

    
351
    '''
352
        @brief  connect line to nearest connection point of symbol
353
        @author humkyung
354
        @date   2018.04.13
355
    '''
356

    
357
    def connectLineToSymbol(self, line, symbol, toler):
358
        startPt = list(line.startPoint())
359
        distStart = [(self.distanceTo(startPt, (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])),
360
                      (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])) for connector in
361
                     symbol.connectors]
362
        distStart.sort()
363

    
364
        endPt = list(line.endPoint())
365
        distEnd = [(self.distanceTo(endPt, (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])),
366
                    (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])) for connector in
367
                   symbol.connectors]
368
        distEnd.sort()
369

    
370
        if distStart[0][0] < distEnd[0][0]:
371
            dx = distStart[0][1][0] - startPt[0]
372
            dy = distStart[0][1][1] - startPt[1]
373
            dir = (endPt[0] - startPt[0], endPt[1] - startPt[1])
374
            if abs(dir[0]) > abs(dir[1]):
375
                endPt[1] += dy
376
            else:
377
                endPt[0] += dx
378

    
379
            res = line.connect_if_possible(symbol, toler)
380
            if res:
381
                line.set_line([res[1], res[2]])
382
        else:
383
            dx = distEnd[0][1][0] - endPt[0]
384
            dy = distEnd[0][1][1] - endPt[1]
385
            dir = (endPt[0] - startPt[0], endPt[1] - startPt[1])
386
            if abs(dir[0]) > abs(dir[1]):
387
                startPt[1] += dy
388
            else:
389
                startPt[0] += dx
390

    
391
            res = line.connect_if_possible(symbol, toler)
392
            if res:
393
                line.set_line([res[1], res[2]])
394

    
395
    '''
396
        @brief  extend line to intersection point
397
        @author humkyung
398
        @date   2018.04.14
399
    '''
400

    
401
    def connectLineToLine(self, lhs, rhs, toler=20):
402
        try:
403
            res = lhs.connect_if_possible(rhs, toler)
404
            if not res:
405
                return
406

    
407
            lhs_start = list(lhs.startPoint())
408
            lhs_end = list(lhs.endPoint())
409
            rhs_start = list(rhs.startPoint())
410
            rhs_end = list(rhs.endPoint())
411

    
412
            dx = rhs_end[0] - rhs_start[0]
413
            dy = rhs_end[1] - rhs_start[1]
414
            length = math.sqrt(dx * dx + dy * dy)
415
            if length == 0: return
416
            dx /= length
417
            dy /= length
418
            rightLine = [(rhs_start[0] - dx * toler, rhs_start[1] - dy * toler),
419
                         (rhs_end[0] + dx * toler, rhs_end[1] + dy * toler)]
420
            shapelyRightLine = shapely.geometry.LineString(rightLine)
421

    
422
            dx = lhs_end[0] - lhs_start[0]
423
            dy = lhs_end[1] - lhs_start[1]
424
            length = math.sqrt(dx * dx + dy * dy)
425
            if length == 0: return
426
            dx /= length
427
            dy /= length
428
            line = [(lhs_start[0] - dx * toler, lhs_start[1] - dy * toler),
429
                    (lhs_end[0] + dx * toler, lhs_end[1] + dy * toler)]
430
            shapelyLine = shapely.geometry.LineString(line)
431

    
432
            pt = shapelyRightLine.intersection(shapelyLine)
433
            if (pt is not None) and (type(pt) == shapely.geometry.point.Point):
434
                if lhs.is_external_point(pt):
435
                    if self.distanceTo(lhs_start, (pt.x, pt.y)) < self.distanceTo(lhs_end, (pt.x, pt.y)):
436
                        lhs_start[0] = pt.x
437
                        lhs_start[1] = pt.y
438
                    else:
439
                        lhs_end[0] = pt.x
440
                        lhs_end[1] = pt.y
441

    
442
                    lhs.set_line([lhs_start, lhs_end])
443
                else:
444
                    if self.distanceTo(lhs_start, (pt.x, pt.y)) < toler:
445
                        lhs_start[0] = pt.x
446
                        lhs_start[1] = pt.y
447
                    elif self.distanceTo(lhs_end, (pt.x, pt.y)) < toler:
448
                        lhs_end[0] = pt.x
449
                        lhs_end[1] = pt.y
450

    
451
                    lhs.set_line([lhs_start, lhs_end])
452

    
453
                if rhs.is_external_point(pt):
454
                    if self.distanceTo(rhs_start, (pt.x, pt.y)) < self.distanceTo(rhs_end, (pt.x, pt.y)):
455
                        rhs_start[0] = pt.x
456
                        rhs_start[1] = pt.y
457
                    else:
458
                        rhs_end[0] = pt.x
459
                        rhs_end[1] = pt.y
460

    
461
                    rhs.set_line([rhs_start, rhs_end])
462

    
463
                else:
464
                    if self.distanceTo(rhs_start, (pt.x, pt.y)) < toler:
465
                        rhs_start[0] = pt.x
466
                        rhs_start[1] = pt.y
467
                    elif self.distanceTo(rhs_end, (pt.x, pt.y)) < toler:
468
                        rhs_end[0] = pt.x
469
                        rhs_end[1] = pt.y
470

    
471
                    rhs.set_line([rhs_start, rhs_end])
472
        except Exception as ex:
473
            from App import App
474
            from AppDocData import MessageType
475

    
476
            message = 'error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
477
                                                           sys.exc_info()[-1].tb_lineno)
478
            App.mainWnd().addMessage.emit(MessageType.Error, message)
479

    
480
    '''
481
        @brief  detech line thickness
482
        @author humkyung
483
        @date   2018.05.18
484
    '''
485

    
486
    def detectLineThickness(self, pt, dir):
487
        import math
488
        from AppDocData import AppDocData
489
        docData = AppDocData.instance()
490
        windowSize = docData.getSlidingWindowSize()
491

    
492
        white = 255
493
        black = 0
494
        color = white
495

    
496
        norm = [-dir[1], dir[0]]
497
        if black == self._image[pt[1] + round(dir[1] * windowSize[1]), pt[0] + round(dir[0] * windowSize[1])]:
498
            _pt = [pt[0] + round(dir[0] * windowSize[1]), pt[1] + round(dir[1] * windowSize[1])]
499
        else:
500
            return windowSize[1]
501

    
502
        color = black
503
        lhs = [pt[0], pt[1]]
504
        count = 1
505
        while color != white:
506
            lhs[0] = pt[0] + round(norm[0] * count)
507
            lhs[1] = pt[1] + round(norm[1] * count)
508
            color = self._image[lhs[1], lhs[0]]
509
            count += 1
510

    
511
        color = black
512
        rhs = [pt[0], pt[1]]
513
        count = 1
514
        while color != white:
515
            rhs[0] = pt[0] - round(norm[0] * count)
516
            rhs[1] = pt[1] - round(norm[1] * count)
517
            color = self._image[rhs[1], rhs[0]]
518
            count += 1
519

    
520
        dx = rhs[0] - lhs[0]
521
        dy = rhs[1] - lhs[1]
522
        return math.sqrt(dx * dx + dy * dy)
523

    
524
    def detectLine(self, pt, dir, thickness, forward):
525
        """detect a line along given direction"""
526
        from AppDocData import AppDocData
527

    
528
        lineLengthConfigs = AppDocData.instance().getConfigs('Small Line Minimum Length', 'Min Length')
529
        lineMinLength = int(lineLengthConfigs[0].value) if 1 == len(lineLengthConfigs) else 10
530
        try:
531
            white = [255]
532
            windowSize = AppDocData.instance().getSlidingWindowSize()
533
            xHalf = round(windowSize[0] * 0.5)
534
            yHalf = round(windowSize[1] * 0.5)
535

    
536
            if ([1, 0] == dir):
537
                image = self._image[(pt[1] - yHalf):(pt[1] + yHalf), pt[0]:self.width]
538
                imgWidth, imgHeight = image.shape[::-1]
539
                index = 0
540
                for i in range(imgWidth - windowSize[0]):
541
                    window = image[0:windowSize[1], i:i + windowSize[0]]
542
                    if (white == window[0:windowSize[1], 0:windowSize[0]]).all(): break
543
                    index += 1
544
                if index > lineMinLength:
545
                    # self._image[(pt[1]-yHalf):(pt[1]+yHalf), pt[0]:(pt[0]+i)] = white
546
                    cv2.line(self._image, (pt[0], pt[1]), (pt[0] + i, pt[1]), 255, thickness)
547
                    return [[pt[0], pt[1]], [pt[0] + i - round(thickness * 0.5), pt[1]]]
548
            elif ([-1, 0] == dir):
549
                image = self._image[(pt[1] - yHalf):(pt[1] + yHalf), 0:pt[0]]
550
                imgWidth, imgHeight = image.shape[::-1]
551
                index = 0
552
                for i in range(imgWidth - windowSize[0], -1, -1):
553
                    window = image[0:windowSize[1], i:i + windowSize[0]]
554
                    if (white == window[0:windowSize[1], 0:windowSize[0]]).all(): break
555
                    index += 1
556
                if index > lineMinLength:
557
                    # self._image[int(pt[1]-yHalf):int(pt[1]+yHalf), (i+windowSize[0]+yHalf):pt[0]] = white
558
                    cv2.line(self._image, (i + windowSize[0], pt[1]), (pt[0], pt[1]), 255, thickness)
559
                    return [[pt[0], pt[1]], [i + windowSize[0] + round(thickness * 0.5), pt[1]]]
560
            elif ([0, 1] == dir):
561
                windowSize.reverse()
562
                xHalf = round(windowSize[0] * 0.5)
563
                yHalf = round(windowSize[1] * 0.5)
564

    
565
                image = self._image[pt[1]:self.height, int(pt[0] - xHalf):int(pt[0] + xHalf)]
566
                imgWidth, imgHeight = image.shape[::-1]
567
                index = 0
568
                for i in range(imgHeight - windowSize[1]):
569
                    window = image[i:i + windowSize[1], 0:windowSize[0]]
570
                    if (white == window[0:windowSize[1], 0:windowSize[0]]).all(): break
571
                    index += 1
572
                if index > lineMinLength:
573
                    # self._image[(pt[1]):(pt[1]+i), (pt[0]-xHalf):(pt[0]+xHalf)] = white
574
                    cv2.line(self._image, (pt[0], pt[1]), (pt[0], pt[1] + i), 255, thickness)
575
                    return [[pt[0], pt[1]], [pt[0], pt[1] + i - round(thickness * 0.5)]]
576
            elif ([0, -1] == dir):
577
                windowSize.reverse()
578
                xHalf = round(windowSize[0] * 0.5)
579
                yHalf = round(windowSize[1] * 0.5)
580

    
581
                image = self._image[0:pt[1], (pt[0] - xHalf):(pt[0] + xHalf)]
582
                imgWidth, imgHeight = image.shape[::-1]
583
                index = 0
584
                for i in range(imgHeight - windowSize[1], -1, -1):
585
                    window = image[i:i + windowSize[1], 0:windowSize[0]]
586
                    if (white == window[0:windowSize[1], 0:windowSize[0]]).all(): break
587
                    index += 1
588
                if index > lineMinLength:
589
                    # self._image[(i+windowSize[1]):pt[1], (pt[0]-xHalf):(pt[0]+xHalf)] = white
590
                    cv2.line(self._image, (pt[0], i + windowSize[1]), (pt[0], pt[1]), 255, thickness)
591
                    return [[pt[0], pt[1]], [pt[0], i + windowSize[1] + round(thickness * 0.5)]]
592
        except Exception as ex:
593
            from App import App
594
            from AppDocData import MessageType
595

    
596
            message = 'error occurred({}) in {}:{}'.format(repr(ex), sys.exc_info()[-1].tb_frame.f_code.co_filename,
597
                                                           sys.exc_info()[-1].tb_lineno)
598
            App.mainWnd().addMessage.emit(MessageType.Error, message)
599

    
600
        return None
601

    
602
    def detectLineWithoutSymbol(self, area):  # path, offsetX, offsetY):
603
        '''
604
            @brief  detect remain line after detection using symbol info
605
            @author euisung
606
            @date   2019.03.28
607
        '''
608
        import os
609
        from AppDocData import AppDocData
610
        from HoughBundler import HoughBundler
611

    
612
        docData = AppDocData.instance()
613
        img = area.img
614

    
615
        if True:
616
            imgNot = np.ones(self._image.shape, np.uint8) * 255
617
            imgNot = cv2.bitwise_xor(self._image, imgNot)
618
            imgNot = cv2.erode(imgNot, np.ones((2, 2), np.uint8))
619

    
620
            contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
621
            if len(contours) is 0:
622
                return []
623

    
624
            smallContours = []
625
            minimumSize = docData.getConfigs('Filter', 'MinimumSize') * 3
626
            lineLengthConfigs = docData.getConfigs('Small Line Minimum Length', 'Min Length')
627
            lineMinLength = int(lineLengthConfigs[0].value) if 1 == len(lineLengthConfigs) else 30
628
            for contour in contours:
629
                [x, y, w, h] = cv2.boundingRect(contour)
630

    
631
                # remove too small one
632
                if len(minimumSize) is 1:
633
                    if (w * h < int(minimumSize[0].value) * int(minimumSize[0].value)):
634
                        smallContours.append(contour)
635
            imgNotRemoveSmall = cv2.drawContours(imgNot, smallContours, -1, 0, -1)
636

    
637
            # detect line
638
            edged = cv2.Canny(imgNotRemoveSmall, 100, 200)
639

    
640
            rate = 25
641
            lines = cv2.HoughLinesP(image=edged, rho=1, theta=np.pi / 180, threshold=rate,
642
                                    minLineLength=lineMinLength * 3, maxLineGap=25)
643
            if lines is None:
644
                return []
645

    
646
            houghBundler = HoughBundler().process_lines(lines, None)
647

    
648
            remainLines = []
649
            for line in houghBundler:
650
                remainLines.append([[line[0][0], line[0][1]], [line[1][0], line[1][1]]])
651

    
652
            # border line check
653
            borderRate = 0.07
654
            borderY, borderX = round(imgNot.shape[0] * borderRate), round(imgNot.shape[1] * borderRate)
655

    
656
            for index in range(len(remainLines) - 1, -1, -1):
657
                if remainLines[index][0][0] < borderX and remainLines[index][1][0] < borderX:
658
                    remainLines.pop(index)
659
                elif remainLines[index][0][0] > self._image.shape[1] - borderX and remainLines[index][1][0] > \
660
                        self._image.shape[1] - borderX:
661
                    remainLines.pop(index)
662
                elif remainLines[index][0][1] < borderY and remainLines[index][1][1] < borderY:
663
                    remainLines.pop(index)
664
                elif remainLines[index][0][1] > self._image.shape[0] - borderY and remainLines[index][1][1] > \
665
                        self._image.shape[0] - borderY:
666
                    remainLines.pop(index)
667

    
668
            return remainLines
669

    
670
    '''
671
        @brief  save the result of image
672
        @author humkyung
673
        @date   2018.04.14
674
    '''
675

    
676
    def saveImage(self):
677
        import datetime
678
        from AppDocData import AppDocData
679

    
680
        try:
681
            nowDate = datetime.datetime.now()
682
            path = AppDocData.instance().getCurrentProject().getTempPath() + '/{}_{}_line_detector.png'.format(
683
                nowDate.strftime('%Y-%m-%d'), nowDate.strftime('%H %M %S'))
684
            cv2.imwrite(path, self._image)
685
        except Exception as ex:
686
            print('error occurred({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename,
687
                                                       sys.exc_info()[-1].tb_lineno))
클립보드 이미지 추가 (최대 크기: 500 MB)