프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / LineDetector.py @ 6c393d19

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

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

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

    
17
    '''
18
        @brief  dot product of given two vectors
19
        @author humkyung
20
        @date   2018.04.14
21
    '''
22
    def dotProduct(self, lhs, rhs):
23
        return sum([lhs[i]*rhs[i] for i in range(len(lhs))])
24

    
25
    '''
26
        @brief  get length of given vector
27
        @author humkyung
28
        @date   2018.04.14
29
    '''
30
    def getLength(self, lhs):
31
        return math.sqrt(self.dotProduct(lhs, lhs))
32

    
33
    '''
34
        @brief  get angle between given two vectors
35
        @author humkyung
36
        @date   2018.04.14
37
        @history    2018.05.29  Jeongwoo    Add try-exception
38
        @TODO : Need to modify case - Exception
39
    '''
40
    def getAngle(self, lhs, rhs):
41
        try:
42
            return math.acos(self.dotProduct(lhs, rhs) / (self.getLength(lhs) * self.getLength(rhs)))
43
        except Exception as ex:
44
            return sys.float_info.max
45
        
46
    """
47
        @brief  get distance between given two points
48
        @author humkyung
49
        @date   2018.04.13
50
    """
51
    def distanceTo(self, lhs, rhs):
52
        dx = rhs[0] - lhs[0]
53
        dy = rhs[1] - lhs[1]
54
        return math.sqrt(dx*dx + dy*dy)
55

    
56
    '''
57
        @brief  rotate given point about origin by angle
58
        @author humkyung
59
        @date   2018.04.12
60
    '''
61
    def rotatePoint(self, point, origin, angle):
62
        dx = point[0] - origin[0]
63
        dy = point[1] - origin[1]
64

    
65
        qx = origin[0] + math.cos(angle) * dx - math.sin(angle) * dy
66
        qy = origin[1] + math.sin(angle) * dx + math.cos(angle) * dy
67
        return [qx, qy]
68

    
69
    '''
70
        @brief  check if given two lines are connected
71
        @author humkyung
72
        @date   2018.04.11
73
    '''
74
    def isConnected(self, lhs, rhs, toler=0):
75
        length = []
76

    
77
        try:
78
            # check if share one point
79
            dx = (lhs[0][0] - rhs[0][0])
80
            dy = (lhs[0][1] - rhs[0][1])
81
            length.append(math.sqrt(dx*dx + dy*dy))
82
            dx = (lhs[1][0] - rhs[0][0])
83
            dy = (lhs[1][1] - rhs[0][1])
84
            length.append(math.sqrt(dx*dx + dy*dy))
85
            dx = (lhs[0][0] - rhs[1][0])
86
            dy = (lhs[0][1] - rhs[1][1])
87
            length.append(math.sqrt(dx*dx + dy*dy))
88
            dx = (lhs[1][0] - rhs[1][0])
89
            dy = (lhs[1][1] - rhs[1][1])
90
            length.append(math.sqrt(dx*dx + dy*dy))
91
            matches = [len for len in length if len < toler]
92
            # up to here
93

    
94
            return (len(matches) == 1)
95
        except Exception as ex:
96
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
97
        
98
        return False
99

    
100
    """
101
        @brief  check if given two lines lie on one line and share one point
102
        @author humkyung
103
        @date   2018.04.11
104
    """
105
    def isCollinear(self, lhs, rhs, toler):
106
        try:
107
            #print(lhs)
108
            #print(rhs)
109
            if self.isConnected(lhs, rhs, toler):
110
                dx1 = lhs[1][0] - lhs[0][0]
111
                dy1 = lhs[1][1] - lhs[0][1]
112
                length = math.sqrt(dx1*dx1 + dy1*dy1)
113
                dx1 /= length
114
                dy1 /= length
115
                dx2 = rhs[1][0] - rhs[0][0]
116
                dy2 = rhs[1][1] - rhs[0][1]
117
                length = math.sqrt(dx2*dx2 + dy2*dy2)
118
                dx2 /= length
119
                dy2 /= length
120

    
121
                dx = math.fabs(dx1) - math.fabs(dx2)
