프로젝트

일반

사용자정보

통계
| 개정판:

hytos / DTI_PID / DTI_PID / LineDetector.py @ 528bc774

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

1
import sys
2
import cv2
3
import numpy as np
4
import matplotlib.pyplot as plt
5
import math
6
import imutils
7
from shapely.geometry import LineString
8
from shapely.ops import linemerge
9

    
10
class LineVector:
11
    def __init__(self, x1, y1, x2, y2):
12
        self.x1 = x1
13
        self.y1 = y1
14
        self.x2 = x2
15
        self.y2 = y2
16

    
17
        dx = x2 - x1
18
        dy = y2 - y1
19
        self.Length = math.sqrt(dx*dx + dy*dy)
20
        self.dx = dx/self.Length
21
        self.dy = dy/self.Length
22

    
23
        self.DrawFlag = True
24

    
25
    """
26
    두 직선이 평행한지 판별한다.
27
    """
28
    def IsParallel(self, rhs):
29
        dx = math.fabs(self.dx) - math.fabs(rhs.dx)
30
        dy = math.fabs(self.dy) - math.fabs(rhs.dy)
31
        Length = math.sqrt(dx*dx + dy*dy)
32

    
33
        cross = []
34
        dx1 = rhs.x1 - self.x1
35
        dy1 = rhs.y1 - self.y1
36
        dx2 = rhs.x1 - self.x2
37
        dy2 = rhs.y1 - self.y2
38
        cross.append(dx1*dy1 - dy1*dx2)
39
        dx1 = rhs.x2 - self.x1
40
        dy1 = rhs.y2 - self.y1
41
        dx2 = rhs.x2 - self.x2
42
        dy2 = rhs.y2 - self.y2
43
        cross.append(dx1*dy1 - dy1*dx2)
44

    
45
        if cross[0] > 0 and cross[1] > 0: return False
46

    
47
        return Length < 0.0001
48

    
49
    """
50
    두 직선의 거리를 구한다.
51
    """
52
    def DistanceTo(self, rhs):
53
        dx = rhs.x1 - self.x1
54
        dy = rhs.y1 - self.y1
55
        dot = self.dx*dx + self.dy*dy
56
        tmp = dot
57
        x1 = self.x1 + tmp*self.dx
58
        y1 = self.y1 + tmp*self.dy
59
        dx = x1 - rhs.x1
60
        dy = y1 - rhs.y1
61
        return math.sqrt(dx*dx + dy*dy)
62

    
63
    '''
64
    두 직선의 사이를 가로지르는 직선을 구한다.
65
    '''
66
    def Merge(self, rhs):
67
        dx1 = rhs.x1 - self.x1
68
        dy1 = rhs.y1 - self.y1
69
        dx2 = rhs.x1 - self.x2
70
        dy2 = rhs.y1 - self.y2
71

    
72
        candidates = []
73
        candidates.append((self.x1, self.y1))
74
        candidates.append((self.x2, self.y2))
75

    
76
        Dir = []
77
        cross = dx1*dy2 - dy1*dx2
78
        if cross > 0:
79
            dot = dx1*self.dx + dy1*self.dy
80
            tmp = (self.x1 + dot*self.dx, self.y1 + dot*self.dy)
81
            candidates.append(tmp)
82
            Dir.append((rhs.x1 - tmp[0], rhs.y1 - tmp[1]))
83
            
84
        dx1 = rhs.x2 - self.x1
85
        dy1 = rhs.y2 - self.y1
86
        dx2 = rhs.x2 - self.x2
87
        dy2 = rhs.y2 - self.y2
88
        cross = dx1*dy2 - dy1*dx2
89
        if cross > 0:
90
            dot = dx1*self.dx + dy1*self.dy
91
            tmp = (self.x1 + dot*self.dx, self.y1 + dot*self.dy)
92
            candidates.append(tmp)
93
            Dir.append((rhs.x2 - tmp[0], rhs.y2 - tmp[1]))
94

    
95
        MaxLength = None
96
        Points = []
97
        for i in range(len(candidates) - 1):
