forgot to change two asd files
[woropt.git] / angular-illumination.lisp
blob3749299cb359df29123f0538019913453b39738e
1 (in-package :run)
3 ;; run the following code to test the downhill simplex optimizer on a
4 ;; 2d function:
7 #+nil
8 (time (let ((start (make-array 2 :element-type 'my-float
9 :initial-contents (list 1.5d0 1.5d0))))
10 (simplex-anneal:anneal (simplex-anneal:make-simplex start 1d0)
11 #'rosenbrock
12 :ftol 1d-5)))
15 ;; +----- | |
16 ;; | \-- | |
17 ;; | \- | |
18 ;; | \- | |
19 ;; | \| |
20 ;; | || |
21 ;; | | |
22 ;; |-------------+-------+------- <- z
23 ;; -nf /0 f
24 ;; object lens bfp
26 (defun draw-ray-into-vol (x-mm y-mm bfp-ratio-x bfp-ratio-y vol
27 &key (dx-mm .2d-3) (dz-mm 1d-3)
28 (shift-z 0d0))
29 (destructuring-bind (z y x)
30 (array-dimensions vol)
31 (let* ((f (lens:focal-length-from-magnification 63d0))
32 (na 1.38d0)
33 (ri 1.515d0)
34 (bfp-radius (lens:back-focal-plane-radius f na))
35 (obj (lens:make-thin-objective :normal (v 0d0 0d0 -1d0)
36 :center (v)
37 :focal-length f
38 :radius bfp-radius
39 :numerical-aperture na
40 :immersion-index ri))
41 (theta (lens:find-inverse-ray-angle x-mm y-mm obj))
42 (phi (atan y-mm x-mm))
43 (start (v (* bfp-ratio-x bfp-radius)
44 (* bfp-ratio-y bfp-radius)
45 f))
46 (dx dx-mm)
47 (dz dz-mm)
48 (cz (* .5d0 z)) ;; position that is in the center of front focal plane
49 (cy (* .5d0 y))
50 (cx (* .5d0 x))
51 (nf (* ri f)))
52 (macrolet ((plane (direction position)
53 ;; for defining a plane that is perpendicular to an
54 ;; axis and crosses it at POSITION
55 (declare (type (member :x :y :z) direction))
56 (let* ((normal (ecase direction
57 (:x (v 1d0))
58 (:y (v 0d0 1d0))
59 (:z (v 0d0 0d0 1d0)))))
60 `(let* ((pos ,position)
61 (center (v* ,normal pos))
62 (outer-normal (normalize center)))
63 (declare (type double-float pos))
64 (lens::make-disk :normal outer-normal :center center)))))
65 ;; define the borders of the viewing volume, distances in mm
66 (let ((p+z (plane :z (- (* dz (- z cz))
67 nf)))
68 (p-z (plane :z (- (* dz (- (- z cz)))
69 nf)))
70 (p+y (plane :y (* dx (- y cy))))
71 (p-y (plane :y (* dx (- (- y cy)))))
72 (p+x (plane :x (* dx (- x cx))))
73 (p-x (plane :x (* dx (- (- x cx))))))
74 (multiple-value-bind (ro s)
75 (lens:thin-objective-ray obj
76 start
77 (v* (v (* (cos phi) (sin theta))
78 (* (sin phi) (sin theta))
79 (cos theta))
80 -1d0))
81 (setf s (v+ s (v 0d0 0d0 (* dz shift-z))))
82 (let* ((nro (normalize ro)))
83 (macrolet ((hit (plane)
84 ;; (declare (type lens::disk plane))
85 ;; find intersection between plane and the ray
86 `(multiple-value-bind (dir hit-point)
87 (lens::plane-ray ,plane
88 ;; shift start of vector a bit
90 nro)
91 (declare (ignore dir))
92 hit-point))
93 (pixel (hit-expr)
94 ;; convert coordinates from mm into integer pixel positions
95 `(let ((h ,hit-expr))
96 (declare (type (or null vec) h))
97 (when h
98 (make-vec-i
99 :z (floor (+ cz (/ (+ (aref h 2) nf) dz)))
100 :y (floor (+ cy (/ (aref h 1) dx)))
101 :x (floor (+ cx (/ (aref h 0) dx))))))))
102 (let* ((h+z (pixel (hit p+z)))
103 (h-z (pixel (hit p-z)))
104 (h+y (pixel (hit p+y)))
105 (h-y (pixel (hit p-y)))
106 (h+x (pixel (hit p+x)))
107 (h-x (pixel (hit p-x)))
108 ;; make a list of all the points
109 (hlist (list h+z h-z h+y h-y h+x h-x))
110 ;; throw away points that are nil or that contain
111 ;; coordinates outside of the array dimensions
112 (filtered-hlist
113 (remove-if-not #'(lambda (v)
114 (if v
115 (and (< -1 (vec-i-x v) x)
116 (< -1 (vec-i-y v) y)
117 (< -1 (vec-i-z v) z))
118 nil)) hlist))
119 ;; sort best points by x
120 (choice (sort filtered-hlist #'< :key (lambda (v) (vec-i-x v)))))
121 (format t "~a~%" (list 'choice choice))
122 (scan-convert-line3
123 (first choice)
124 (second choice)
125 vol))))))))))
127 #+nil
128 (let ((vol (make-array (list 128 128 128) :element-type '(unsigned-byte 8))))
129 (loop for i in '(4.0d-3 -.2d-3) do
130 (draw-ray-into-vol i 0d0 .99d0 .0d0 vol)
131 #+nil(draw-ray-into-vol i 0d0 -.99d0 .0d0 vol)
132 (draw-ray-into-vol i 0d0 0d0 .99d0 vol)
133 (draw-ray-into-vol i 0d0 0d0 -.99d0 vol))
135 (save-stack-ub8 "/home/martin/tmp/line"
136 vol))
139 #+nil
140 (let ((vol (make-array (list 128 128 128) :element-type '(unsigned-byte 8))))
141 (draw-line3 (make-vec-i :x 108 :y 112 :z 103)
142 (make-vec-i :x 82 :y 102 :z 10)
143 vol))
146 ;; |
147 ;; -------+-------
148 ;; -/ h (3) | \--- (2) q_R=NA/ri*q_max
149 ;; -----------+------------/------------
150 ;; | alpha /--- \-
151 ;; | /-- \
152 ;; | /--- \
153 ;; ---------+-----------------+-------
154 ;; | (0) / (1) q_max=1/(2*pixel)
156 ;; The resolution of the image has to be big enough to fit the top
157 ;; section of the k-sphere with radius |k|=2pi*q_max into the k space.
158 ;; q_max (see (1)) is due to the nyquist theorem and corresponds to 1
159 ;; over two sample widths. The radius of the backfocal plane
160 ;; corresponds to q_R (see (2) ri is the refractive index,
161 ;; e.g. 1.515). It is bigger for an objective with a high NA.
163 ;; A transform with uneven number of samples doesn't have a bin for
164 ;; the nyquist sampling (draw the unit circle and divide it into n
165 ;; equal bins starting from e^(i*0). For uneven n there will be no bin
166 ;; on e^-i (i.e. -1, 1, -1 ...), e.g. n=3). For even n there will be
167 ;; n/2+1 bins ontop of the real axis (e.g. 0=1, 1=e^(-i pi/2), 2=e^-i
168 ;; for n=4, the arguments to the exponential are (i 2 pi j/n) for the
169 ;; j-th bin) and n/2-1 bins below (e.g 3=e^(i pi/2)). In order to
170 ;; simplify fftshift3 I only consider transforms with even n.
171 ;; fftshift moves the n/2+1 bins from the front of the array to the
172 ;; back (for n=4: [0 1 2 3] -> [3 0 1 2]). In the shifted array the
173 ;; highest reverse frequency (bin 3) is mapped to index 0. The origin
174 ;; of k-space (see (0) in the sketch) is therefor mapped to bin n/2-1
175 ;; (bin 1 for n=4). The nyquist frequency is in the last bin n-1 (bin
176 ;; 3 for n=4).
178 ;; We now search for the right z-sampling dz to fit the top of the
179 ;; sphere below the nyquist bin (which corresponds to q_max=1/(2*dz)).
180 ;; |k|=2 pi/lambda = 2 pi q_max, with wavelength lambda
181 ;; lambda=2 dz -> dz = lambda/2.
183 ;; We could use the same sampling x and y to represent the electric
184 ;; field. For small numerical apertures the sampling distance can be
185 ;; increased. This time the radius q_R has to be smaller than the nyquist
186 ;; frequency:
187 ;; 1/(2*dx)=q_R=NA/ri * 1/lambda
188 ;; -> dx= lambda/2 * ri/NA= dz *ri/NA=dz*1.515/1.38
190 ;; The sampling distances dz and dx that I derived above are only good
191 ;; to represent the amplitude psf. When the intensity is to be
192 ;; calculated the sampling distance has to be changed to accomodate
193 ;; for the convolution in k space.
195 ;; The height of the sphere cap (h see (3) in sketch) is
196 ;; h=q_max-q_max*cos(alpha)=q_max ( 1-cos(alpha))
197 ;; =q_max*(1-sqrt(1-sin(alpha)^2))=q_max*(1-sqrt(1-(NA/ri)^2)) The z
198 ;; sample distance dz2 for the intensity psf should correspond to 1/(2
199 ;; dz2)=2 h, i.e. dz2=1/h=dz*2/(1-sqrt(1-(NA/ri)^2))>dz so the necessary z
200 ;; sampling distance for the intensity is in general bigger than for
201 ;; the amplitude.
203 ;; The radius of the convolved donut shape is 2 q_R. Therefor the
204 ;; transversal sampling distance for the intensity has to be smaller:
205 ;; dx2=dx/2.
207 ;; As we are only interested in the intensity psf we can sample the
208 ;; amplitude psf with a sampling distance dz2. The sphere cap is
209 ;; possibly wrapped along the k_z direction. The transversal direction
210 ;; of the amplitude psf has to be oversampled with dx2.
212 ;; To get an angular illumination psf we multiply the values on the
213 ;; sphere with a k_z plane containing a disk that is centered at any
214 ;; k_x and k_y inside the back focal plane. Later I might want to
215 ;; replace this with a gaussian or a more elaborate window function.
217 ;; With a sampling dx2 the radius of the backfocal plane fills half of
218 ;; the k space. The coordinate calculations below are corrected for
219 ;; this. So setting cx to 1. and cy to 0. below would center the
220 ;; circle on the border of the bfp.
222 ;; For n=1.515 and NA=1.38 the ratio dz2/dx2 is ca. 6. Angular
223 ;; blocking allows to increase dz2 and dx2 a bit. Depending on which
224 ;; and how big an area of the BFP is transmitted. Calculating these
225 ;; smaller bounds seems to be quite complicated and I don't think it
226 ;; will speed things up considerably. Also it will be possible to
227 ;; calculate many different angular illuminations from an amplitude
228 ;; otf that has been sampled with dx2 and dz2 without reevalutation of
229 ;; Wolfs integrals.
231 ;; I want to be able to set dz3 and dx3 to the same values that
232 ;; Jean-Yves used for the confocal stack. I have to introduce
233 ;; sx=dx2/dx3 to scale cx and cy into the back focal plane.
236 (let ((k0 nil)
237 (k1 nil)
238 (k2 nil))
239 (defun angular-psf (&key (x 64) (y x) (z 40)
240 (window-radius #.(coerce .2 'my-float))
241 (window-x (- #.(coerce 1 'my-float) window-radius))
242 (window-y #.(coerce 0 'my-float))
243 (pixel-size-x nil)
244 (pixel-size-z nil)
245 (wavelength #.(coerce .480 'my-float))
246 (numerical-aperture #.(coerce 1.38 'my-float))
247 (immersion-index #.(coerce 1.515 'my-float))
248 (integrand-evaluations 30)
249 (debug nil)
250 (initialize nil))
251 (declare (fixnum x y z integrand-evaluations)
252 (my-float window-radius window-x window-y
253 wavelength numerical-aperture
254 immersion-index)
255 ((or null my-float) pixel-size-x pixel-size-z)
256 (boolean debug initialize)
257 (values (simple-array (complex my-float) 3) &optional))
258 ;; changing z,y,x without touching dx or dz leaves the area that is
259 ;; shown in k space constant
260 (let* ((na numerical-aperture)
261 (ri immersion-index)
262 (lambd (/ wavelength ri))
263 (dz (* +one+ .5 lambd))
264 (dz2 (* dz (/ 2 (- 1 (sqrt (- 1
265 (let ((sinphi (/ na ri)))
266 (* sinphi sinphi))))))))
267 (dx (* dz (/ ri na)))
268 (dx2 (* dx .5))
269 (dx3 (if pixel-size-x
270 (progn
271 #+nil (unless (< pixel-size-x dx2)
272 (error "pixel-size-x is ~a but should be smaller than ~a"
273 pixel-size-x dx2))
274 pixel-size-x)
275 dx2))
276 (dz3 (if pixel-size-z
277 (progn
278 #+nil(unless (< pixel-size-z dz2)
279 (error "pixel-size-z is ~a but should be smaller than ~a"
280 pixel-size-z dz2))
281 pixel-size-z)
282 dz2)))
284 ;; electric field caps are only calculated upon first call FIXME: or
285 ;; when parameters change (implemented via reinitialize argument)
286 (when (or (null k0) (null k1) (null k2) initialize)
287 (multiple-value-bind (e0 e1 e2)
288 (psf:electric-field-psf z y x (* dz3 z) (* dx3 x)
289 :numerical-aperture na
290 :immersion-index ri
291 :wavelength wavelength
292 :integrand-evaluations integrand-evaluations)
293 (when debug
294 (write-pgm "/home/martin/tmp/cut-0psf.pgm"
295 (normalize-2-csf/ub8-abs (cross-section-xz e0))))
296 (setf k0 (fftshift (ft e0))
297 k1 (fftshift (ft e1))
298 k2 (fftshift (ft e2)))
299 (when debug (write-pgm "/home/martin/tmp/cut-1psf-k.pgm"
300 (normalize-2-csf/ub8-abs (cross-section-xz k0))))))
301 (let* ((cr window-radius)
302 (cx window-x)
303 (cy window-y)
304 (sx (/ dx2 dx3))
305 (cr2 (* cr cr))
306 (window (make-array (list y x) :element-type 'my-float))
307 (kk0 (make-array (array-dimensions k0)
308 :element-type '(complex my-float)))
309 (kk1 (make-array (array-dimensions k1)
310 :element-type '(complex my-float)))
311 (kk2 (make-array (array-dimensions k2)
312 :element-type '(complex my-float))))
313 ;; 2d window
314 (do-region ((j i) (y x))
315 (let* ((xx (- (* sx (* 4 (- (* i (/ +one+ x)) .5))) cx))
316 (yy (- (* sx (* 4 (- (* j (/ +one+ y)) .5))) cy))
317 (r2 (+ (* xx xx) (* yy yy))))
318 (when (< r2 cr2)
319 (setf (aref window j i) +one+))))
320 (do-region ((k j i) (z y x))
321 (setf (aref kk0 k j i) (* (aref k0 k j i) (aref window j i))
322 (aref kk1 k j i) (* (aref k1 k j i) (aref window j i))
323 (aref kk2 k j i) (* (aref k2 k j i) (aref window j i))))
324 (when debug
325 (write-pgm "/home/martin/tmp/cut-2psf-k-mul.pgm"
326 (normalize-2-csf/ub8-abs (cross-section-xz kk0))))
327 (let* ((e0 (ift (fftshift kk0)))
328 (e1 (ift (fftshift kk1)))
329 (e2 (ift (fftshift kk2)))
330 (intens k0)) ;; instead of allocating a new array
331 ;; we store into k0
332 (do-region ((k j i) (z y x))
333 (setf (aref intens k j i)
334 (+ (* (aref e0 k j i) (conjugate (aref e0 k j i)))
335 (* (aref e1 k j i) (conjugate (aref e1 k j i)))
336 (* (aref e2 k j i) (conjugate (aref e2 k j i))))))
337 (when debug
338 (write-pgm
339 "/home/martin/tmp/cut-3psf-intens.pgm"
340 (normalize-2-csf/ub8-realpart (cross-section-xz intens)))
341 (let ((k (fftshift (ft intens))))
342 (write-pgm "/home/martin/tmp/cut-4psf-intk.pgm"
343 (normalize-2-csf/ub8-abs (cross-section-xz k)))))
344 intens)))))
346 #+nil
347 (write-pgm "/home/martin/tmp/interpolation-test.pgm"
348 (normalize-2-sf/ub8 (.- (resample-2-sf (draw-disk-sf 25.0 75 75) 1s0 1s0 .25 .25)
349 (draw-disk-sf 100.0 300 300))))
350 #+nil
351 (time (progn
352 (angular-psf :x 128 :z 128 :integrand-evaluations 280 :debug t :initialize t)
353 nil))
355 (defmacro debug-out (&rest rest)
356 `(format t "~a~%"
357 (list ,@(mapcar #'(lambda (x) `(list ,(format nil "~a" x) ,x))
358 rest))))
361 #|| sketch of the cap kz(kx,ky) of the k-vectors that enter the back
362 focal plane. the small circle with the x in the center is a
363 transparent window. the distance from the center of the window to the
364 center of the cap is rho. we are interested in the cap height at
365 points A and B along the vector rho. the point A will be the highest
366 point of the cap and B the lowest. when the window encloses the center
367 of the cap the highest point is k. when the window touches the
368 periphery of the bfp, the lowest point is kz(R), where R=NA*k0 is the
369 diameter of the bfp.
371 -----+-----
372 from top: ---/ | \---
373 -/ | \-
374 -/ | \- / kx
375 / | \/
376 / | ----- / \
377 / | / \ \
378 / | / \ \
379 | | | x | |
380 / | | | \
381 | | |\ /| |
382 | o | ----- | |
383 | | | | |
384 \ | | | |
385 | | | | ||
386 \ | | | /|
387 \ | | | / |
388 \ | | | / |
389 \ | | | / |
390 -\ | | |/- |
391 -\ | | /+ |
392 ---\ | | /--- | |
393 -----+--+-- | |
394 ------+--+--- | |
395 from side ----/ kz | \---v |
396 --/ | \-- |
397 -/ | \- |
398 -/ | \/
399 -/ | -/ \-
400 / | / \ kz(kx)
401 / | -/ \
402 / | / \
403 / | -/ \
404 / | alph -/ \
405 / | / \
406 | | -/ |
407 / | / \
408 | | -/ |
409 | |/ |
410 |-----------------------+--o-------o------o--------+ kx
411 | | A B R |
412 | | k=k0*n
413 \ | |-------| /
414 | | 2 rr |
417 (defun angular-intensity-psf-minimal-resolution
418 (&key
419 (x-um #.(coerce 50 'my-float)) (y-um x-um) (z-um #.(coerce 40 'my-float))
420 (window-radius #.(coerce .1 'my-float))
421 (window-x #.(coerce .5 'my-float))
422 (window-y #.(coerce 0 'my-float))
423 (wavelength #.(coerce .480 'my-float))
424 (numerical-aperture #.(coerce 1.38 'my-float))
425 (immersion-index #.(coerce 1.515 'my-float))
426 (integrand-evaluations 30)
427 (debug nil)
428 (initialize t)
429 (store-cap nil)
430 (big-window nil))
431 "Calculate angular intensity PSF, ensure that the maximum sampling
432 distance is chosen. Set INITIALIZE to nil if the e-field can be reused
433 from a previous calculation. In that case you may need to set
434 STORE-CAP to true for all evaluations and use a lot more memory. Only
435 if the window diameter is going to be bigger than the radius of the
436 back focal plane set BIG-WINDOW to true."
437 (declare ((my-float 0 1) window-radius)
438 ((my-float -1 1) window-x window-y)
439 (my-float wavelength numerical-aperture immersion-index
440 x-um y-um z-um)
441 (fixnum integrand-evaluations)
442 (boolean debug initialize store-cap big-window)
443 (values (simple-array (complex my-float) 3)
444 my-float my-float &optional))
445 (let* ((k0 (/ #.(coerce (* 2 pi) 'my-float) wavelength))
446 (k (* immersion-index k0))
447 (R (* numerical-aperture k0))
448 (rr (* R window-radius)))
449 (labels ((kz (kx)
450 (sqrt (- (* k k) (* kx kx)))))
451 (let* ((rho (sqrt (+ (* window-x window-x)
452 (* window-y window-y))))
453 (kza (kz (- rho rr)))
454 (kzb (kz (+ rho rr)))
455 (top (if (< rho rr)
456 (max k kza kzb)
457 (max kza kzb)))
458 (bot (if (< rho (- R r)) ;; window is in center of bfp
459 (min (kz R) kza kzb)
460 (min kza kzb)))
461 (kzextent (if store-cap
462 ;; store the full cap without wrapping,
463 ;; this way one can reuse the efields
464 (- k (kz R))
465 ;; just leave enough space to accommodate
466 ;; the final donut, this improves
467 ;; performance a lot for small windows
468 (* 16 (- top bot))))
469 (kxextent (if big-window
470 ;; for window diameter bigger than
471 ;; bfp-diam/2 transversally the bfp has to
472 ;; fit in twice, to accommodate the full
473 ;; donut
474 (* +one+ 2 R)
475 (* +one+ R)))
476 (pif #.(coerce pi 'my-float))
477 (dx (/ pif kxextent))
478 (dz (/ pif kzextent)))
479 (debug-out dx dz kxextent R rr kzextent k rho)
480 (values
481 (angular-psf :x (floor x-um dx) :y (floor y-um dx) :z (floor z-um dz)
482 :window-radius window-radius
483 :window-x window-x :window-y window-y
484 :wavelength wavelength
485 :numerical-aperture numerical-aperture
486 :immersion-index immersion-index
487 :integrand-evaluations integrand-evaluations
488 :pixel-size-x dx
489 :pixel-size-z dz
490 :debug debug
491 :initialize initialize)
492 dx dz)))))
494 #+nil
495 (time
496 (multiple-value-bind (a dx dz)
497 (angular-intensity-psf-minimal-resolution
498 :x-um (* +one+ 20)
499 :z-um (* +one+ 40)
500 :window-radius (* +one+ .14)
501 :window-x (* +one+ .73)
502 :window-y (* +one+ 0)
503 :integrand-evaluations 180
504 :initialize t
505 :debug t)
506 (write-pgm "/home/martin/tmp/cut-5resampled.pgm"
507 (normalize-2-csf/ub8-realpart
508 (cross-section-xz
509 (resample-3-csf a dx dx dz .2 .2 .2))))
510 (sb-ext:gc :full t)))
513 (defmacro defstuff ()
514 `(progn
515 ,@(loop for i in '(*dims* ;; dimensions of the input stack in
516 ;; pixels and slices
517 *centers* ;; integral center coordinates of
518 ;; the nuclei (0 .. dim-x) ...
519 *index-spheres* ;; each nuclei is drawn with its index
520 *spheres-c-r* ;; scaled (isotropic axis, in
521 ;; micrometeres) and shifted (so
522 ;; that origin in center of
523 ;; volume) coordinates
525 collect
526 `(defparameter ,i nil))))
528 (defstuff)
530 (defun create-sphere-array (dims centers)
531 (declare (cons dims)
532 ((simple-array vec-i 1) centers)
533 (values (simple-array sphere 1) &optional))
534 (destructuring-bind (z y x)
535 dims
536 (declare (fixnum z y x))
537 (let* ((dx (* +one+ .2e-3))
538 (dz (* +one+ 1e-3))
539 (xh (* +one+ .5d0 x))
540 (yh (* +one+ .5d0 y))
541 (zh (* +one+ .5d0 z))
542 (n (length centers))
543 (result-l nil))
544 (labels ((convert-point (point)
545 (declare (vec-i point)
546 (values vec &optional))
547 (v (* dx (- (vec-i-x point) xh))
548 (* dx (- (vec-i-y point) yh))
549 (* dz (- (vec-i-z point) zh)))))
550 (dotimes (i n)
551 (push (make-sphere :center (convert-point (aref centers i))
552 :radius (* dx 1d0))
553 result-l))
554 (make-array (length result-l) :element-type 'sphere
555 :initial-contents (nreverse result-l))))))
557 (defun init-angular-model ()
558 ;; find the centers of the nuclei and store into *centers*
559 #+nil(multiple-value-bind (c ch dims)
560 (find-centers)
561 (declare (ignore ch))
562 (defparameter *centers* c)
563 (defparameter *dims* dims)
564 (sb-ext:gc :full t))
565 (progn
566 (defparameter *dims* '(34 206 296))
567 (let* ((result nil)
568 (nx 10)
569 (ny 7)
570 (z 20)
571 (dx 30))
572 (loop for i below nx do
573 (loop for j below ny do
574 (let ((x (+ (floor dx 2) (* dx i)))
575 (y (+ (floor dx 2) (* dx j))))
576 (unless (and (= i 4) (= j 3))
577 (push (make-vec-i :x x :y y :z z)
578 result)))))
579 ;; the first sphere is the one i want to illuminate its center
580 ;; is in plane 10 (why is z inverted. in spheres the single
581 ;; nucleus is in plane 25)
582 (push (make-vec-i :x 130 :y 100 :z 10) result)
583 (defparameter *centers* (make-array (length result)
584 :element-type 'vec-i
585 :initial-contents result))))
587 (defparameter *spheres-c-r*
588 (create-sphere-array *dims* *centers*))
589 (let ((spheres
590 (destructuring-bind (z y x)
591 *dims*
592 (draw-indexed-ovals 12.0 *centers* z y x))))
593 (setf *index-spheres* spheres)
594 (write-pgm "/home/martin/tmp/angular-indexed-spheres-cut.pgm"
595 (normalize-2-csf/ub8-realpart
596 (cross-section-xz *index-spheres*
597 (vec-i-y (elt *centers* 0)))))
598 (sb-ext:gc :full t))
599 (let ((spheres
600 (destructuring-bind (z y x)
601 *dims*
602 (draw-ovals 12.0 *centers* z y x))))
603 (setf *spheres* spheres)
604 (save-stack-ub8 "/home/martin/tmp/angular-spheres/"
605 (normalize-3-csf/ub8-realpart *spheres*))
606 (write-pgm "/home/martin/tmp/angular-spheres-cut.pgm"
607 (normalize-2-csf/ub8-realpart
608 (resample-2-csf
609 (cross-section-xz *spheres*
610 (vec-i-y (elt *centers* 0)))
611 .2 1.0 .2 .2)))
612 (sb-ext:gc :full t)))
614 #+nil
615 (time (init-angular-model))
617 #+bla
618 (defun init-angular-psf ()
619 ;; calculate angular intensity psf, make extend in z big enough to
620 ;; span the full fluorophore concentration even when looking at the
621 ;; bottom plane of it
623 ;; the size of the k space must be big enough: 2*bfp-diameter for
624 ;; k_{x,y}, and 2*cap-height for k_z. then the full donut can be
625 ;; accomodated.
627 ;; if only a small part of the cap is selected the dimensions can be
628 ;; reduced accordingly. calculating the size requires to find the
629 ;; intersection of a cylinder and the sphere cap.
630 (let* ((dx .2)
631 (dz 1.0)
632 (psf (destructuring-bind (z y x)
633 *dims*
634 (declare (ignore y x))
635 (let ((r 100))
636 (angular-psf
637 :window-radius .4
638 :window-x .6
639 :z (* 8 z) :x (* 2 r) :y (* 2 r)
640 :pixel-size-z (* .25 dz) :pixel-size-x (* .5 dx)
641 :integrand-evaluations 400
642 :debug t
643 :initialize t)))))
644 (defparameter *psf* psf)
645 (write-pgm "/home/martin/tmp/psf.pgm"
646 (normalize-2-csf/ub8-realpart (cross-section-xz psf)))
647 (sb-ext:gc :full t)))
649 #+nil
650 (time (init-angular-psf)) ;; 62.2 s
652 #+nil ;; store indexed-spheres for inspection
653 (save-stack-ub8 "/home/martin/tmp/angular-indexed-spheres/"
654 (normalize-3-csf/ub8-realpart *index-spheres*))
657 (defun get-visible-nuclei (k)
658 "Find all the nuclei in slice K."
659 (declare (fixnum k)
660 (values list &optional))
661 (destructuring-bind (z y x)
662 (array-dimensions *index-spheres*)
663 (unless (< k z)
664 (error "slice k isn't contained in array *index-spheres*."))
665 ;; use bit-vector to store which nuclei are contained
666 (let* ((n (length *centers*))
667 (result (make-array n
668 :element-type 'boolean
669 :initial-element nil)))
670 (do-region ((j i) (y x))
671 (let ((v (round (realpart (aref *index-spheres* k j i)))))
672 (when (< 0 v n)
673 (setf (aref result v) t))))
674 (loop for i from 1 below n
675 when (aref result i)
676 collect
677 (1- i)))))
678 #+nil
679 (get-visible-nuclei 25)
681 #+nil
682 (time
683 (loop for i below (array-dimension *index-spheres* 0)
684 collect
685 (list i (get-visible-nuclei i))))
688 ;; create a volume containing just the current slice
689 (defun get-lcos-volume (k nucleus)
690 (declare (fixnum k)
691 (values (simple-array (complex my-float) 3) &optional))
692 (destructuring-bind (z y x)
693 (array-dimensions *index-spheres*)
694 (unless (< 0 k z)
695 (error "slice index k out of range."))
696 (let ((vol (make-array (list z y x)
697 :element-type '(complex my-float))))
698 ;; only the current nucleus will be illuminated
699 ;; note that nucleus 0 has value 1 in index-spheres
700 (do-region ((j i) (y x))
701 (if (< (abs (- nucleus (1- (aref *index-spheres* k j i)))) .5)
702 (setf (aref vol k j i) (aref *spheres* k j i))))
703 vol)))
705 (defun write-section (fn vol &optional (y (floor (array-dimension vol 1) 2)))
706 (declare (simple-string fn)
707 ((simple-array (complex my-float) 3) vol)
708 (values null &optional))
709 (write-pgm fn (normalize-2-csf/ub8-realpart (cross-section-xz vol y))))
712 #+nil
713 (let* ((k 25)
714 (nuc (first (get-visible-nuclei k)))
715 (vol (get-lcos-volume k nuc)))
716 (format t "~a~%" `(nuc ,nuc))
717 (write-section "/home/martin/tmp/angular-0lcos-cut.pgm" vol)
718 (save-stack-ub8 "/home/martin/tmp/angular-0lcos" (normalize-3-csf/ub8-realpart vol)))
721 (defvar *nucleus-index* 0)
722 (defvar *bfp-window-radius* .08d0)
724 ;; the following global variable contain state for merit-function:
725 ;; *bfp-window-radius* *nucleus-index* *spheres-c-r*
726 (defun merit-function (vec2)
727 (declare ((simple-array double-float (2)) vec2)
728 (values double-float &optional))
729 (let* ((border-value 0d0) ;; value to return when outside of bfp
730 ;; this has to be considerably bigger than the maxima on the bfp
731 (border-width *bfp-window-radius*) ;; in this range to the
732 ;; border of the bfp
733 ;; enforce bad merit
734 ;; function
735 (sum 0d0)
736 (radius (norm2 vec2)))
737 (if (< radius (- .99d0 border-width))
738 ;; inside
739 (loop for dirs in '((:right :left)
740 (:top :bottom)) do
741 (loop for dir in dirs do
742 (loop for bfp-dir in dirs do
743 (incf sum
744 (illuminate-ray *spheres-c-r* *nucleus-index* dir
745 (vec2-x vec2) (vec2-y vec2)
746 *bfp-window-radius*
747 bfp-dir)))))
748 ;; in the border-width or outside of bfp
749 (incf sum border-value))
750 sum))
752 (defun find-optimal-bfp-window-center (nucleus)
753 (declare (fixnum nucleus)
754 (values vec2 &optional))
755 (let ((*nucleus-index* nucleus))
756 (loop
757 (multiple-value-bind (min point)
758 (simplex-anneal:anneal (simplex-anneal:make-simplex
759 (make-vec2 :x -1d0 :y -1d0) 1d0)
760 #'merit-function
761 ;; set temperature bigger than the
762 ;; maxima in the bfp but smaller
763 ;; than border-value
764 :start-temperature 2.4d0
765 :eps/m .02d0
766 :itmax 1000
767 :ftol 1d-3)
768 (when (< min 100d0)
769 (return-from find-optimal-bfp-window-center point))))))
771 #+nil
772 (find-optimal-bfp-window-center 0)
773 ;; FIXME: are these coordinates in mm or relative positions for a bfp-radius of 1?
774 ;; I think the latter, but I'm not sure.
775 #+nil
776 (time
777 (let*((dx .2d0)
778 (dz 1d0)
779 (r 100)
780 (z (array-dimension *spheres* 0))
781 (psf (angular-psf :x r :y r :z (* 2 z)
782 :pixel-size-x dx :pixel-size-z dz
783 :window-radius *bfp-window-radius*
784 :window-x -.714d0
785 :window-y .16d0
786 :initialize t
787 :debug t
788 :integrand-evaluations 400)))
789 (save-stack-ub8 "/home/martin/tmp/psf" (normalize3-cdf/ub8-realpart psf))
790 nil))
793 ;; calculate the excitation one nucleus
794 #+bla
795 (defun calc-light-field (k nucleus)
796 (declare (fixnum k nucleus))
797 (let* ((result nil)
798 (lcos (get-lcos-volume k nucleus))
799 (bfp-pos (find-optimal-bfp-window-center nucleus))
800 (psf (let ((dx .2d0)
801 (dz 1d0)
802 (r 100)
803 (z (array-dimension *spheres* 0)))
804 (multiple-value-bind (h ddx ddz)
805 (angular-intensity-psf-minimal-resolution
806 :x-um (* r dx) :y-um (* r dx) :z-um (* 2 z dz)
807 :window-radius *bfp-window-radius*
808 :window-x (vec2-x bfp-pos)
809 :window-y (vec2-y bfp-pos)
810 :initialize t
811 :debug t
812 :integrand-evaluations 1000)
813 (resample-3-cdf h ddx ddx ddz dx dx dz)))))
814 (format t "~a~%" `(bfp-pos ,bfp-pos))
815 (write-section (format nil "/home/martin/tmp/angular-1expsf-cut-~3,'0d.pgm" nucleus) psf)
817 (multiple-value-bind (conv conv-start)
818 (convolve-nocrop lcos psf)
819 ;; light distribution in sample
820 (defparameter *angular-light-field* conv)
821 (defparameter *angular-light-field-start* conv-start)
822 (write-section (format nil "/home/martin/tmp/angular-2light-cut-~3,'0d.pgm" nucleus)
823 conv
824 (vec-i-y (aref *centers* nucleus)))
825 (save-stack-ub8 (format nil "/home/martin/tmp/angular-2light-~3,'0d/" nucleus)
826 (normalize-3-cdf/ub8-realpart conv))
827 ;; multiply fluorophore concentration with light distribution
828 (let ((excite (.* conv *spheres* conv-start)))
829 (defparameter *excite* excite)
830 (write-section (format nil "/home/martin/tmp/angular-3excite-cut-~3,'0d.pgm" nucleus)
831 excite
832 (vec-i-y (aref *centers* nucleus)))
833 (save-stack-ub8 (format nil "/home/martin/tmp/angular-3excite-~3,'0d/" nucleus)
834 (normalize-3-cdf/ub8-realpart excite))
835 (destructuring-bind (z y x)
836 (array-dimensions excite)
837 (declare (ignorable z))
838 (let* ((in-focus (extract-bbox-3-cdf excite
839 (make-bbox :start (v 0d0 0d0 (* 1d0 k))
840 :end (v (* 1d0 (1- x))
841 (* 1d0 (1- y))
842 (* 1d0 k))))))
843 (save-stack-ub8 "/home/martin/tmp/angular-4in-focus/"
844 (normalize-3-cdf/ub8-realpart in-focus))
845 (let*((mplane (mean (convert-3-cdf/df-realpart in-focus)))
846 (mvol (mean (convert-3-cdf/df-realpart excite)))
847 (gamma (/ mplane mvol)))
848 (push (list mplane mvol gamma) result)
849 (debug-out mplane mvol gamma)
850 (format t "plane-result ~f ~f ~f~%" mplane mvol gamma))
851 result))))))
853 #+nil
854 (time
855 (progn
856 (with-open-file (*standard-output* "/home/martin/tmp/angular-stack.log"
857 :direction :output
858 :if-exists :supersede
859 :if-does-not-exist :create)
860 (let ((result nil))
861 (dotimes (k (array-dimension *spheres* 0))
862 (let* ((nucs (get-visible-nuclei k)))
863 (loop for nuc in nucs do
864 (format t "~a~%" (list 'doing k nucs))
865 (push (list k nuc (calc-light-field k nuc)) result))))
866 (defparameter *scan-result* result)))
867 (with-open-file (s "/home/martin/tmp/angular-stack-struc.lisp"
868 :direction :output
869 :if-exists :supersede
870 :if-does-not-exist :create)
871 (write *scan-result* :stream s))))
873 #+nil ;; overlay lightfield and spheres
874 (time
875 (save-stack-ub8 "/home/martin/tmp/sphere-and-excite/"
876 (normalize3-cdf/ub8-realpart
877 (labels ((con (vol)
878 (convert3-ub8/cdf-complex (normalize3-cdf/ub8-realpart vol))))
879 (.+ (con *angular-light-field*)
880 (s* .7d0 (con *spheres*))
881 *angular-light-field-start*)))))
883 #+nil
884 (dotimes (i (length *centers*))
885 (format t "~a~%"
886 (raytrace::ray-spheres-intersection (v) (v 0d0 0d0 -1d0) *sphere-c-r* i)))
888 #+nil
889 (progn
890 (defun merit-function (vec)
891 (declare (vec vec)
892 (values my-float &optional))
893 (raytrace:ray-spheres-intersection
894 (v 0d0 0d0 0d0)
895 (normalize (direction (aref vec 0) (aref vec 1)))
896 *sphere-c-r*))
897 (let ((start (make-array 2 :element-type 'my-float
898 :initial-contents (list 100d0 100d0))))
899 (with-open-file (*standard-output* "/dev/shm/anneal.log"
900 :direction :output
901 :if-exists :supersede)
902 (anneal (make-simplex start 1d0)
903 #'merit-function
904 :start-temperature 3d0))))
907 ;; The merit function should get two parameters r and phi. if r isn't
908 ;; inside the back focal plane radius (minus the diameter of the
909 ;; aperture window) some high value is returned. Several rays should
910 ;; be sent through the spheres starting from different positions on
911 ;; the aperture window and targetting different positions in the
912 ;; circle that should be illuminated in the sample.
914 ;; Maybe later I can add the aperture size in the back focal plane as
915 ;; another parameter. The bigger the aperture, the better for the
916 ;; optimization.
918 ;; Possibly I shouldn't call it merit function as I try to minimize
919 ;; its result.
921 (defun get-ray-behind-objective (x-mm y-mm bfp-ratio-x bfp-ratio-y)
922 "Take a point on the back focal plane and a point in the sample and
923 calculate the ray direction ro that leaves the objective. So its the
924 same calculation that is used for draw-ray-into-vol."
925 (declare (double-float x-mm y-mm bfp-ratio-x bfp-ratio-y)
926 (values vec vec &optional))
927 (let* ((f (lens:focal-length-from-magnification 63d0))
928 (na 1.38d0)
929 (ri 1.515d0)
930 (bfp-radius (lens:back-focal-plane-radius f na))
931 (obj (lens:make-thin-objective :normal (v 0d0 0d0 -1d0)
932 :center (v)
933 :focal-length f
934 :radius bfp-radius
935 :numerical-aperture na
936 :immersion-index ri))
937 (theta (lens:find-inverse-ray-angle x-mm y-mm obj))
938 (phi (atan y-mm x-mm))
939 (start (v (* bfp-ratio-x bfp-radius)
940 (* bfp-ratio-y bfp-radius)
941 f)))
942 (multiple-value-bind (ro s)
943 (lens:thin-objective-ray obj
944 start
945 (v* (v (* (cos phi) (sin theta))
946 (* (sin phi) (sin theta))
947 (cos theta))
948 -1d0))
949 (values ro s))))
951 #+nil
952 (get-ray-behind-objective .1d0 .1d0 0d0 0d0)
954 ;; In *spheres-c-r* I stored the coordinates of all the nuclei
955 ;; relative to the center of the initial stack of images. It also
956 ;; contains the radius of each nuclieus. Now I consider how to
957 ;; illuminate selected circles inside of the sample. The nucleus which
958 ;; is beeing illuminated will be centered on the focal plane. The
959 ;; length of the vector ro coming out of the objective is
960 ;; nf=1.515*2.6mm~3000um and therefore a lot bigger than the z extent
961 ;; of the stack (~40 um). It is not necessary to z-shift the nuclei
962 ;; before intersecting them with the rays. So I will just use the
963 ;; nucleus' x and y coordinates as arguments to
964 ;; get-ray-behind-objective. I also supply the position in the back
965 ;; focal plane from where the ray originates.
967 (deftype direction ()
968 `(member :left :right :top :bottom))
970 (defun sample-circle (center radius direction)
971 "Given a circle CENTER and RADIUS return the point in the left,
972 right, top or bottom of its periphery. CENTER and result are complex
973 numbers x+i y."
974 (declare ((complex double-float) center)
975 (double-float radius)
976 (direction direction)
977 (values (complex double-float) &optional))
978 (let ((phi (ecase direction
979 (:right 0d0)
980 (:top (* .5d0 pi))
981 (:left pi)
982 (:bottom (* 1.5d0 pi)))))
983 (+ center (* radius (exp (complex 0d0 phi))))))
985 #+nil
986 (sample-circle (complex 1d0 1d0) 1d0 :right)
988 #+nil ;; prepare volume for drawing lines
989 (progn
990 (defparameter *spheres-ub8* (normalize-3-csf/ub8-realpart
991 (resample-3-csf *spheres* .2 .2 1.0 .2 .2 .2)))
993 (save-stack-ub8 "/home/martin/tmp/spheres-ub8" *spheres-ub8*)
995 #+buk
996 (with-slots (center radius)
997 (aref *spheres-c-r* 0)
998 (let* ((sample-pos (complex 0d0)#+nil(sample-circle (complex (vec-x center) (vec-y center))
999 radius
1000 :left))
1001 (bfp-pos (complex 0d0)#+nil(sample-circle (complex 0d0 0)
1002 .1d0
1003 :left)))
1004 (draw-ray-into-vol (realpart sample-pos)
1005 (imagpart sample-pos)
1006 (realpart bfp-pos)
1007 (imagpart bfp-pos)
1008 *spheres-ub8*)))
1009 #+nil
1010 (init-angular-model)
1011 #+nil
1012 (destructuring-bind (z y x)
1013 *dims*
1014 (let* ((x-mm .04d0)
1015 (y-mm 0d0)
1016 (bfp-ratio-x 0d0)
1017 (bfp-ratio-y 0d0)
1018 (f (lens:focal-length-from-magnification 63d0))
1019 (na 1.38d0)
1020 (ri 1.515d0)
1021 (bfp-radius (lens:back-focal-plane-radius f na))
1022 (obj (lens:make-thin-objective :normal (v 0d0 0d0 -1d0)
1023 :center (v)
1024 :focal-length f
1025 :radius bfp-radius
1026 :numerical-aperture na
1027 :immersion-index ri))
1028 (theta (lens:find-inverse-ray-angle x-mm y-mm obj))
1029 (phi (atan y-mm x-mm))
1030 (start (v (* bfp-radius bfp-ratio-x)
1031 (* bfp-radius bfp-ratio-y)
1033 (dx .2d-3)
1034 (dz 1d-3)
1035 (cz (* .5d0 z)) ;; position that is in the center of front focal plane
1036 (cy (* .5d0 y))
1037 (cx (* .5d0 x))
1038 (nf (* ri f))
1039 (shift-z 25))
1040 (debug-out f bfp-radius theta phi)
1041 (macrolet ((plane (direction position)
1042 ;; for defining a plane that is perpendicular to an
1043 ;; axis and crosses it at POSITION
1044 (declare (type (member :x :y :z) direction))
1045 (let* ((normal (ecase direction
1046 (:x (v 1d0))
1047 (:y (v 0d0 1d0))
1048 (:z (v 0d0 0d0 1d0)))))
1049 `(let* ((pos ,position)
1050 (center (v* ,normal pos))
1051 (outer-normal (normalize center)))
1052 (declare (type double-float pos))
1053 (lens::make-disk :normal outer-normal :center center)))))
1054 (let ((p+z (plane :z (- (* dz (- z cz))
1055 nf)))
1056 (p-z (plane :z (- (* dz (- (- z cz)))
1057 nf)))
1058 (p+y (plane :y (* dx (- y cy))))
1059 (p-y (plane :y (* dx (- (- y cy)))))
1060 (p+x (plane :x (* dx (- x cx))))
1061 (p-x (plane :x (* dx (- (- x cx))))))
1062 (multiple-value-bind (ro s)
1063 (lens:thin-objective-ray obj
1064 start
1065 (v* (v (* (cos phi) (sin theta))
1066 (* (sin phi) (sin theta))
1067 (cos theta))
1068 -1d0))
1069 (format t "~a~%" (list 'dir (v* (v (* (cos phi) (sin theta))
1070 (* (sin phi) (sin theta))
1071 (cos theta))
1072 -1d0)
1073 's s
1074 's-new (v+ s (v 0d0 0d0 (* dz shift-z)))))
1075 (setf s (v+ s (v 0d0 0d0 (* dz shift-z))))
1076 (let* ((nro (normalize ro)))
1077 (debug-out nro)
1078 (macrolet ((hit (plane)
1079 ;; find intersection between plane and the ray
1080 `(multiple-value-bind (dir hit-point)
1081 (lens::plane-ray ,plane
1082 ;; shift start of vector a bit
1084 nro)
1085 (declare (ignore dir))
1086 hit-point))
1087 (pixel (hit-expr)
1088 ;; convert coordinates from mm into integer pixel positions
1089 `(let ((h ,hit-expr))
1090 (declare (type (or null vec) h))
1091 (when h
1092 (make-vec-i
1093 :z (floor (+ cz (/ (+ (aref h 2) nf) dz)))
1094 :y (floor (+ cy (/ (aref h 1) dx)))
1095 :x (floor (+ cx (/ (aref h 0) dx))))))))
1096 (let* ((h+z (pixel (hit p+z)))
1097 (h-z (pixel (hit p-z)))
1098 (h+y (pixel (hit p+y)))
1099 (h-y (pixel (hit p-y)))
1100 (h+x (pixel (hit p+x)))
1101 (h-x (pixel (hit p-x)))
1102 ;; make a list of all the points
1103 (hlist (list h+z h-z h+y h-y h+x h-x))
1104 ;; throw away points that are nil or that contain
1105 ;; coordinates outside of the array dimensions
1106 (filtered-hlist
1107 (remove-if-not #'(lambda (v)
1108 (if v
1109 (and (< -1 (vec-i-x v) x)
1110 (< -1 (vec-i-y v) y)
1111 (< -1 (vec-i-z v) z))
1112 nil)) hlist))
1113 ;; sort best points by x
1114 (choice (sort filtered-hlist #'< :key (lambda (v) (vec-i-x v)))))
1115 (debug-out h+z h-z)
1116 (format t "~a~%" (list 'choice choice))
1117 #+nil (scan-convert-line3
1118 (first choice)
1119 (second choice)
1120 *spheres-ub8*)))))))))
1122 #+nil
1123 (destructuring-bind (z y x)
1124 (array-dimensions vol)
1125 (let* ((f (lens:focal-length-from-magnification 63d0))
1126 (na 1.38d0)
1127 (ri 1.515d0)
1128 (bfp-radius (lens:back-focal-plane-radius f na))
1129 (obj (lens:make-thin-objective :normal (v 0d0 0d0 -1d0)
1130 :center (v)
1131 :focal-length f
1132 :radius bfp-radius
1133 :numerical-aperture na
1134 :immersion-index ri))
1135 (theta (lens:find-inverse-ray-angle x-mm y-mm obj))
1136 (phi (atan y-mm x-mm))
1137 (start (v (* bfp-ratio-x bfp-radius)
1138 (* bfp-ratio-y bfp-radius)
1140 (dx dx-mm)
1141 (dz dz-mm)
1142 (cz (* .5d0 z)) ;; position that is in the center of front focal plane
1143 (cy (* .5d0 y))
1144 (cx (* .5d0 x))
1145 (nf (* ri f)))
1146 (macrolet ((plane (direction position)
1147 ;; for defining a plane that is perpendicular to an
1148 ;; axis and crosses it at POSITION
1149 (declare (type (member :x :y :z) direction))
1150 (let* ((normal (ecase direction
1151 (:x (v 1d0))
1152 (:y (v 0d0 1d0))
1153 (:z (v 0d0 0d0 1d0)))))
1154 `(let* ((pos ,position)
1155 (center (v* ,normal pos))
1156 (outer-normal (normalize center)))
1157 (declare (type double-float pos))
1158 (lens::make-disk :normal outer-normal :center center)))))
1159 ;; define the borders of the viewing volume, distances in mm
1160 (let ((p+z (plane :z (- (* dz (- z cz))
1161 nf)))
1162 (p-z (plane :z (- (* dz (- (- z cz)))
1163 nf)))
1164 (p+y (plane :y (* dx (- y cy))))
1165 (p-y (plane :y (* dx (- (- y cy)))))
1166 (p+x (plane :x (* dx (- x cx))))
1167 (p-x (plane :x (* dx (- (- x cx))))))
1168 (multiple-value-bind (ro s)
1169 (lens:thin-objective-ray obj
1170 start
1171 (v* (v (* (cos phi) (sin theta))
1172 (* (sin phi) (sin theta))
1173 (cos theta))
1174 -1d0))
1175 (setf s (v+ s (v 0d0 0d0 (* dz shift-z))))
1176 (let* ((nro (normalize ro)))
1177 (macrolet ((hit (plane)
1178 ;; (declare (type lens::disk plane))
1179 ;; find intersection between plane and the ray
1180 `(multiple-value-bind (dir hit-point)
1181 (lens::plane-ray ,plane
1182 ;; shift start of vector a bit
1184 nro)
1185 (declare (ignore dir))
1186 hit-point))
1187 (pixel (hit-expr)
1188 ;; convert coordinates from mm into integer pixel positions
1189 `(let ((h ,hit-expr))
1190 (declare (type (or null vec) h))
1191 (when h
1192 (make-vec-i
1193 :z (floor (+ cz (/ (+ (aref h 2) nf) dz)))
1194 :y (floor (+ cy (/ (aref h 1) dx)))
1195 :x (floor (+ cx (/ (aref h 0) dx))))))))
1196 (let* ((h+z (pixel (hit p+z)))
1197 (h-z (pixel (hit p-z)))
1198 (h+y (pixel (hit p+y)))
1199 (h-y (pixel (hit p-y)))
1200 (h+x (pixel (hit p+x)))
1201 (h-x (pixel (hit p-x)))
1202 ;; make a list of all the points
1203 (hlist (list h+z h-z h+y h-y h+x h-x))
1204 ;; throw away points that are nil or that contain
1205 ;; coordinates outside of the array dimensions
1206 (filtered-hlist
1207 (remove-if-not #'(lambda (v)
1208 (if v
1209 (and (< -1 (vec-i-x v) x)
1210 (< -1 (vec-i-y v) y)
1211 (< -1 (vec-i-z v) z))
1212 nil)) hlist))
1213 ;; sort best points by x
1214 (choice (sort filtered-hlist #'< :key (lambda (v) (vec-i-x v)))))
1215 (format t "~a~%" (list 'choice choice))
1216 (scan-convert-line3
1217 (first choice)
1218 (second choice)
1219 vol)))))))))
1221 (defun illuminate-ray (spheres-c-r illuminated-sphere-index
1222 sample-position
1223 bfp-center-x bfp-center-y
1224 bfp-radius bfp-position)
1225 "Trace a ray from a point in the back focal plane through the disk
1226 that encompasses the nucleus with index
1227 ILLUMINATED-SPHERE-INDEX. SAMPLE-POSITION and BFP-POSITION can assume
1228 one of the four values :LEFT, :RIGHT, :TOP and :BOTTOM indicating
1229 which point on the periphery of the correspondi2ng circle is meant
1230 Coordinates in the back focal plane are ratios, e.g. bfp-center-x=1 is
1231 on the border and a window with bfp-radius=1 passes all light through
1232 the bfp."
1233 (declare (fixnum illuminated-sphere-index)
1234 (direction sample-position bfp-position)
1235 (double-float bfp-center-x bfp-center-y bfp-radius)
1236 ((simple-array sphere 1) spheres-c-r)
1237 (values double-float &optional))
1238 (with-slots (center radius)
1239 (aref spheres-c-r illuminated-sphere-index)
1240 (let* ((sample-pos (sample-circle (complex (vec-x center) (vec-y center))
1241 radius
1242 sample-position))
1243 (bfp-pos (sample-circle (complex bfp-center-x bfp-center-y)
1244 bfp-radius
1245 bfp-position)))
1246 (multiple-value-bind (ro s)
1247 (get-ray-behind-objective (realpart sample-pos)
1248 (imagpart sample-pos)
1249 (realpart bfp-pos)
1250 (imagpart bfp-pos))
1251 (let* ((exposure
1252 (ray-spheres-intersection
1253 ;; shift by nf so that sample is in origin
1254 (v+ s
1255 (v 0d0
1257 (* 1.515d0 (lens:focal-length-from-magnification 63d0))))
1258 (normalize ro)
1259 spheres-c-r
1260 illuminated-sphere-index)))
1261 exposure)))))
1263 #+nil
1264 (illuminate-ray *spheres-c-r* 20 :bottom
1265 .1d0 .0d0
1266 .01d0 :right)
1268 (defvar *bfp-window-radius* .0001d0)
1270 #+nil ;; store the scan for each nucleus in the bfp
1271 (time
1272 (let* ((n 100)
1273 (a (make-array (list n n) :element-type 'double-float))
1274 (nn (length *spheres-c-r*))
1275 (mosaicx (ceiling (sqrt nn)))
1276 (mosaic (make-array (list (* n mosaicx) (* n mosaicx))
1277 :element-type 'double-float)))
1278 (dotimes (*nucleus-index* 3 nn)
1279 (dotimes (i n)
1280 (dotimes (j n)
1281 (let ((x (- (* 2d0 (/ i n)) 1d0))
1282 (y (- (* 2d0 (/ j n)) 1d0)))
1283 (setf (aref a j i)
1284 (merit-function
1285 (make-vec2 :x x :y y))))))
1286 (do-region ((j i) (n n))
1287 (let ((x (mod *nucleus-index* mosaicx))
1288 (y (floor *nucleus-index* mosaicx)))
1289 (setf (aref mosaic (+ (* n y) j) (+ (* n x) i))
1290 (aref a j i)))))
1291 (write-pgm "/home/martin/tmp/scan-mosaic.pgm" (normalize-2-df/ub8 mosaic))))
1293 #+nil
1294 (let ((vol (make-array dims :element-type '(unsigned-byte 8))))
1295 (loop for i in '(4.0d-3 -.2d-3) do
1296 (draw-ray-into-vol i 0d0 .99d0 .0d0 vol)
1297 #+nil(draw-ray-into-vol i 0d0 -.99d0 .0d0 vol)
1298 (draw-ray-into-vol i 0d0 0d0 .99d0 vol)
1299 (draw-ray-into-vol i 0d0 0d0 -.99d0 vol))
1301 (save-stack-ub8 "/home/martin/tmp/line"
1302 vol))
1305 #+nil
1306 (time
1307 (let* ((n 100)
1308 (a (make-array (list n n) :element-type '(unsigned-byte 8)))
1309 (nn (length *spheres-c-r*))
1310 (mosaicx (ceiling (sqrt nn)))
1311 (mosaic (make-array (list (* n mosaicx) (* n mosaicx))
1312 :element-type '(unsigned-byte 8))))
1313 (with-open-file (*standard-output* "/dev/shm/a"
1314 :direction :output
1315 :if-exists :supersede)
1316 (dotimes (*nucleus-index* nn)
1317 (dotimes (i 10)
1318 (tagbody again
1319 (multiple-value-bind (min point)
1320 (simplex-anneal:anneal (simplex-anneal:make-simplex
1321 (make-vec2 :x -1d0 :y -1d0) 1d0)
1322 #'merit-function
1323 ;; set temperature bigger than the
1324 ;; maxima in the bfp but smaller
1325 ;; than border-value
1326 :start-temperature 2.4d0
1327 :eps/m .02d0
1328 :itmax 1000
1329 :ftol 1d-3)
1330 (unless (<= min 100d0)
1331 (go again))
1332 (let* ((x (aref point 0))
1333 (y (aref point 1))
1334 (ix (floor (* n (+ x 1)) 2))
1335 (iy (floor (* n (+ y 1)) 2))
1336 (mx (mod *nucleus-index* mosaicx))
1337 (my (floor *nucleus-index* mosaicx)))
1338 (incf (aref mosaic (+ (* n my) iy) (+ (* n mx) ix)))
1339 (format t "min ~a~%" (list min ix iy))))))))
1340 (write-pgm "/home/martin/tmp/scan-mosaic-max.pgm" mosaic)))