122
                dy = math.fabs(dy1) - math.fabs(dy2)
123

    
124
                return (math.sqrt(dx*dx + dy*dy) < 0.00001)
125
        except Exception as ex:
126
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
127

    
128
        return False
129

    
130
    '''
131
        @brief  connect given lines
132
        @author humkyung
133
        @date   2018.04.11
134
        @history    2018.05.16  Jeongwoo    Connect Lines with long distance points
135
    '''
136
    def connectLines(self, lines, toler):
137
        res = []
138

    
139
        pts = []
140
        #print(lines)
141
        maxthickness = 0
142
        for line in lines:
143
            pts.append(line[0])
144
            pts.append(line[1])
145
            maxthickness = line[2] if maxthickness < line[2] else maxthickness
146

    
147
        maxLength = None
148
        for lpt in pts:
149
            for rpt in pts:
150
                if lpt != rpt:
151
                    dx = rpt[0] - lpt[0]
152
                    dy = rpt[1] - lpt[1]
153
                    length = math.sqrt(dx * dx + dy * dy)
154
                    if maxLength is None or maxLength < length:
155
                        maxLength = length
156
                        res.clear()
157
                        res.append(lpt)
158
                        res.append(rpt)
159
                        res.append(maxthickness)
160
        #print(res)
161
        return res
162

    
163
    '''
164
        @brief      adjust start point
165
        @author     humkyung
166
        @date       2018.06.21
167
        @history    humkyung 2018.07.31 return -1 thickness tuple if near point is not black
168
    '''
169
    def adjustStartPoint(self, pt, dir):
170
        from AppDocData import AppDocData
171
        docData = AppDocData.instance()
172
        windowSize = docData.getSlidingWindowSize()
173

    
174
        try:
175
            white = 255
176
            black = 0
177

    
178
            norm = [-dir[1], dir[0]]
179
            if black == self._image[pt[1] + round(dir[1]*windowSize[1]), pt[0] + round(dir[0]*windowSize[1])]:
180
                _pt = [pt[0] + round(dir[0]*windowSize[1]), pt[1] + round(dir[1]*windowSize[1])]
181
            else:
182
                found = False
183
                for step in [1,2,-1,-2]:
184
                    if black == self._image[pt[1] + round(dir[1]*windowSize[1] + norm[1]*step), pt[0] + round(dir[0]*windowSize[1] + norm[0]*step)]:
185
                        _pt = [pt[0] + round(dir[0]*windowSize[1] + norm[0]*step), pt[1] + round(dir[1]*windowSize[1] + norm[1]*step)]
186
                        found = True
187
                        break
188

    
189
                if not found: return (pt, -1)
190

    
191
            color = black 
192
            lhs = [_pt[0], _pt[1]]
193
            lhscount = 1
194
            while color != white:
195
                lhs[0] = _pt[0] + round(norm[0]*lhscount)
196
                lhs[1] = _pt[1] + round(norm[1]*lhscount)
197
                color = self._image[lhs[1], lhs[0]]
198
                lhscount += 1
199

    
200
            color = black 
201
            rhs = [_pt[0], _pt[1]]
202
            rhscount = 1
203
            while color != white:
204
                rhs[0] = _pt[0] - round(norm[0]*rhscount)
205
                rhs[1] = _pt[1] - round(norm[1]*rhscount)
206
                color = self._image[rhs[1], rhs[0]]
207
                rhscount += 1
208

    
209
            size = lhscount + rhscount
210
            offset = round((lhscount - rhscount)*0.5) if size < windowSize[1] else 0
211
            #print(size)
212
            return ([pt[0] + norm[0]*offset, pt[1] + norm[1]*offset], size)# if size > windowSize[1] else windowSize[1])
213
        except Exception as ex:
214
            from App import App 
215
            from AppDocData import MessageType
216

    
217
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
218
            App.mainWnd().addMessage.emit(MessageType.Error, message) if not 'is out of bounds for axis 1' in message else ''
219
            return (pt, -1)
220

    
221
        return (pt, windowSize[1])
