Statistiques
| Révision :

root / bin / image2geometry / bib_sepals.py @ 1

Historique | Voir | Annoter | Télécharger (12,62 ko)

1
import numpy as np
2
import matplotlib.pyplot as plt
3
import math
4
from scipy import optimize
5

    
6
from titk_tools.io import imread, imsave, SpatialImage
7

    
8
def center_on_point(img, cm):
9
        '''
10
        Centers the image img on the point cm
11
        '''
12
        x = np.array(np.where(img > 0) )
13
        xp=np.zeros_like(x)
14
        img1=np.zeros_like(img)
15
        s=img.shape
16
        center_img=np.array(s)/2
17
        for i in [0, 1, 2]:
18
                xp[i]=x[i]+center_img[i]-cm[i]
19
        for i in range(0, len(x[0])):
20
                if ((xp[0][i]>=0) and (xp[0][i]<s[0]) and (xp[1][i]>=0) and (xp[1][i]<s[1]) and (xp[2][i]>=0) and (xp[2][i]<s[2])):
21
                        img1[xp[0][i],xp[1][i],xp[2][i]]=img[x[0][i],x[1][i],x[2][i]]
22
        img1=SpatialImage(img1)
23
        img1.resolution=img.resolution
24
        return img1
25

    
26

    
27
def center_on_cm(img):
28
        '''
29
        Centers the image img on the point cm
30
        '''
31
        s=img.shape
32
        res=img.voxelsize
33
        x = np.array(np.where(img > 0) )
34
        cm = np.array([np.mean(x[i]) for i in [0,1,2]])
35
        xp=np.zeros_like(x)
36
        img1=np.zeros_like(img)
37
        center_img=np.array(s)/2
38
        for i in [0, 1, 2]:
39
                xp[i]=x[i]+center_img[i]-cm[i]
40
        for i in range(0, len(x[0])):
41
                if ((xp[0][i]>=0) and (xp[0][i]<s[0]) and (xp[1][i]>=0) and (xp[1][i]<s[1]) and (xp[2][i]>=0) and (xp[2][i]<s[2])):
42
                        img1[xp[0][i],xp[1][i],xp[2][i]]=img[x[0][i],x[1][i],x[2][i]]
43
        img1=SpatialImage(img1)
44
        img1.voxelsize=res
45
        return img1
46

    
47

    
48
def principal_directions(img, threshold=10):        
49
        '''
50
        Returns the first two principal eigen vectors of an input image
51
        Fixed threshold of 10 for a 8 bit image
52
        It suppposes an isotropic sampling of the image.
53
        '''
54
        x,y,z = np.where(img > threshold) ## appropriate thresholding 
55
        x = x - np.mean(x)
56
        y = y - np.mean(y)
57
        z = z - np.mean(z)
58
        coords = np.vstack([x,y,z])
59
        cov = np.cov(coords)
60
        evals,evecs = np.linalg.eig(cov)
61
        sort_indices = np.argsort(evals)[::-1]
62
        return list(evecs[:,sort_indices][0:2])
63

    
64
def direct_frame(pv):
65
        '''
66
        Constructs a direct frame with first two unitvectors v0 and v1
67
        '''
68
        return np.array([pv[0], pv[1], np.cross(pv[0], pv[1])])
69

    
70

    
71
def write_transformation(T, trsf_file):
72
        '''
73
        writes the affine transformation T (4x4 matrix) in
74
        a textfile read by blockmatching.
75
        '''
76
        f = open(trsf_file,'w')
77
        f.write("( "+"\n")
78
        f.write("08 "+"\n")
79
        for i in T:
80
                for j in i:
81
                        f.write("  "+str(j)+" ")
82
                f.write("\n")
83
        f.write(") ")
84
        return
85

    
86

    
87

    
88
def write_rigid(trsf_file, R, a):
89
        '''
90
        write a rigid transformation with rotation matrix R and translation a
91
        '''
92
        T = np.identity(4)
93
        T[0:3,0:3] = R
94
        T = np.transpose(T)
95
        T[0:3,3] = a
96
        write_transformation(T, trsf_file)
97
        return
