44This package implements the method from
55 Dubska et al, PCLines - Line detection with parallel coordinates, CVPR 2011
66
7+
8+ Module
9+ ------
10+ The module provides a high-level function for line detection in image
11+ and also low-level functions for
12+ * accumulation of observations to PCLines space,
13+ * point mapping from PCLines space to homogeneous lines
14+ that can be used to construct a custom PCLines transform of user-defined
15+ edge points.
16+
17+
18+ See also
19+ --------
20+ * pclines.accumulate
21+ * pclines.find_peaks
22+ * pclines.line_parameters
23+
24+
25+ References
26+ ----------
27+ [1] Dubska et al, PCLines - Line detection with parallel coordinates, CVPR 2011
28+
29+
730"""
831
32+
933import numpy as np
1034import numba as nb
1135from skimage .feature import peak_local_max
1236from skimage .morphology .grey import erosion , dilation
1337
1438
15- def linear_transform (src , dst ):
39+ def _linear_transform (src , dst ):
40+ """ Parameters of a linear transform from range specifications """
1641 (s0 , s1 ), (d0 ,d1 ) = src , dst
1742 w = (d1 - d0 ) / (s1 - s0 )
1843 b = d0 - w * s0
1944 return w , b
2045
2146
2247class Normalizer :
48+ """
49+ Range mapping
50+ """
2351 def __init__ (self , src_range , dst_range = (0 ,1 )):
24- self .w , self .b = linear_transform (src_range , dst_range )
25- self .wi , self .bi = linear_transform (dst_range , src_range )
52+ # TODO: check ranges
53+ self .w , self .b = _linear_transform (src_range , dst_range )
54+ self .wi , self .bi = _linear_transform (dst_range , src_range )
2655
2756 def transform (self , x ):
28- """ src -> dst """
57+ """ src -> dst mapping of x """
2958 return self .w * x + self .b
3059
3160 def inverse (self , x ):
32- """ dst -> src """
61+ """ dst -> src mapping of x """
3362 return self .wi * x + self .bi
3463
3564 __call__ = transform
3665
3766
38- def accumulate (x , bbox = None , d = 256 ):
67+ def accumulate (x , w = None , bbox = None , d = 256 ):
3968 """
4069 Accumulate observation in PCLines space
4170
4271 bbox : tuple
4372 (x,y,w,h) format
4473 """
74+ # TODO: Check inputs
75+
76+
4577 # Create accumulator
4678 acc_shape = d ,2 * d - 1
4779 A = np .zeros (acc_shape , "f" ) # The accumulator
4880
49- if bbox is None :
50- # autodetection of bbox
51- pass
52-
53- # Normalizers
81+ # Axis normalizers
5482 def normalizers ():
5583 x ,y ,w ,h = bbox
5684 shape = (w ,h )
5785 ranges = d * np .array (shape )/ max (shape )
5886 ofs = (d - ranges ) / 2
5987 (x0 ,y0 ),(x1 ,y1 ) = ofs , (ranges - 1 )+ ofs
60- print (bbox )
6188 norm0 = Normalizer ((y ,y + h ), (y1 , y0 ))
6289 norm1 = Normalizer ((x ,x + w ), (x0 , x1 ))
6390 norm2 = Normalizer ((y ,y + h ), (y0 , y1 ))
6491 return norm0 , norm1 , norm2
6592
6693 norm0 , norm1 , norm2 = normalizers ()
6794
68- x = [
69- norm0 (x [:,1 ]),
70- norm1 (x [:,0 ]),
71- norm2 (x [:,1 ])
72- ]
95+ # Coordinates on parallel axes
96+ x0 ,x1 = np .split (x ,2 ,axis = 1 )
97+ x = np .concatenate ([norm0 (x1 ), norm1 (x0 ), norm2 (x1 )], axis = 1 )
7398
74- for a ,b ,c in zip (* x ): # remove space wraping
99+ # Rasterize the lines
100+ for a ,b ,c in x : # remove space wraping
75101 t_part = np .linspace (a ,b ,d )
76102 s_part = np .linspace (b ,c ,d )
77103 c = np .arange (2 * d - 1 ,dtype = "i" )
@@ -82,51 +108,51 @@ def normalizers():
82108 return A
83109
84110
85- def lines_parameters (peaks , d , bbox ):
111+ def lines_parameters (peaks , bbox , d ):
112+ """
113+ Get homogeneous line parameters from location in the accumulator
114+ """
86115 u = peaks [:,1 ]
87116 v = peaks [:,0 ]
88-
89117 #centrovanie
90118 u = u - (d - 1 )
91-
92119 x ,y ,w ,h = bbox
93120 shape = w ,h
94121 m = max (shape ) - 1
95122 normV = Normalizer ((0 ,d - 1 ),(- m / 2 , m / 2 ))
96123 v = normV (v )
97-
98124 f = u < 0
99-
100- l = np .array ([f * (d + u )+ (1 - f )* (d - u ), u , - v * d ], "f" ).T
101-
125+ l = np .array ([f * (d + u )+ (1 - f )* (d - u ), u , - v * d ], "f" ).T # TODO: add reference to eq in paper
102126 tx ,ty = x + 0.5 * w , y + 0.5 * h
103-
104127 l [:,2 ] -= l [:,0 ]* tx + l [:,1 ]* ty
105-
106128 return l
107129
108130
109131
110- # @nb.njit("(f4[:,:],f4[:,:])")
111- # def rasterize_lines(lines, acc):
112- # """
113- # """
114- # d = acc.shape[1]
115- # for i in range(lines.shape[0]):
116- # y0, y1 = lines[i]
117- # dy = (y1 - y0) / (d-1)
118- # y = y0
119- # for x in range(d):
120- # acc[x,y] += 1
121- # y += dy
122-
132+ @nb .njit ("(f4[:,:],f4[:,:],i4)" )
133+ def rasterize_polylines (lines , acc , d ):
134+ """
135+ """
123136
124137
125138def find_peaks (A , t ):
139+ """
140+ Retrieve locations with prominent local maxima in the accumulator
141+ """
126142 prominence = dilation (A + 1 )/ erosion (A + 1 )
127143 peaks = peak_local_max (A , threshold_abs = t , min_distance = 1 )
128144 r ,c = peaks [:,0 ], peaks [:,1 ]
129145 value = A [r ,c ]
130146 valid = prominence [r ,c ] > 1.3
131147 return peaks [valid ], value [valid ]
132-
148+
149+
150+ def get_lines (image ):
151+ """
152+ PCLines transform of an image
153+ """
154+ # TODO: Get edges
155+ # TODO: Accumulate
156+ # TODO: Locate peaks
157+ # TODO: Transform peaks to line parameters
158+ pass
0 commit comments