222

    
223
    '''
224
        @brief      symbol을 기준으로 양쪽으로 이미지에서 직선을 검출한다.
225
        @author     humkyung
226
        @date       2018.04.??
227
        @history    humkyung 2018.04.11 merge lines which are collinear or connected    
228
                    humkyung 2018.04.12 rotate connection points by symbol angle
229
                    humkyung 2018.04.12 use connection points without rotating(already rotated)
230
                    humkyung 2018.09.01 use connector's direction for detecting line
231
    '''
232
    def detectConnectedLine(self, symbol, offsetX, offsetY):
233
        res = []
234

    
235
        try:
236
            pool = []
237
            if (0 == symbol.angle) or (1.57 == symbol.angle) or (3.14 == symbol.angle) or (4.71 == symbol.angle):
238
                for connector in symbol.connectors:
239
                    ## get direction of connector
240
                    direction = connector.dir()
241
                    if direction is None:
242
                        dx = connector.sceneConnectPoint[0] - symbol.origin[0]
243
                        dy = connector.sceneConnectPoint[1] - symbol.origin[1]
244
                    else:
245
                        dx, dy = direction[0], direction[1]
246
                    #print('dx : ' + str(dx) + ', dy : ' + str(dy))
247
                    ## up to here
248

    
249
                    length = math.sqrt(dx*dx + dy*dy)
250
                    if length > 0:
251
                        dx /= length
252
                        dy /= length
253

    
254
                        if abs(dx) < 0.1:   # vertical line
255
                            dir = [0,1 if dy > 0 else -1]
256
                            pt = [round(connector.sceneConnectPoint[0] - offsetX), round(connector.sceneConnectPoint[1] - offsetY)]
257
                            pt, thickness = self.adjustStartPoint(pt, dir)
258
                            if thickness != -1: pool.append([dir, pt, thickness, True if not pool else False])
259
                            #print("v")
260
                        elif abs(dy) < 0.1: # horizontal line
261
                            dir = [1 if dx > 0 else -1,0]
262
                            pt = [round(connector.sceneConnectPoint[0] - offsetX), round(connector.sceneConnectPoint[1] - offsetY)]
263
                            pt, thickness = self.adjustStartPoint(pt, dir)
264
                            if thickness != -1: pool.append([dir, pt, thickness, True if not pool else False])
265
                            #print("h")
266
                    #print(thickness)
267
                #print(pool)
268

    
269
            #print(len(pool))
270
            while len(pool) > 0:
271
                dir, pt, thickness, forward = pool.pop()
272
                line = self.detectLine(pt, dir, thickness, forward)
273
                if line is not None:
274
                    line.append(thickness)
275
                    #print(line)
276
                    res.append(line if forward else [line[1], line[0], thickness])
277
                    if ([1,0] == dir) or ([-1,0] == dir):   # turn up/down
278
                        connectedPt = [line[1][0], pt[1]]
279
                        pt, thickness = self.adjustStartPoint(connectedPt, [0,1])
280
                        if thickness != -1: pool.append([[0,1], pt, thickness, forward])
281
                        pt, thickness = self.adjustStartPoint(connectedPt, [0,-1])
282
                        if thickness != -1: pool.append([[0,-1], pt, thickness, not forward])
283
                    elif ([0,1] == dir) or ([0,-1] == dir): # turn left/right
284
                        connectedPt = [pt[0], line[1][1]]
285
                        pt, thickness = self.adjustStartPoint(connectedPt, [1,0])
286
                        if thickness != -1: pool.append([[1,0], pt, thickness, forward])
287
                        pt, thickness = self.adjustStartPoint(connectedPt, [-1,0])
288
                        if thickness != -1: pool.append([[-1,0], pt, thickness, not forward])
289
            #print(res)
290
            return res
291
        except Exception as ex:
292
            from App import App 
293
            from AppDocData import MessageType
294

    
295
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
296
            App.mainWnd().addMessage.emit(MessageType.Error, message)
297

    
298
    '''
299
        @breif      merge lines(1.connect collinear lines 2.connect lines)
300
        @author     humkyung
301
        @date       2018.04.11
302
        @history    humkyung 2018.04.12 continue loop if line is not in connectedLines
303
                    Jeongwoo 2018.05.16 Make comments if-statement
304
    '''