98

    
99

    
100

    
101
def write_superposing_transfo(pv_flo, pv_ref, img, trsf_file):
102
        '''
103
        - pv_flo and pv-ref are lists of the first two principal vectors of the floating and the reference images respectively
104
        - img is the floating image
105
        '''
106
        # compute the rotation matrix R
107
        pframe_ref = direct_frame(pv_ref)
108
        pframe_flo = direct_frame(pv_flo)
109
        F = np.transpose(pframe_ref)
110
        G = np.transpose(pframe_flo)
111
        R = np.matmul(G, np.linalg.inv(F))
112
        # blockmatching rotates arround the origin,
113
        # so a rotation arround the middle of the image contains an additional translation t
114
        s = img.shape
115
        res = img.voxelsize
116
        com = np.array(res)*np.array(s)/2
117
        i = np.identity(3)
118
        t = np.dot((i-R),np.array(com))
119
        R = np.transpose(R)
120
        write_rigid(trsf_file, R, t)
121
        return
122

    
123

    
124
def add_side_slices(Img, x0, x1, y0, y1, z0, z1):
125
        """
126
        adds 0 value slices on different sides of the image
127
        """
128
        s=Img.shape
129
        ss=(s[0]+x0+x1,s[1]+y0+y1,s[2]+z0+z1)
130
        Img_new = (SpatialImage(np.zeros(ss))).astype('uint8')
131
        Img_new[x0:ss[0]-x1,y0:ss[1]-y1,z0:ss[2]-z1]=Img
132
        Img_new.voxelsize = Img.voxelsize
133
        return Img_new
134

    
135

    
136
def remove_side_slices(Img, x0, x1, y0, y1, z0, z1):
137
        """
138
        removes slices on different sides of the image
139
        """
140
        ss=Img.shape
141
        Img_new=Img[x0:ss[0]-x1,y0:ss[1]-y1,z0:ss[2]-z1]
142
        #Img_new.resolution = Img.resolution
143
        return Img_new
144

    
145

    
146
def measure_hight(a):
147
        b= np.where(a)
148
        height = 0
149
        if b[0].shape[0]>0 :
150
                height = b[0][-1]
151
        return height
152

    
153

    
154
def measure_height(a):
155
        b= np.where(a)
156
        height = [0,0]
157
        if b[0].shape[0]>0 :
158
                height = [b[0].min(),b[0].max()]
159
        return height
160

    
161

    
162
def extract_curve(x_section):
163
        sx = x_section.shape
164
        xx = np.zeros((sx[0],2))
165
        for i in range(0,sx[0]):
166
                xx[i,0] = i
167
                xx[i,1] = measure_hight(x_section[i,:])
168
        return xx
169

    
170

    
171
def extract_curves(x_section):
172
        sx = x_section.shape
173
        xup = np.zeros((sx[0],2))
174
        xdown = np.zeros((sx[0],2))
175
        for i in range(0,sx[0]):
176
                xup[i,0] = i
177
                xdown[i,0] = i
178
                xup[i,1] = measure_height(x_section[i,:])[1]
179
                xdown[i,1] = measure_height(x_section[i,:])[0]
180
        return xup, xdown
181

    
182

    
183
def write_curve(y, pixelsize, filename) :
184
        FILE = open(filename,"w")
185
        FILE.write('# '+str(pixelsize[0])+' '+str(pixelsize[1])+'\n')
186
        s = y.shape
187
        #FILE.write('# '+str(s[0])+'\n')
188
        for i in range(0,s[0]) :
189
                FILE.write(str(y[i,0])+' '+str(y[i,1])+'\n')
190
        FILE.close()
191
        return
192

    
193

    
194
def struct_element_sphere(radius):
195
        s=int(2*radius+1)
196
        structure=np.zeros((s,s,s))
197
        center = np.array([radius, radius, radius])
198
        Nl=range(s)
199
        for i in Nl:
200
                for j in Nl:
201
                        for k in Nl:
202
                                p=np.array([i,j,k])
203
                                d=p-center
204
                                dist=np.sqrt(sum(d*d))
205
                                if dist<=radius:
206
                                        structure[i,j,k]=1
207
        return structure
208

    
209

    
210
def create_artificial_sepal(AA, RR, resolution):
211
        # A = parameters of an ellipse, as projected on xy plane (2d array)