98
            for j in range(i+1, len(candidates)):
99
                dx = candidates[i][0] - candidates[j][0]
100
                dy = candidates[i][1] - candidates[j][1]
101
                Length = dx*dx + dy*dy
102
                if MaxLength == None:
103
                    MaxLength = Length
104
                    Points.append(candidates[i])
105
                    Points.append(candidates[j])
106
                elif MaxLength < Length:
107
                    MaxLength = Length
108
                    Points[0] = candidates[i]
109
                    Points[1] = candidates[j]
110

    
111
        if len(Points) == 2 and len(Dir) > 0:
112
            x1 = Points[0][0] + Dir[0][0]*0.5
113
            y1 = Points[0][1] + Dir[0][1]*0.5
114
            x2 = Points[1][0] + Dir[0][0]*0.5
115
            y2 = Points[1][1] + Dir[0][1]*0.5
116
            return LineVector(x1, y1, x2, y2)
117
        else:
118
            dx1 = rhs.x1 - self.x1
119
            dy1 = rhs.y1 - self.y1
120
            dot = dx1*self.dx + dy1*self.dy
121
            tmp = (self.x1 + dot*self.dx, self.y1 + dot*self.dy)
122
            Dir.append((rhs.x1 - tmp[0], rhs.y1 - tmp[1]))
123
            x1 = Points[0][0] + Dir[0][0]*0.5
124
            y1 = Points[0][1] + Dir[0][1]*0.5
125
            x2 = Points[1][0] + Dir[0][0]*0.5
126
            y2 = Points[1][1] + Dir[0][1]*0.5
127
            return LineVector(x1, y1, x2, y2)
128

    
129
        return LineVector(self.x1, self.y1, self.x2, self.y2)
130

    
131
    def Draw(self, img, r, g, b):
132
        cv2.line(img, (int(self.x1), int(self.y1)), (int(self.x2), int(self.y2)), (r, g, b), 1)
133

    
134
class LineDetector():
135
    def __init__(self, image):
136
        thresh = 127
137
        self.image = cv2.threshold(image, thresh, 255, cv2.THRESH_BINARY)[1]
138
        self.width, self.height = self.image.shape[::-1]
139
        self.Result = np.zeros((self.width, self.height, 3), np.uint8)
140

    
141
    '''
142
        @brief symbol을 기준으로 양쪽으로 이미지에서 직선을 검출한다.
143
        @author humkyung
144
        @date   2018.04.??
145
    '''
146
    def Detect(self, symbol, offsetX, offsetY):
147
        res = []
148

    
149
        try:
150
            pool = []
151
            if (0 == symbol.angle) or (3.14 == symbol.angle):
152
                right = int(symbol.rect().right())
153
                left = int(symbol.rect().left())
154

    
155
                pt = [right - offsetX, int(symbol.center.y()) - offsetY]
156
                pool.append([[1,0], pt])
157
                pt = [left - offsetX, int(symbol.center.y()) - offsetY]
158
                pool.append([[-1,0], pt])
159
            elif (1.57 == symbol.angle) or (4.71 == symbol.angle):  # rotated by 90 or 270 degree
160
                bottom = int(symbol.rect().bottom())
161
                top = int(symbol.rect().top())
162

    
163
                pt = [int(symbol.center.x()) - offsetX, bottom - offsetY]
164
                pool.append([[0,1], pt])
165
                pt = [int(symbol.center.x()) - offsetX, top - offsetY]
166
                pool.append([[0,-1], pt])
167

    
168
            while len(pool) > 0:
169
                dir, pt = pool.pop()
170
                line = self.detectLine(pt,dir)
171
                if line is not None:
172
                    res.append(line)
173
                    if ([1,0] == dir) or ([-1,0] == dir):   # turn up/down
174
                        pt[0] = line[1][0]
175
                        pool.append([[0,1], pt])
176
                        pool.append([[0,-1], pt])
177
                    elif ([0,1] == dir) or ([0,-1] == dir): # turn left/right
178
                        pt[1] = line[1][1]