305
    def mergeLines(self, connectedLines, toler):
306
        try:
307
            for line in connectedLines[:]:
308
                if line not in connectedLines: continue
309
                matches = [param for param in connectedLines if (line != param) and self.isCollinear(line, param, toler)]
310
                if len(matches) > 0:
311
                    #print(len(matches))
312
                    matches.append(line)
313
                    #if len(matches) > 2: matches = matches[0:2] # pick last two objects
314
                    mergedLine = self.connectLines(matches, toler)
315
                    if mergedLine is not None and len(mergedLine) > 0:
316
                        connectedLines.append(mergedLine)
317
                        for match in matches: connectedLines.remove(match)
318
        except Exception as ex:
319
            from App import App 
320
            from AppDocData import MessageType
321

    
322
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
323
            App.mainWnd().addMessage.emit(MessageType.Error, message)
324

    
325
    '''
326
        @brief  connect line to nearest connection point of symbol
327
        @author humkyung
328
        @date   2018.04.13
329
    '''
330
    def connectLineToSymbol(self, line, symbol, toler):
331
        startPt = list(line.startPoint())
332
        distStart = [(self.distanceTo(startPt, (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])), (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])) for connector in symbol.connectors]
333
        distStart.sort()
334

    
335
        endPt = list(line.endPoint())
336
        distEnd = [(self.distanceTo(endPt, (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])), (connector.sceneConnectPoint[0], connector.sceneConnectPoint[1])) for connector in symbol.connectors]
337
        distEnd.sort()
338

    
339
        if distStart[0][0] < distEnd[0][0]:
340
            dx = distStart[0][1][0] - startPt[0]
341
            dy = distStart[0][1][1] - startPt[1]
342
            dir = (endPt[0] - startPt[0], endPt[1] - startPt[1])
343
            if abs(dir[0]) > abs(dir[1]):
344
                endPt[1] += dy
345
            else:
346
                endPt[0] += dx
347

    
348
            line.set_line([[distStart[0][1][0],distStart[0][1][1]], endPt])
349
            line.connect_if_possible(symbol, toler)
350
        else:
351
            dx = distEnd[0][1][0] - endPt[0]
352
            dy = distEnd[0][1][1] - endPt[1]
353
            dir = (endPt[0] - startPt[0], endPt[1] - startPt[1])
354
            if abs(dir[0]) > abs(dir[1]):
355
                startPt[1] += dy
356
            else:
357
                startPt[0] += dx
358

    
359
            line.set_line([startPt, [distEnd[0][1][0],distEnd[0][1][1]]])
360
            line.connect_if_possible(symbol, toler)
361

    
362
    '''
363
        @brief  extend line to intersection point
364
        @author humkyung
365
        @date   2018.04.14
366
    '''
367
    def connectLineToLine(self, lhs, rhs, toler=20):
368
        try:
369
            lhs_start = list(lhs.startPoint())
370
            lhs_end = list(lhs.endPoint())
371
            rhs_start = list(rhs.startPoint())
372
            rhs_end = list(rhs.endPoint())
373

    
374
            dx = rhs_end[0] - rhs_start[0]
375
            dy = rhs_end[1] - rhs_start[1]
376
            length = math.sqrt(dx*dx + dy*dy)
377
            if length == 0: return
378
            dx /= length
379
            dy /= length
380
            rightLine = [(rhs_start[0]-dx*toler, rhs_start[1]-dy*toler), (rhs_end[0]+dx*toler, rhs_end[1]+dy*toler)]
381
            shapelyRightLine = shapely.geometry.LineString(rightLine)
382

    
383
            dx = lhs_end[0] - lhs_start[0]
384
            dy = lhs_end[1] - lhs_start[1]
385
            length = math.sqrt(dx*dx + dy*dy)
386
            if length == 0: return
387
            dx /= length
388
            dy /= length
389
            line = [(lhs_start[0]-dx*toler, lhs_start[1]-dy*toler), (lhs_end[0]+dx*toler, lhs_end[1]+dy*toler)]