212
        # R = curvature radius in directions x and y (2d array)
213
        # (A and R are given in micrometers)
214
        # resolution = voxelsize of the output stack
215
        A = np.array([AA[i]/resolution[i] for i in [0,1]])
216
        R = np.array([RR[i]/resolution[i] for i in [0,1]])
217
        h = np.array([math.sqrt(R[i]**2-A[i]**2) for i in range(len(A))])
218
        zmax=np.array([R[i]-math.sqrt(R[i]**2-A[i]**2) for i in [0,1]])
219
        #zmax= int(math.sqrt(zmax[0]*zmax[1]))
220
        zmax=zmax.mean()
221
        #zmax = 5
222
        print(zmax)
223
        marge = 0.2
224
        # creating the stack and the surface
225
        s = (int(A[0]*2*(1+marge)), int(A[1]*2*(1+marge)), int(zmax*(1+2*marge)))
226
        cm = np.array(s)/2
227
        x = np.array(range(0,s[0]))
228
        y = np.array(range(0,s[1]))
229
        xx = x - cm[0]
230
        yy = y - cm[1]
231
        #xgrid, ygrid = np.meshgrid(xx, yy, sparse=True)  # make sparse output arrays
232
        #mask=(((xgrid**2)/float(A[0])**2+(ygrid**2)/float(A[1])**2)<1).astype('uint8')
233
        z = np.zeros((s[0],s[1]))
234
        stack= np.zeros(s).astype('uint8')
235
        for i in x:
236
                for j in range(0,s[1]):
237
                        if xx[i]**2/float(A[0])**2+yy[j]**2/float(A[1])**2<=1 :
238
                                zx = (math.sqrt(R[0]**2-xx[i]**2)-h[0])*(1-abs(yy[j])/A[1])
239
                                zy = (math.sqrt(R[1]**2-yy[j]**2)-h[1])*(1-abs(xx[i])/A[0])
240
                                z[i,j] = math.sqrt(zx*zy)
241
                                #z[i,j] = (zx+zy)/2
242
                                #z[i,j] = 5
243
                                stack[i,j,int(zmax*marge+z[i,j])]=1
244
        stack = SpatialImage(stack)
245
        stack.resolution = resolution
246
        return z, stack
247

    
248

    
249

    
250
def create_artificial_sepal_ellipse(AA, H, resolution):
251
        '''
252
        # A = parameters of an ellipse, as projected on xy plane (2d array)
253
        # R = curvature radius in directions x and y (2d array)
254
        # (A and R are given in micrometers)
255
        # resolution = voxelsize of the output stack
256
        '''
257
        A = np.array([AA[i]/resolution[i] for i in [0,1]])
258
        h = H/resolution[2]
259
        zmax=h
260
        marge = 0.2
261
        # creating the stack and the surface
262
        s = (int(A[0]*2*(1+marge)), int(A[1]*2*(1+marge)), int(zmax*(1+5*marge)))
263
        cm = np.array(s)/2
264
        x = np.array(range(0,s[0]))
265
        y = np.array(range(0,s[1]))
266
        xx = x - cm[0]
267
        yy = y - cm[1]
268
        #xgrid, ygrid = np.meshgrid(xx, yy, sparse=True)  # make sparse output arrays
269
        #mask=(((xgrid**2)/float(A[0])**2+(ygrid**2)/float(A[1])**2)<1).astype('uint8')
270
        z = np.zeros((s[0],s[1]))
271
        #z = -h*((xgrid**2)/float(A[0])**2+(ygrid**2)/float(A[1])**2)
272
        stack= np.zeros(s).astype('uint8')
273
        for i in x:
274
                for j in y:
275
                        if xx[i]**2/float(A[0])**2+yy[j]**2/float(A[1])**2<=1 :
276
                                z[i,j] = -h*((xx[i]**2)/float(A[0])**2+(yy[j]**2)/float(A[1])**2-1)
277
                                stack[i,j,int(zmax*4*marge+z[i,j])]=1
278
        stack = SpatialImage(stack)
279
        stack.voxelsize = resolution
280
        return z, stack
