import%20marimo%0A%0A__generated_with%20%3D%20%220.23.4%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0Awith%20app.setup%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Image%20transformation%20with%20%60transformnd%60%0A%0A%20%20%20%20%60transformnd%60%20transforms%20coordinates%2C%20not%20images%2C%20but%20coordinate%20transformations%20can%20be%20used%20to%20transform%20images.%0A%20%20%20%20Your%20output%20(transformed)%20and%20source%20images%20both%20have%20pixels%20with%20an%20%60xy%60%20coordinate%20in%20their%20respective%20image%20spaces%2C%0A%20%20%20%20and%20image%20transformation%20is%20simply%20a%20case%20of%20finding%20which%20source%20pixel%20to%20use%20for%20each%20output%20pixel.%0A%0A%20%20%20%20Here%20we%20take%20a%202-channel%20fluorescence%20microscopy%20image%20of%20some%20cells%20in%203%20dimensions%2C%20use%20scaling%20information%20to%20map%20those%20pixels%20into%20a%20real-world%20space%2C%20and%20then%20map%20the%20pixels%20of%20our%20viewport%20into%20the%20the%20same%20space.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20skimage.data%20import%20cells3d%0A%0A%20%20%20%20%23%20ZCYX%2C%20(0.29um%2C%20membrane%2Fnuclei%20channels%2C%200.26um%2C%200.26um)%0A%20%20%20%20CELLS_SPACE%20%3D%20%22cells%22%0A%0A%20%20%20%20%23%20CZYX%2C%20(membrane%2Fnuclei%2C%20um%2C%20um%2C%20um)%0A%20%20%20%20WORLD_SPACE%20%3D%20%22world%22%0A%0A%20%20%20%20%23%20YXC%20image%20with%20RGB%20channels%0A%20%20%20%20VIEWPORT_SPACE%20%3D%20%22viewport%22%0A%0A%20%20%20%20cells%20%3D%20cells3d()%0A%20%20%20%20cells%20%3D%20cells.astype(%22float64%22)%0A%20%20%20%20cells%20-%3D%20cells.min()%0A%20%20%20%20cells%20%2F%3D%20cells.max()%0A%0A%20%20%20%20print(f%22%7Bcells.shape%3D%7D%22)%0A%20%20%20%20print(f%22%7Bcells.dtype%3D%7D%22)%0A%20%20%20%20print(f%22%7Bcells.min()%3D%7D%22)%0A%20%20%20%20print(f%22%7Bcells.max()%3D%7D%22)%0A%20%20%20%20return%20CELLS_SPACE%2C%20VIEWPORT_SPACE%2C%20WORLD_SPACE%2C%20cells%0A%0A%0A%40app.cell%0Adef%20_(CELLS_SPACE%2C%20VIEWPORT_SPACE%2C%20WORLD_SPACE)%3A%0A%20%20%20%20import%20transformnd%20as%20tnd%0A%20%20%20%20from%20transformnd.transforms%20import%20ProjectAxis%2C%20MapAxis%2C%20Scale%0A%0A%20%20%20%20%23%20Aligned%20at%20world%20origin.%0A%20%20%20%20%23%20This%20would%20be%20stored%20alongside%20the%20data.%0A%20%20%20%20cells_to_world%20%3D%20tnd.base.TransformSequence(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Move%20the%20color%20axis%20to%20the%20first%20position%0A%20%20%20%20%20%20%20%20%20%20%20%20MapAxis(%5B1%2C%200%2C%202%2C%203%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Scale%20the%20space%20axes%0A%20%20%20%20%20%20%20%20%20%20%20%20Scale(%5B1%2C%200.29%2C%200.26%2C%200.26%5D)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20spaces%3Dtnd.Spaces(CELLS_SPACE%2C%20WORLD_SPACE)%2C%0A%20%20%20%20)%0A%20%20%20%20print(cells_to_world)%0A%0A%20%20%20%20%23%20Aligned%20at%20world%20origin.%0A%20%20%20%20%23%20This%20would%20be%20chosen%20by%20the%20viewing%20application.%0A%20%20%20%20viewport_to_world%20%3D%20tnd.base.TransformSequence(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Create%20a%20Z%20axis%0A%20%20%20%20%20%20%20%20%20%20%20%20ProjectAxis(created%3D%7B0%7D%2C%20source_ndim%3D3)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Move%20the%20color%20axis%20to%20the%20first%20position%0A%20%20%20%20%20%20%20%20%20%20%20%20MapAxis(%5B3%2C%200%2C%201%2C%202%5D)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%23%20Choose%20a%20spatial%20sampling%20frequency%20(here%200.2um%20isotropic)%0A%20%20%20%20%20%20%20%20%20%20%20%20Scale(%5B1%2C%200.2%2C%200.2%2C%200.2%5D)%2C%0A%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20spaces%3Dtnd.Spaces(VIEWPORT_SPACE%2C%20WORLD_SPACE)%2C%0A%20%20%20%20)%0A%20%20%20%20print(viewport_to_world)%0A%20%20%20%20return%20cells_to_world%2C%20viewport_to_world%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Both%20images%20know%20how%20to%20transform%20their%20array%20indices%20into%20the%20real%20world.%0A%0A%20%20%20%20We%20can%20invert%20one%20of%20those%20transforms%20to%20get%20a%20transformation%20between%20viewport-space%20and%20cell-space.%0A%20%20%20%20We%20can%20also%20have%20a%20separate%20transformation%20to%20control%20moving%20the%20viewport%20(useful%20if%20we%20had%20an%20interactive%20viewer).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cells_to_world%2C%20viewport_to_world)%3A%0A%20%20%20%20from%20transformnd.transforms%20import%20Translate%0A%0A%20%20%20%20%23%20Shift%20the%20viewport%20within%20the%20data%2C%20in%20world%20measurements.%0A%20%20%20%20%23%20This%20would%20be%20controlled%20by%20the%20user%20as%20they%20peruse%20the%20data.%0A%20%20%20%20viewport_offset%20%3D%20Translate(%5B0%2C%2035%20*%200.29%2C%2064%20*%200.26%2C%200.0%5D)%0A%0A%20%20%20%20viewport_to_cells%20%3D%20viewport_to_world%20%7C%20viewport_offset%20%7C%20~cells_to_world%0A%20%20%20%20return%20(viewport_to_cells%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20Here%20we%20want%20to%20get%20all%20of%20the%20coordinates%20of%20our%20viewport%2C%20across%20all%20channels%2C%20in%20the%20shape%20needed%20by%20%60transformnd%60%20(number%20of%20coordinates%20x%20dimensionality%20of%20coordinates).%0A%0A%20%20%20%20We%20then%20transform%20that%20to%20get%20the%20positions%20of%20those%20coordinates%20within%20the%20cells%20image.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(viewport_to_cells)%3A%0A%20%20%20%20import%20numpy%20as%20np%0A%0A%20%20%20%20%23%202D%20YXC%20image%0A%20%20%20%20viewport_shape%20%3D%20(128%2C%20256%2C%203)%0A%0A%20%20%20%20indices%20%3D%20%5Bnp.arange(s%2C%20dtype%3Dfloat)%20for%20s%20in%20viewport_shape%5D%0A%20%20%20%20grids%20%3D%20np.meshgrid(*indices%2C%20indexing%3D%22ij%22)%0A%0A%20%20%20%20%23%20Y%2C%20X%2C%20C%2C%20coords%0A%20%20%20%20vp_coords_3d%20%3D%20np.stack(grids%2C%20-1)%0A%20%20%20%20print(f%22%7Bvp_coords_3d.shape%3D%7D%22)%0A%0A%20%20%20%20%23%20Y*X*C%2C%20coords%0A%20%20%20%20vp_coords%20%3D%20vp_coords_3d.reshape((-1%2C%20len(viewport_shape)))%0A%20%20%20%20print(f%22%7Bvp_coords.shape%3D%7D%22)%0A%0A%20%20%20%20%23%20Z*C*Y*X%2C%20coords%0A%20%20%20%20cells_coords%20%3D%20viewport_to_cells.apply(vp_coords)%0A%20%20%20%20print(f%22%7Bcells_coords.shape%3D%7D%22)%0A%20%20%20%20return%20cells_coords%2C%20viewport_shape%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%60scipy.ndimage.map_coordinates%60%20is%20where%20the%20magic%20happens%3B%20looking%20up%20our%20coordinates%20in%20the%20cells%20image%20to%20get%20the%20intensities.%20There's%20a%20dask%20version%20too!%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(cells%2C%20cells_coords%2C%20viewport_shape)%3A%0A%20%20%20%20from%20scipy.ndimage%20import%20map_coordinates%0A%0A%20%20%20%20%23%20transformnd%20uses%20%60NxD%60%20coordinate%20arrays%3B%20map_coordinates%20uses%20%60DxN%60%0A%20%20%20%20cells_vals%20%3D%20map_coordinates(cells%2C%20cells_coords.T).T%0A%20%20%20%20print(f%22%7Bcells_vals.shape%3D%7D%22)%0A%20%20%20%20viewport%20%3D%20cells_vals.reshape(viewport_shape)%0A%20%20%20%20print(f%22%7Bviewport.shape%3D%7D%22)%0A%20%20%20%20return%20(viewport%2C)%0A%0A%0A%40app.cell%0Adef%20_(viewport)%3A%0A%20%20%20%20from%20matplotlib%20import%20pyplot%20as%20plt%0A%0A%20%20%20%20plt.imshow(viewport)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
fd15c045edf90a15c7c31012de8891d4