390
            shapelyLine = shapely.geometry.LineString(line)
391

    
392
            pt = shapelyRightLine.intersection(shapelyLine)
393
            if (pt is not None) and (type(pt) == shapely.geometry.point.Point):
394
                if lhs.is_external_point(pt):
395
                    if self.distanceTo(lhs_start, (pt.x, pt.y)) < self.distanceTo(lhs_end, (pt.x, pt.y)):
396
                        lhs_start[0] = pt.x
397
                        lhs_start[1] = pt.y
398
                    else:
399
                        lhs_end[0] = pt.x
400
                        lhs_end[1] = pt.y
401

    
402
                    lhs.set_line([lhs_start, lhs_end])
403
                else:
404
                    if self.distanceTo(lhs_start, (pt.x, pt.y)) < toler:
405
                        lhs_start[0] = pt.x
406
                        lhs_start[1] = pt.y
407
                    elif self.distanceTo(lhs_end, (pt.x, pt.y)) < toler:
408
                        lhs_end[0] = pt.x
409
                        lhs_end[1] = pt.y
410

    
411
                    lhs.set_line([lhs_start, lhs_end])
412

    
413
                if rhs.is_external_point(pt):
414
                    if self.distanceTo(rhs_start, (pt.x, pt.y)) < self.distanceTo(rhs_end, (pt.x, pt.y)):
415
                        rhs_start[0] = pt.x
416
                        rhs_start[1] = pt.y
417
                    else:
418
                        rhs_end[0] = pt.x
419
                        rhs_end[1] = pt.y
420

    
421
                    rhs.set_line([rhs_start, rhs_end])
422

    
423
                else:
424
                    if self.distanceTo(rhs_start, (pt.x, pt.y)) < toler:
425
                        rhs_start[0] = pt.x
426
                        rhs_start[1] = pt.y
427
                    elif self.distanceTo(rhs_end, (pt.x, pt.y)) < toler:
428
                        rhs_end[0] = pt.x
429
                        rhs_end[1] = pt.y
430

    
431
                    rhs.set_line([rhs_start, rhs_end])
432

    
433
                lhs.connect_if_possible(rhs, toler=1)
434
        except Exception as ex:
435
            from App import App 
436
            from AppDocData import MessageType
437

    
438
            message = 'error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno)
439
            App.mainWnd().addMessage.emit(MessageType.Error, message)
440

    
441
    '''
442
        @brief  detech line thickness
443
        @author humkyung
444
        @date   2018.05.18
445
    '''
446
    def detectLineThickness(self, pt, dir):
447
        import math
448
        from AppDocData import AppDocData
449
        docData = AppDocData.instance()
450
        windowSize = docData.getSlidingWindowSize()
451

    
452
        white = 255
453
        black = 0
454
        color = white
455

    
456
        norm = [-dir[1], dir[0]]
457
        if black == self._image[pt[1] + round(dir[1]*windowSize[1]), pt[0] + round(dir[0]*windowSize[1])]:
458
            _pt = [pt[0] + round(dir[0]*windowSize[1]), pt[1] + round(dir[1]*windowSize[1])]
459
        else:
460
            return windowSize[1]
461

    
462
        color = black 
463
        lhs = [pt[0], pt[1]]
464
        count = 1
465
        while color != white:
466
            lhs[0] = pt[0] + round(norm[0]*count)
467
            lhs[1] = pt[1] + round(norm[1]*count)
468
            color = self._image[lhs[1], lhs[0]]
469
            count += 1
470

    
471
        color = black 
472
        rhs = [pt[0], pt[1]]
473
        count = 1
474
        while color != white:
475
            rhs[0] = pt[0] - round(norm[0]*count)
476
            rhs[1] = pt[1] - round(norm[1]*count)
477
            color = self._image[rhs[1], rhs[0]]
478
            count += 1
479

    
480
        dx = rhs[0] - lhs[0]
481
        dy = rhs[1] - lhs[1]
482
        return math.sqrt(dx*dx + dy*dy)