179
                        pool.append([[1,0], pt])
180
                        pool.append([[-1,0], pt])
181

    
182
            return res
183
        except Exception as ex:
184
            print('error occured({}) in {}:{}'.format(ex, sys.exc_info()[-1].tb_frame.f_code.co_filename, sys.exc_info()[-1].tb_lineno))
185

    
186
    '''
187
        @brief  detect a line along given direction
188
        @author humkyung
189
        @date   2018.04
190
    '''
191
    def detectLine(self, pt, dir):
192
        from AppDocData import AppDocData
193

    
194
        white = [255]
195
        windowSize = AppDocData.instance().getSlidingWindowSize()
196
        xHalf = int(windowSize[0]*0.5)
197
        yHalf = int(windowSize[1]*0.5)
198

    
199
        if ([1,0] == dir):
200
            image = self.image[(pt[1]-yHalf):(pt[1]+yHalf), pt[0]:self.width]
201
            imgWidth, imgHeight = image.shape[::-1]
202
            for i in range(imgWidth-windowSize[0]):
203
                window = image[0:windowSize[1], i:i+windowSize[0]]
204
                if (white == window[0:windowSize[1],0:windowSize[0]]).all(): break
205
            if i > windowSize[0]:
206
                self.image[(pt[1]-yHalf):(pt[1]+yHalf), pt[0]:(pt[0]+i)] = white
207
                return [(pt[0], pt[1]), (pt[0] + i, pt[1])]
208
        elif ([-1,0] == dir):
209
            image = self.image[(pt[1]-yHalf):(pt[1]+yHalf), 0:pt[0]]
210
            imgWidth, imgHeight = image.shape[::-1]
211
            for i in range(imgWidth-windowSize[0], -1, -1):
212
                window = image[0:windowSize[1], i:i+windowSize[0]]
213
                if (white == window[0:windowSize[1],0:windowSize[0]]).all(): break
214
            if abs(pt[0] - i) > windowSize[0]: 
215
                self.image[int(pt[1]-yHalf):int(pt[1]+yHalf), (i+windowSize[0]):pt[0]] = white
216
                return [(pt[0], pt[1]), (i+windowSize[0], pt[1])]
217
        elif ([0,1] == dir):
218
            windowSize.reverse()
219
            xHalf = int(windowSize[0]*0.5)
220
            yHalf = int(windowSize[1]*0.5)
221

    
222
            image = self.image[pt[1]:self.height, int(pt[0]-xHalf):int(pt[0]+xHalf)]
223
            imgWidth, imgHeight = image.shape[::-1]
224
            for i in range(imgHeight-windowSize[1]):
225
                window = image[i:i+windowSize[1], 0:windowSize[0]]
226
                if (white == window[0:windowSize[1],0:windowSize[0]]).all(): break
227
            if i > windowSize[1]:
228
                self.image[(pt[1]):(pt[1]+i), (pt[0]-xHalf):(pt[0]+xHalf)] = white
229
                return [(pt[0], pt[1]), (pt[0], pt[1] + i)]
230
        elif ([0,-1] == dir):
231
            windowSize.reverse()
232
            xHalf = int(windowSize[0]*0.5)
233
            yHalf = int(windowSize[1]*0.5)
234

    
235
            image = self.image[0:pt[1], int(pt[0]-xHalf):int(pt[0]+xHalf)]
236
            imgWidth, imgHeight = image.shape[::-1]
237
            for i in range(imgHeight-windowSize[1], -1, -1):
238
                window = image[i:i+windowSize[1], 0:windowSize[0]]
239
                if (white == window[0:windowSize[1],0:windowSize[0]]).all(): break
240
            if abs(pt[1] - i) > windowSize[1]:
241
                self.image[(i+windowSize[1]):pt[1], (pt[0]-xHalf):(pt[0]+xHalf)] = white
242
                return [(pt[0], pt[1]), (pt[0], i+windowSize[1])]
243

    
244
        return None
245

    
246
    def save(self):
247
        cv2.imwrite('d:\\test.png', self.image)