281

    
282
# -------------------------------------------------------------------
283

    
284
def read_curve(filename) :
285
        """
286
        Reads the coordinates of the point-list defining a 2D curve.
287
        **Returns**
288
        y : 2-column array
289
                defines a curve as an ordered list of points, 
290
                each row of the array represents a point on the curve and contains
291
                its 2D cartesian coordinates
292
        pixelsize : tuple (res0, res1) in micrometers
293
        """        
294
        x0 = []
295
        pixelsize = (1.0,1.0)
296
        for line in file(filename):
297
                if line[0] == "#" :
298
                        line = line.lstrip('# ')
299
                        line = line.rstrip('\n')
300
                        line_list = [float(x) for x in line.split(' ')]
301
                        pixelsize = (line_list[0], line_list[1])
302
                else :
303
                        line = line.rstrip('\n')
304
                        line_list = [float(x) for x in line.split(' ')]
305
                        x0.append(line_list)
306
        y = np.array(x0)
307
        return y, pixelsize
308

    
309

    
310

    
311
def distance(p1, p2):
312
        return np.sqrt((p1[0]-p2[0])**2+(p1[1]-p2[1])**2)
313

    
314
def distance_to_line(p1, p2, p0):
315
        '''
316
        gives the distance of point p0 to the line defined by points p1 and p2 (in 2d)
317
        '''
318
        return abs((p2[1]-p1[1])*p0[0]-(p2[0]-p1[0])*p0[1]+p2[0]*p1[1]-p2[1]*p1[0])/distance(p1,p2)
319

    
320
def curvature(p1,p2,p3):
321
        t1 = (p3[0]**2-p2[0]**2+p3[1]**2-p2[1]**2)/(2.0*(p3[1]-p2[1]))
322
        t2 = (p2[0]**2-p1[0]**2+p2[1]**2-p1[1]**2)/(2.0*(p2[1]-p1[1]))
323
        n1 = (p3[0]-p2[0])/(p3[1]-p2[1])
324
        n2 = (p2[0]-p1[0])/(p2[1]-p1[1])
325
        pcx = (t1-t2+p3[1]-p1[1])/(n1-n2)
326
        pc = [pcx, -n1*pcx+t1/2+p3[1]/2+p1[1]/2]
327
        R = distance(p1, pc)
328
        return 1.0/R, pc
329

    
330

    
331
def curvature2(p1,p2,p3):
332
        # http://www.ambrsoft.com/TrigoCalc/Circle3D.htm
333
        A = p1[0]*(p2[1]-p3[1])-p1[1]*(p2[0]-p3[0])+p2[0]*p3[1]-p3[0]*p2[1]
334
        B = (p1[0]**2+p1[1]**2)*(p3[1]-p2[1])+(p2[0]**2+p2[1]**2)*(p1[1]-p3[1])+(p3[0]**2+p3[1]**2)*(p2[1]-p1[1])
335
        C = (p1[0]**2+p1[1]**2)*(p3[0]-p2[0])+(p2[0]**2+p2[1]**2)*(p1[0]-p3[0])+(p3[0]**2+p3[1]**2)*(p2[0]-p1[0])
336
        D = (p1[0]**2+p1[1]**2)*(p3[0]*p2[1]-p2[0]*p3[1])+(p2[0]**2+p2[1]**2)*(p1[0]*p3[1]-p3[0]*p1[1])+(p3[0]**2+p3[1]**2)*(p2[0]*p1[1]-p1[0]*p2[1])
337
        x = -B/(2*A)
338
        y = C/(2*A)
339
        pc = [x, y]
340
        R = distance(p1, pc)
341
        return 1.0/R, pc
342

    
343

    
344
def curve_perle(y, first, last, step):
345
        yy=[]
346
        for i in range(first, last, step):
347
                if y[i][1]>0:
348
                        yy.append(y[i])
349
        if i<last:
350
                yy.append(y[last])
351
        return np.array(yy)
352

    
353

    
354

    
355
def compute_curvature_radius(x,y):
356
        # coordinates of the barycenter
357
        x_m = np.mean(x)
358
        y_m = np.mean(y)
359
        def calc_R(c):
360
                """ calculate the distance of each 2D points from the center c=(xc, yc) """
361
                return np.sqrt((x-c[0])**2 + (y-c[1])**2)
362
        #