483

    
484
    '''
485
        @brief      detect a line along given direction
486
        @author     humkyung
487
        @date       2018.04
488
        @history    humkyung 2018.05.18 add parameter for thickness
489
                    Jeongwoo 2018.05.18 Read LineLengthConfig and Comapare on if-statement
490
    '''
491
    def detectLine(self, pt, dir, thickness, forward):
492
        from AppDocData import AppDocData
493

    
494
        lineLengthConfigs = AppDocData.instance().getConfigs('Small Line Minimum Length', 'Min Length')
495
        lineMinLength = int(lineLengthConfigs[0].value) if 1 == len(lineLengthConfigs) else 10
496
        try:
497
            white = [255]
498
            windowSize = AppDocData.instance().getSlidingWindowSize()
499
            xHalf = round(windowSize[0]*0.5)
500
            yHalf = round(windowSize[1]*0.5)
501

    
502
            if ([1,0] == dir):
503
                image = self._image[(pt[1]-yHalf):(pt[1]+yHalf), pt[0]:self.width]
504
                imgWidth, imgHeight = image.shape[::-1]
505
                index = 0
506
                for i in range(imgWidth-windowSize[0]):
507
                    window = image[0:windowSize[1], i:i+windowSize[0]]
508
                    if (white == window[0:windowSize[1],0:windowSize[0]]).all(): break
509
                    index += 1
510
                if index > lineMinLength:
511
                    #self._image[(pt[1]-yHalf):(pt[1]+yHalf), pt[0]:(pt[0]+i)] = white
512
                    cv2.line(self._image, (pt[0],pt[1]), (pt[0]+i,pt[1]), 255, thickness)
513
                    return [[pt[0], pt[1]], [pt[0] + i - round(thickness*0.5), pt[1]]]
514
            elif ([-1,0] == dir):
515
                image = self._image[(pt[1]-yHalf):(pt[1]+yHalf), 0:pt[0]]
516
                imgWidth, imgHeight = image.shape[::-1]
517
                index = 0
518
                for i in range(imgWidth-windowSize[0], -1, -1):
519
                    window = image[0:windowSize[1], i:i+windowSize[0]]
520
                    if (white == window[0:windowSize[1],0:windowSize[0]]).all(): break
521
                    index += 1
522
                if index > lineMinLength: 
523
                    #self._image[int(pt[1]-yHalf):int(pt[1]+yHalf), (i+windowSize[0]+yHalf):pt[0]] = white
524
                    cv2.line(self._image, (i+windowSize[0],pt[1]), (pt[0],pt[1]), 255, thickness)
525
                    return [[pt[0], pt[1]], [i+windowSize[0]+round(thickness*0.5), pt[1]]]
526
            elif ([0,1] == dir):
527
                windowSize.reverse()
528
                xHalf = round(windowSize[0]*0.5)
529
                yHalf = round(windowSize[1]*0.5)
530

    
531
                image = self._image[pt[1]:self.height, int(pt[0]-xHalf):int(pt[0]+xHalf)]
532
                imgWidth, imgHeight = image.shape[::-1]
533
                index = 0
534
                for i in range(imgHeight-windowSize[1]):
535
                    window = image[i:i+windowSize[1], 0:windowSize[0]]
536
                    if (white == window[0:windowSize[1],0:windowSize[0]]).all(): break
537
                    index += 1
538
                if index > lineMinLength:
539
                    #self._image[(pt[1]):(pt[1]+i), (pt[0]-xHalf):(pt[0]+xHalf)] = white
540
                    cv2.line(self._image, (pt[0],pt[1]), (pt[0],pt[1]+i), 255, thickness)
541
                    return [[pt[0], pt[1]], [pt[0], pt[1] + i - round(thickness*0.5)]]
542
            elif ([0,-1] == dir):
543
                windowSize.reverse()
544
                xHalf = round(windowSize[0]*0.5)
545
                yHalf = round(windowSize[1]*0.5)
546

    
547
                image = self._image[0:pt[1], (pt[0]-xHalf):(pt[0]+xHalf)]
548
                imgWidth, imgHeight = image.shape[::-1]
