-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #184 from jonwright/master
adds an mlem notebook and 3.11 to CI (3.x is frequently broken)
- Loading branch information
Showing
2 changed files
with
282 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,281 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"id": "d9f646df-ac47-4e8f-a65f-53922d9acaf1", | ||
"metadata": {}, | ||
"source": [ | ||
"An \"MLEM\" algorithm from XRDUA was used in this paper:\n", | ||
"\n", | ||
"\"Impurity precipitation in atomized particles evidenced by nano x-ray diffraction computed tomography\"\n", | ||
"Anne Bonnin; Jonathan P. Wright; Rémi Tucoulou; Hervé Palancher\n", | ||
"Appl. Phys. Lett. 105, 084103 (2014)\n", | ||
"https://doi.org/10.1063/1.4894009\n", | ||
"\n", | ||
"This python code implements something similar based on a youtube video (https://www.youtube.com/watch?v=IhETD4nSJec)\n", | ||
"\n", | ||
"There are lots of papers from mathematicians in the literature about MART (multiplicative ART). The conversion of latex algebra back and forth into computer code seems to be a bit of a problem for me (Jon Wright - 17 Nov 2023)." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "cc820bc1-a9e9-4daa-b10f-fc23ef6a96d4", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"from ImageD11.sinograms.roi_iradon import iradon, radon # to have projection_shifts\n", | ||
"from skimage.transform import iradon_sart\n", | ||
"import numpy as np, pylab as pl\n", | ||
"import skimage.data\n", | ||
"\n", | ||
"def backproject( sino, theta, projection_shifts = None ):\n", | ||
" \"\"\" project the sinogram into the sample \"\"\"\n", | ||
" return iradon( sino, theta, filter_name=None, projection_shifts = projection_shifts )\n", | ||
"\n", | ||
"def forwardproject( sample, theta, projection_shifts = None ):\n", | ||
" \"\"\" project the sample into the experiment (sinogram) \"\"\"\n", | ||
" return radon( sample, theta, projection_shifts = projection_shifts )\n", | ||
"\n", | ||
"def mlem( sino, theta, \n", | ||
" startvalue = 1,\n", | ||
" projection_shifts = None,\n", | ||
" pad=0, niter=50, \n", | ||
" divtol=1e-5, ):\n", | ||
" # \n", | ||
" # Also called \"MART\" for Multiplicative ART\n", | ||
" # This keeps a positivity constraint for both the data and reconstruction\n", | ||
" #\n", | ||
" # This implementation was inspired from from:\n", | ||
" # https://www.youtube.com/watch?v=IhETD4nSJec\n", | ||
" # by Andrew Reader\n", | ||
" #\n", | ||
" # ToDo : implement a mask\n", | ||
" # ToDo : check about the corners / circle=False aspects\n", | ||
" #\n", | ||
" # Number of pixels hitting each output in the sample:\n", | ||
" sensitivity_image = backproject( np.ones_like(sino), theta,\n", | ||
" projection_shifts = projection_shifts )\n", | ||
" recip_sensitivity_image = 1./sensitivity_image\n", | ||
" # The image reconstruction:\n", | ||
" mlem_rec = np.empty( sensitivity_image.shape, np.float32)\n", | ||
" mlem_rec[:] = startvalue\n", | ||
" for i in range(niter):\n", | ||
" calc_sino = forwardproject( mlem_rec, theta, projection_shifts = projection_shifts )\n", | ||
" ratio = sino / (calc_sino + divtol )\n", | ||
" correction = recip_sensitivity_image * backproject( ratio, theta, \n", | ||
" projection_shifts = projection_shifts )\n", | ||
" mlem_rec *= correction\n", | ||
" return mlem_rec" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "78c6d5b3-a575-4286-a26a-da0cfbcbe588", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"im = skimage.data.shepp_logan_phantom().astype(np.float32)\n", | ||
"pl.imshow(im)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "305bf8e6-64db-4c97-9d97-af7a85ebd639", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"# generate some angles from a diffraction pattern\n", | ||
"\n", | ||
"import ImageD11.transform, ImageD11.unitcell\n", | ||
"import scipy.spatial.transform\n", | ||
"\n", | ||
"# Angles to use are going to correspond to 5 rings of fcc\n", | ||
"a = 3.06\n", | ||
"cell = ImageD11.unitcell.unitcell( [a,a,a,90,90,90],'F')\n", | ||
"cell.makerings(2)\n", | ||
"hkls = []\n", | ||
"for ds in cell.ringds[:5]:\n", | ||
" hkls += cell.ringhkls[ds]\n", | ||
"hkls = np.array( hkls )\n", | ||
"hkls.shape" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "27e7634c-a59d-4134-9055-8eba0ef50c99", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"UB = scipy.spatial.transform.Rotation.random( random_state = 97 ).as_matrix()\n", | ||
"gvecs = np.dot( UB/a, hkls.T )" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "38c231c6-bbfa-407c-9ae3-999c233883a4", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"tth, (eta0, eta1), (omega0, omega1) = ImageD11.transform.uncompute_g_vectors(gvecs, 12.39/50)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "f745a86b-4a7b-4a22-8a74-e27a7e894678", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"measured = np.concatenate( [ omega0[ np.abs( np.sin( np.radians( eta0 ) ) ) > 0.1 ],\n", | ||
" omega1[ np.abs( np.sin( np.radians( eta1 ) ) ) > 0.1 ] ] )\n", | ||
"print(eta0.shape, measured.shape, measured.min(), measured.max())" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "dffc1087-70f5-4ca0-ad09-b67250dceaa1", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"order = np.argsort( measured )\n", | ||
"theta = measured[ order ]\n", | ||
"theta = theta[ theta > 0 ]" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "3b17d387-bdc8-4633-9c48-46a1f700f435", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"sino = forwardproject( im, theta )" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "6314be77-51a6-445a-814a-be1f305814de", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"pl.imshow(sino, aspect='auto', interpolation='nearest')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "009c4844-6b8b-47a0-9ced-287e96fd4f54", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"def disp(im, title):\n", | ||
" f, a = pl.subplots(1,2,figsize=(12,5))\n", | ||
" f.colorbar( a[1].imshow(im), ax=a[0] )\n", | ||
" a[1].set(title=title, xticks=(), yticks=())\n", | ||
" a[0].plot( im[im.shape[0]//2] )\n", | ||
" a[0].plot( im[:,im.shape[1]//2] )\n", | ||
" pl.show()" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "69a4c8d3-bb3c-4a4a-adcc-0ad024391234", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"fbp = iradon( sino, theta, output_size=im.shape[0])\n", | ||
"disp(fbp,'Hann filter')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "8dd83a9a-41b5-44f8-a7a7-da920b6e649f", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"mlr = mlem( sino, theta, niter=1)\n", | ||
"n = 1\n", | ||
"disp( mlr, f'mlem {n} steps' )\n", | ||
"for niter in (1,4,20,100):\n", | ||
" mlr = mlem( sino, theta, startvalue = mlr, niter=niter)\n", | ||
" n += niter\n", | ||
" disp( mlr, f'mlem {n} steps' )" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "fef2e35e-9330-475b-b8ff-56bcb2180247", | ||
"metadata": { | ||
"tags": [] | ||
}, | ||
"outputs": [], | ||
"source": [ | ||
"rsart = skimage.transform.iradon_sart(sino, theta )\n", | ||
"disp( rsart, 'iradon_sart')" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "62356c3e-9f7b-48a4-a0b4-6b29f01a75cf", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3 (main)", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.11.6" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 5 | ||
} |