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
|