549
                index = 0
550
                for i in range(imgHeight-windowSize[1], -1, -1):
551
                    window = image[i:i+windowSize[1], 0:windowSize[0]]
552
                    if (white == window[0:windowSize[1],0:windowSize[0]]).all(): break
553
                    index += 1
554
                if index > lineMinLength:
555
                    #self._image[(i+windowSize[1]):pt[1], (pt[0]-xHalf):(pt[0]+xHalf)] = white
556
                    cv2.line(self._image, (pt[0],i+windowSize[1]), (pt[0],pt[1]), 255, thickness)
557
                    return [[pt[0], pt[1]], [pt[0], i+windowSize[1]+round(thickness*0.5)]]
558
        except Exception as ex:
559
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
560

    
561
        return None
562

    
563
    def detectLineWithoutSymbol(self, area):# path, offsetX, offsetY):
564
        '''
565
            @brief  detect remain line after detection using symbol info
566
            @author euisung
567
            @date   2019.03.28
568
        '''
569
        import os
570
        from AppDocData import AppDocData
571
        from HoughBundler import HoughBundler
572

    
573
        docData = AppDocData.instance()
574
        img = area.img
575

    
576
        if True:
577
            imgNot = np.ones(self._image.shape, np.uint8)*255
578
            imgNot = cv2.bitwise_xor(self._image, imgNot)
579
            imgNot = cv2.erode(imgNot, np.ones((2,2), np.uint8))
580

    
581
            image, contours, hierarchy = cv2.findContours(imgNot, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
582
            if len(contours) is 0:
583
                return []
584
                
585
            smallContours = []
586
            minimumSize = docData.getConfigs('Filter', 'MinimumSize') * 3
587
            lineLengthConfigs = docData.getConfigs('Small Line Minimum Length', 'Min Length')
588
            lineMinLength = int(lineLengthConfigs[0].value) if 1 == len(lineLengthConfigs) else 30
589
            for contour in contours:
590
                [x, y, w, h] = cv2.boundingRect(contour)
591

    
592
                # remove too small one
593
                if len(minimumSize) is 1:
594
                    if (w * h < int(minimumSize[0].value) * int(minimumSize[0].value)):
595
                        smallContours.append(contour)
596
            imgNotRemoveSmall = cv2.drawContours(imgNot, smallContours, -1, 0, -1)
597

    
598
            # detect line
599
            edged = cv2.Canny(imgNotRemoveSmall, 100, 200)
600

    
601
            rate = 25
602
            lines = cv2.HoughLinesP(image=edged, rho=1, theta=np.pi/180, threshold=rate, minLineLength=lineMinLength*3, maxLineGap=25)
603
            if lines is None:
604
                return []
605

    
606
            houghBundler = HoughBundler().process_lines(lines, None)
607

    
608
            remainLines = []
609
            for line in houghBundler:
610
                remainLines.append([[line[0][0], line[0][1]], [line[1][0], line[1][1]]])
611

    
612
            # border line check
613
            borderRate = 0.07
614
            borderY, borderX = round(imgNot.shape[0] * borderRate), round(imgNot.shape[1] * borderRate)
615

    
616
            for index in range(len(remainLines) - 1, -1, -1):
617
                if remainLines[index][0][0] < borderX and remainLines[index][1][0] < borderX:
618
                    remainLines.pop(index)
619
                elif remainLines[index][0][0] > self._image.shape[1] - borderX and remainLines[index][1][0] > self._image.shape[1] - borderX:
620
                    remainLines.pop(index)
621
                elif remainLines[index][0][1] < borderY and remainLines[index][1][1] < borderY:
622
                    remainLines.pop(index)
623
                elif remainLines[index][0][1] > self._image.shape[0] - borderY and remainLines[index][1][1] > self._image.shape[0] - borderY:
624
                    remainLines.pop(index)
625

    
626
            return remainLines
627

    
628
    '''
629
        @brief  save the result of image
630
        @author humkyung
631
        @date   2018.04.14
632
    '''
633
    def saveImage(self):
634
        import datetime
635
        from AppDocData import AppDocData
636

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