363
        def calc_ecart(c):
364
                """ calculate the algebraic distance between the 2D points and the mean circle centered at c=(xc, yc) """
365
                Ri = calc_R(c)
366
                return Ri - Ri.mean()
367
        #
368
        center_estimate = x_m, y_m
369
        center_2, ier = optimize.leastsq(calc_ecart, center_estimate)
370
        #
371
        xc_2, yc_2 = center_2
372
        Ri_2       = calc_R(center_2)
373
        R_2        = Ri_2.mean()
374
        residu_2   = sum((Ri_2 - R_2)**2)
375
        pc=[xc_2, yc_2]
376
        return R_2, pc
377

    
378

    
379
def analyze_curve1(name, y, pixelsize, outfilename, graph_name='graph.png'):
380
        # isotropic pixels
381
        y[:,0] = y[:,0]*pixelsize[0]
382
        y[:,1] = y[:,1]*pixelsize[1]
383
        maxv = [y[:,i].max() for i in [0,1]]
384
        # find first and last points on the curve
385
        step = 5
386
        sep_indices = np.where(y[:,1])[0]
387
        first = sep_indices[0]#+step/2
388
        last = sep_indices[-1]#-step/2
389
        # compute flat length
390
        flatlength = distance(y[first],y[last])
391
        print("flat length =", flatlength," um")
392
        # compute curved length and height (maximal distance to the base)
393
        yy = curve_perle(y, first, last, step)
394
        length = 0
395
        height = 0
396
        height_index = 0
397
        p1 = yy[0]
398
        p2 = yy[-1]
399
        for i in range(0, len(yy)-1):
400
                length = length + distance(yy[i+1],yy[i])
401
                d = distance_to_line(p1, p2, yy[i])
402
                if d>height :
403
                        height = d
404
                        height_index = i
405
        print("length = ",length," um")
406
        print("height = ",height," um")
407
        middle_point = yy[int(len(yy)/2)]
408
        # compute curvature radius by fitting the sepal section to a circle
409
        yyy=y[first:last]
410
        R, pc = compute_curvature_radius(yyy[:,0],yyy[:,1])
411
        print("R=",R," um")
412
        # cutting curve in pieces in order to estimate curvature radius on portions of the curve
413
        slength=len(yyy)
414
        y21=yyy[0:int(slength/2)]
415
        y22=yyy[int(slength/2):slength]
416
        y41=yyy[0:int(slength/4)]
417
        y423=yyy[int(slength/4):3*int(slength/4)]
418
        y44=yyy[3*int(slength/4):slength]
419
        # and computing the curvature radius for each portion
420
        R21, pc = compute_curvature_radius(y21[:,0],y21[:,1])
421
        R22, pc = compute_curvature_radius(y22[:,0],y22[:,1])
422
        R41, pc = compute_curvature_radius(y41[:,0],y41[:,1])
423
        R423, pc = compute_curvature_radius(y423[:,0],y423[:,1])
424
        R44, pc = compute_curvature_radius(y44[:,0],y44[:,1])
425
        fig = plt.figure()
426
        plt.plot(yy[:,0], yy[:,1], 'bo')
427
        plt.plot(y[:,0], y[:,1], 'r')
428
        plt.plot(y[first][0], y[first][1], 'ro')
429
        plt.plot(middle_point[0], middle_point[1], 'ro')
430
        plt.plot(y[last][0], y[last][1], 'ro')
431
        plt.plot(yy[height_index][0], yy[height_index][1], 'go')
432
        plt.plot(pc[0], pc[1], 'bx')
433
        plt.gca().set_aspect('equal', adjustable='box')
434
        #plt.show()
435
        fig.savefig(graph_name)
436
        #plt.close()
437
        # write the data into a file
438
        FILE = open(outfilename,"a")
439
        FILE.write(name+';'+str(length)+';'+str(R)+';'+str(flatlength)+';'+str(height)+'\n')
440
        FILE.close()
441
        return length, flatlength, height, R, R21, R22, R41, R423, R44
442

    
443
def imsave2D(filename, matrix):
444
        fig = plt.figure()
445
        plt.gca().imshow(np.transpose(matrix), interpolation='none')
446
        fig.savefig(filename)
447
        return