%23%20%2F%2F%2F%20script%0A%23%20requires-python%20%3D%20%22%3E%3D3.11%22%0A%23%20dependencies%20%3D%20%5B%0A%23%20%20%20%20%20%22ngio%3D%3D0.5.9%22%2C%0A%23%20%20%20%20%20%22marimo%22%2C%0A%23%20%20%20%20%20%22matplotlib%22%2C%0A%23%20%20%20%20%20%22scikit-image%22%2C%0A%23%20%20%20%20%20%22altair%22%2C%0A%23%20%5D%0A%23%20%2F%2F%2F%0A%0Aimport%20marimo%0A%0A__generated_with%20%3D%20%220.23.4%22%0Aapp%20%3D%20marimo.App(width%3D%22full%22%2C%20auto_download%3D%5B%22html%22%5D)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20from%20pathlib%20import%20Path%0A%0A%20%20%20%20return%20Path%2C%20mo%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%3Cdiv%20style%3D%22display%3Ainline-block%3Bfont-family%3A'DM%20Mono'%2Cui-monospace%2Cmonospace%3Bfont-size%3A0.7rem%3Bfont-weight%3A500%3Bletter-spacing%3A0.1em%3Btext-transform%3Auppercase%3Bcolor%3A%233DBDB8%3Bbackground%3Argba(61%2C189%2C184%2C0.10)%3Bborder%3A1px%20solid%20rgba(61%2C189%2C184%2C0.30)%3Bborder-radius%3A99px%3Bpadding%3A2px%2010px%3B%22%3EMODULE%2001%3C%2Fdiv%3E%0A%0A%20%20%20%20%23%20Introduction%20to%20ngio%0A%0A%20%20%20%20**ngio**%20is%20a%20Python%20library%20that%20provides%20a%20clean%2C%20object-oriented%20API%20for%20reading%2C%0A%20%20%20%20writing%2C%20and%20exploring%20%5BOME-Zarr%5D(https%3A%2F%2Fngff.openmicroscopy.org%2F)%20files.%0A%0A%20%20%20%20%23%23%23%20Goals%20of%20this%20notebook%0A%20%20%20%20-%20Become%20familiar%20with%20ngio%2C%20its%20abstractions%2C%20and%20APIs.%0A%20%20%20%20-%20Open%2C%20explore%2C%20and%20process%20an%20OME-Zarr%20container.%0A%20%20%20%20-%20Integrate%20tabular%20data%20(ROIs%2C%20feature%20tables%2C%20and%20more).%0A%20%20%20%20-%20Implement%20a%20basic%20image-processing%20pipeline%3A%0A%20%20%20%20%20%20Maximum%20Intensity%20Projection%20%E2%86%92%20Basic%20segmentation%20%E2%86%92%20Feature%20extraction.%0A%20%20%20%20-%20Perform%20quality%20control%20by%20linking%20image%20that%20and%20quantification%20in%20an%20interactive%20scatter%20plot.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_()%3A%0A%20%20%20%20%23%20BVC%20matplotlib%20defaults%20%E2%80%94%20applied%20once%20for%20the%20whole%20notebook%20(STYLE.md%20%C2%A79).%0A%20%20%20%20import%20matplotlib.pyplot%20as%20plt%0A%20%20%20%20import%20numpy%20as%20np%0A%0A%20%20%20%20BVC_NAVY%20%3D%20%22%231B2A4A%22%0A%20%20%20%20BVC_TEAL%20%3D%20%22%233DBDB8%22%0A%20%20%20%20BVC_PALETTE%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22%233DBDB8%22%2C%20%20%23%20teal%0A%20%20%20%20%20%20%20%20%22%234A8FD4%22%2C%20%20%23%20blue%0A%20%20%20%20%20%20%20%20%22%235CB87A%22%2C%20%20%23%20green%0A%20%20%20%20%20%20%20%20%22%23F4956A%22%2C%20%20%23%20orange%0A%20%20%20%20%20%20%20%20%22%23FF7B9C%22%2C%20%20%23%20pink%0A%20%20%20%20%20%20%20%20%22%23B6A3FF%22%2C%20%20%23%20purple%0A%20%20%20%20%5D%0A%20%20%20%20BVC_FIGSIZE%20%3D%20(8%2C%208)%0A%0A%20%20%20%20from%20matplotlib%20import%20font_manager%0A%0A%20%20%20%20_installed_fonts%20%3D%20%7Bf.name%20for%20f%20in%20font_manager.fontManager.ttflist%7D%0A%20%20%20%20_font_family%20%3D%20%5B%0A%20%20%20%20%20%20%20%20f%0A%20%20%20%20%20%20%20%20for%20f%20in%20(%22Inter%22%2C%20%22DejaVu%20Sans%22%2C%20%22sans-serif%22)%0A%20%20%20%20%20%20%20%20if%20f%20in%20_installed_fonts%20or%20f%20%3D%3D%20%22sans-serif%22%0A%20%20%20%20%5D%0A%0A%20%20%20%20plt.rcParams.update(%0A%20%20%20%20%20%20%20%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22figure.facecolor%22%3A%20%22white%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.facecolor%22%3A%20%22white%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.edgecolor%22%3A%20BVC_NAVY%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.labelcolor%22%3A%20BVC_NAVY%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.titlecolor%22%3A%20BVC_NAVY%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.spines.top%22%3A%20False%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.spines.right%22%3A%20False%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.linewidth%22%3A%200.8%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22xtick.color%22%3A%20%22%236B7A99%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22ytick.color%22%3A%20%22%236B7A99%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22font.family%22%3A%20_font_family%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22font.size%22%3A%2011%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.titlesize%22%3A%2013%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.titleweight%22%3A%20%22bold%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22axes.labelsize%22%3A%2011%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22legend.frameon%22%3A%20False%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22savefig.dpi%22%3A%20150%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22savefig.bbox%22%3A%20%22tight%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22image.cmap%22%3A%20%22viridis%22%2C%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20)%0A%20%20%20%20plt.rcParams%5B%22axes.prop_cycle%22%5D%20%3D%20plt.cycler(color%3DBVC_PALETTE)%0A%20%20%20%20return%20BVC_TEAL%2C%20np%2C%20plt%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20ngio_classes%20%3D%20mo.mermaid(%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20%20%20%20%20graph%20TD%0A%20%20%20%20%20%20%20%20%20%20%20%20%25%25%20%3D%3D%3D%20STYLE%20DEFINITIONS%20(Easy%20to%20modify!)%20%3D%3D%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20%25%25%20Container%20nodes%20-%20Main%20containers%20(BVC%20navy)%0A%20%20%20%20%20%20%20%20%20%20%20%20classDef%20containerStyle%20fill%3A%231B2A4A%2Cstroke%3A%230D1826%2Cstroke-width%3A3px%2Ccolor%3A%23fff%2Cfont-weight%3Abold%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%25%25%20Multiscale%20nodes%20-%20Collections%20of%20pyramid%20levels%20(BVC%20teal)%0A%20%20%20%20%20%20%20%20%20%20%20%20classDef%20multiscaleStyle%20fill%3A%233DBDB8%2Cstroke%3A%23278784%2Cstroke-width%3A2px%2Ccolor%3A%23fff%2Cfont-weight%3Abold%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%25%25%20Image%2FLabel%20nodes%20-%20Individual%20pyramid%20levels%20(BVC%20blue)%0A%20%20%20%20%20%20%20%20%20%20%20%20classDef%20imageStyle%20fill%3A%234A8FD4%2Cstroke%3A%232E5C8A%2Cstroke-width%3A2px%2Ccolor%3A%23fff%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%25%25%20Table%20nodes%20-%20Data%20tables%20(BVC%20green)%0A%20%20%20%20%20%20%20%20%20%20%20%20classDef%20tableStyle%20fill%3A%235CB87A%2Cstroke%3A%233D7A54%2Cstroke-width%3A2px%2Ccolor%3A%23fff%2Cfont-weight%3Abold%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%25%25%20%3D%3D%3D%20GRAPH%20STRUCTURE%20%3D%3D%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20OZC%5B%22OmeZarrContainer%22%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20OZC%20--%3E%20IMG%5B%22MultiscaleImage%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20IMG%20--%3E%20IMGL0%5B%22Image(path%3D'0')%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20IMG%20--%3E%20IMGL1%5B%22Image(path%3D'1')%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20IMG%20--%3E%20IMGL2%5B%22...%22%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20OZC%20--%3E%20LC%5B%22LabelsContainer%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20LC%20--%3E%20OZLC-nuc%5B%22nuclei%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20OZLC-nuc%20--%3E%20OZLC-nucL0%5B%22Label(path%3D'0')%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20OZLC-nuc%20--%3E%20OZLC-nucL1%5B%22Label(path%3D'1')%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20OZLC-nuc%20--%3E%20OZLC-nucL2%5B%22...%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20LC%20--%3E%20OZLC-other%5B%22...%22%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20OZC%20--%3E%20TBL%5B%22TablesContainer%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20TBL%20--%3E%20TBL-roi%5B%22RoiTable%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20TBL%20--%3E%20TBL-features%5B%22FeaturesTable%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20TBL%20--%3E%20TBL-other%5B%22...%22%5D%0A%0A%20%20%20%20%20%20%20%20%20%20%20%20%25%25%20%3D%3D%3D%20APPLY%20STYLES%20%3D%3D%3D%0A%20%20%20%20%20%20%20%20%20%20%20%20class%20OZC%2CLC%2CTBL%20containerStyle%0A%20%20%20%20%20%20%20%20%20%20%20%20class%20IMG%2COZLC-nuc%2COZLC-other%20multiscaleStyle%0A%20%20%20%20%20%20%20%20%20%20%20%20class%20IMGL0%2CIMGL1%2CIMGL2%2COZLC-nucL0%2COZLC-nucL1%2COZLC-nucL2%20imageStyle%0A%20%20%20%20%20%20%20%20%20%20%20%20class%20TBL-roi%2CTBL-features%2CTBL-other%20tableStyle%0A%20%20%20%20%20%20%20%20%22%22%22%0A%20%20%20%20)%0A%20%20%20%20return%20(ngio_classes%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20ngio_classes)%3A%0A%20%20%20%20mo.md(rf%22%22%22%0A%20%20%20%20%23%23%201%20Overview%0A%0A%20%20%20%20%23%23%23%20Why%20OME-Zarr%3F%0A%0A%20%20%20%20OME-Zarr%20is%20the%20community-standardised%20file%20format%20for%20bioimaging%0A%20%20%20%20(%5BNGFF%5D(https%3A%2F%2Fngff.openmicroscopy.org%2F)).%20It%20stores%20images%20in%20**chunks**%0A%20%20%20%20(so%20viewers%20and%20pipelines%20only%20read%20what%20they%20need)%2C%20keeps%20a%20**multiscale%0A%20%20%20%20pyramid**%20for%20fast%20zoom-out%2C%20and%20lives%20equally%20well%20on%20a%20local%20disk%20or%20in%0A%20%20%20%20cloud%20object%20storage.%20ngio%20is%20a%20Pythonic%20layer%20on%20top%20of%20it.%0A%0A%20%20%20%20%23%23%23%20What%20is%20the%20OME-Zarr%20container%3F%0A%0A%20%20%20%20The%20%60OmeZarrContainer%60%20is%20your%20entry%20point.%20From%20it%20you%20can%3A%0A%0A%20%20%20%20-%20**Inspect**%20the%20OME-Zarr%20file%3A%20pyramid%20levels%2C%20channels%2C%20available%20labels%20and%20tables.%0A%20%20%20%20-%20**Read%20images**%20at%20any%20resolution%20level%20%2F%20pixel%20size.%0A%20%20%20%20-%20**Manage%20labels**%3A%20list%2C%20read%2C%20and%20create%20new%20segmentation%20masks.%0A%20%20%20%20-%20**Manage%20tables**%3A%20list%2C%20read%2C%20and%20add%20ROI%20%2F%20feature%20%2F%20condition%20tables.%0A%20%20%20%20-%20**Derive**%20new%20OME-Zarr%20images%20that%20share%20the%20source's%20metadata.%0A%20%20%20%20-%20**Edit**%20OME-Zarr%20metadata%20through%20high-level%20APIs.%0A%0A%20%20%20%20%7Bngio_classes%7D%0A%0A%20%20%20%20%23%23%23%20What%20it%20isn't%0A%0A%20%20%20%20The%20container%20does%20**not**%20expose%20the%20pixel%20data%20directly%20%E2%80%94%20for%20that%20you%0A%20%20%20%20ask%20it%20for%20an%20%60Image%60%2C%20%60Label%60%2C%20or%20%60Table%60%20object.%20The%20diagram%20above%20is%0A%20%20%20%20the%20mental%20model%20to%20keep%20in%20mind%20for%20the%20rest%20of%20this%20notebook.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%202%20Setup%0A%0A%20%20%20%20We%20will%20use%20a%20small%20sample%20HCS%20plate%20(%60CardiomyocyteTiny%60)%20that%20ships%20with%0A%20%20%20%20ngio's%20test%20datasets.%20The%20helper%20%60download_ome_zarr_dataset%60%20fetches%20it%0A%20%20%20%20into%20a%20local%20temp%20directory%20the%20first%20time%20it%20runs%20(subsequent%20runs%20reuse%0A%20%20%20%20the%20cached%20copy).%0A%0A%20%20%20%20We%20then%20open%20a%20single%20well%20image%20(%60B%2F03%2F0%60)%20as%20an%20%60OmeZarrContainer%60.%0A%20%20%20%20HCS%20plates%20are%20organised%20as%20**row%20letter%20%2F%20column%20number%20%2F%20image%0A%20%20%20%20index**%2C%20so%20%60B%2F03%2F0%60%20is%20*row%20B%2C%20column%203%2C%20image%200*%20%E2%80%94%20one%20well%20image%20out%0A%20%20%20%20of%20the%20plate.%20This%20is%20the%20object%20we%20will%20work%20with%20throughout%20the%0A%20%20%20%20notebook.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(Path)%3A%0A%20%20%20%20import%20ngio%0A%20%20%20%20from%20ngio.utils%20import%20download_ome_zarr_dataset%2C%20list_ome_zarr_datasets%0A%0A%20%20%20%20data_dir%20%3D%20Path(%22data%22)%0A%20%20%20%20data_dir.mkdir(exist_ok%3DTrue)%0A%20%20%20%20hcs_path%20%3D%20download_ome_zarr_dataset(%0A%20%20%20%20%20%20%20%20%22CardiomyocyteTiny%22%2C%20download_dir%3Ddata_dir%0A%20%20%20%20)%0A%20%20%20%20image_path%20%3D%20hcs_path%20%2F%20%22B%22%20%2F%20%2203%22%20%2F%20%220%22%0A%20%20%20%20ome_zarr_container%20%3D%20ngio.open_ome_zarr_container(image_path)%0A%20%20%20%20image%20%3D%20ome_zarr_container.get_image()%0A%20%20%20%20return%20data_dir%2C%20image%2C%20image_path%2C%20ngio%2C%20ome_zarr_container%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(ome_zarr_container%2C%20plot_image)%3A%0A%20%20%20%20plot_image(%0A%20%20%20%20%20%20%20%20ome_zarr_container%2C%0A%20%20%20%20%20%20%20%20title%3D%22CardiomyocyteTiny%2C%20well%20B%2F03%2F0%20(original%20image%2C%20level%200)%22%2C%0A%20%20%20%20%20%20%20%20axes_order%3D%22yx%22%2C%0A%20%20%20%20%20%20%20%20z%3D0%2C%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(image_path%2C%20mo)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20Opened%20container%3A%20%60%7Bimage_path%7D%60%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%203%20The%20ngio%20object%20model%0A%0A%20%20%20%20We%20will%20look%20at%20four%20pieces%20in%20turn%3A%0A%0A%20%20%20%201.%20**%60OmeZarrContainer%60**%20%E2%80%94%20the%20file-level%20handle.%0A%20%20%20%202.%20**%60Image%60**%20and%20**%60Label%60**%20%E2%80%94%20pixel%20data%2C%20one%20resolution%20level%20at%20a%20time.%0A%20%20%20%203.%20**%60Table%60**%20%E2%80%94%20the%20tabular%20companion%20(ROIs%2C%20features%2C%20conditions%2C%20%E2%80%A6).%0A%20%20%20%204.%20**%60Deriving%60**%20%E2%80%94%20how%20to%20spawn%20a%20new%20OME-Zarr%20that%20mirrors%20the%20source.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%203.1%20OmeZarrContainer%0A%0A%20%20%20%20The%20container%20exposes%20the%20file's%20structure%20as%20plain%20Python%20attributes%0A%20%20%20%20and%20methods.%20The%20most%20useful%20ones%20at%20a%20glance%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20ome_zarr_container)%3A%0A%20%20%20%20_rows%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%22%7C%20Property%20%7C%20Value%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%22%7C---%7C---%7C%22%2C%0A%20%20%20%20%20%20%20%20f%22%7C%20%60levels%60%20%7C%20%60%7Bome_zarr_container.levels%7D%60%20%7C%22%2C%0A%20%20%20%20%20%20%20%20f%22%7C%20%60level_paths%60%20%7C%20%60%7Bome_zarr_container.level_paths%7D%60%20%7C%22%2C%0A%20%20%20%20%20%20%20%20f%22%7C%20%60is_3d%60%20%7C%20%60%7Bome_zarr_container.is_3d%7D%60%20%7C%22%2C%0A%20%20%20%20%20%20%20%20f%22%7C%20%60is_time_series%60%20%7C%20%60%7Bome_zarr_container.is_time_series%7D%60%20%7C%22%2C%0A%20%20%20%20%20%20%20%20f%22%7C%20%60channel_labels%60%20%7C%20%60%7Bome_zarr_container.channel_labels%7D%60%20%7C%22%2C%0A%20%20%20%20%20%20%20%20f%22%7C%20%60list_labels()%60%20%7C%20%60%7Bome_zarr_container.list_labels()%7D%60%20%7C%22%2C%0A%20%20%20%20%20%20%20%20f%22%7C%20%60list_tables()%60%20%7C%20%60%7Bome_zarr_container.list_tables()%7D%60%20%7C%22%2C%0A%20%20%20%20%5D%0A%20%20%20%20mo.md(%22%5Cn%22.join(_rows))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%23%20A%20note%20on%20multiscale%20pyramids%0A%0A%20%20%20%20%60levels%20%3D%205%60%20means%20this%20image%20is%20stored%20at%20**5%20resolutions**%3A%20level%20%600%60%0A%20%20%20%20is%20the%20full-resolution%20data%2C%20and%20each%20subsequent%20level%20is%20a%20downsampled%0A%20%20%20%20copy%20(typically%20a%20factor%20of%202%20in%20XY).%0A%0A%20%20%20%20Throughout%20this%20notebook%2C%20%60path%3D%220%22%60%20means%20*full%20resolution*%3B%20pass%0A%20%20%20%20%60path%3D%221%22%60%2C%20%60%222%22%60%2C%20%E2%80%A6%20to%20read%20a%20coarser%2C%20smaller%20copy.%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%20ngio%20import%20create_ome_zarr_from_array%2C%20create_empty_ome_zarr%0A%20%20%20%20%23%20TODOs%20Create%20a%20sample%20image%20container%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(image%2C%20mo)%3A%0A%20%20%20%20mo.md(rf%22%22%22%0A%20%20%20%20%23%23%23%203.2%20Images%20and%20Labels%0A%0A%20%20%20%20An%20**%60Image%60**%20represents%20**one%20resolution%20level**%20of%20the%20multiscale%0A%20%20%20%20pyramid.%20Get%20it%20from%20the%20container%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20image%20%3D%20ome_zarr_container.get_image()%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20full%20resolution%20(default)%0A%20%20%20%20image%20%3D%20ome_zarr_container.get_image(path%3D%221%22)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20specific%20pyramid%20level%0A%20%20%20%20image%20%3D%20ome_zarr_container.get_image(pixel_size%3Dps%2C%20strict%3DFalse)%20%20%20%23%20nearest%20matching%20resolution%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Key%20properties%20of%20the%20image%20we%20just%20opened%3A%0A%0A%20%20%20%20%7C%20Property%20%7C%20Value%20%7C%0A%20%20%20%20%7C---%7C---%7C%0A%20%20%20%20%7C%20%60dimensions%60%20%7C%20%60%7Bimage.dimensions%7D%60%20%7C%0A%20%20%20%20%7C%20%60pixel_size%60%20%7C%20%60%7Bimage.pixel_size%7D%60%20%7C%0A%20%20%20%20%7C%20%60shape%60%20%7C%20%60%7Bimage.shape%7D%60%20%7C%0A%20%20%20%20%7C%20%60axes%60%20%7C%20%60%7Bimage.axes%7D%60%20%7C%0A%20%20%20%20%7C%20%60dtype%60%20%7C%20%60%7Bimage.dtype%7D%60%20%7C%0A%0A%20%20%20%20%60pixel_size%60%20is%20given%20in%20physical%20units%20(here%20micrometers)%20and%20is%20the%0A%20%20%20%20bridge%20between%20**world%20coordinates**%20(ROI%20tables%2C%20scale%20bars)%20and%0A%20%20%20%20**pixel%20coordinates**%20(numpy%20slices).%20%60axes%60%20tells%20you%20the%20order%20of%0A%20%20%20%20the%20array%20axes%20%E2%80%94%20in%20our%20case%20%60c%2C%20z%2C%20y%2C%20x%60.%0A%0A%20%20%20%20Key%20methods%3A%0A%0A%20%20%20%20-%20**%60image.get_as_numpy(...)%60**%20%E2%80%94%20eager%20read%20into%20RAM.%20Use%20this%20for%0A%20%20%20%20%20%20small%20ROIs%20you'll%20work%20on%20in%20memory.%0A%20%20%20%20-%20**%60image.get_as_dask(...)%60**%20%E2%80%94%20lazy%20read.%20Use%20it%20when%20the%20array%20is%0A%20%20%20%20%20%20larger%20than%20RAM%2C%20or%20when%20you%20want%20to%20compose%20lazy%20operations%20(the%0A%20%20%20%20%20%20MIP%20further%20down%20is%20a%20good%20example).%0A%20%20%20%20-%20**%60image.set_array(...)%60**%20%E2%80%94%20write%20data%20back%20into%20the%20OME-Zarr.%0A%20%20%20%20-%20**%60image.consolidate()%60**%20%E2%80%94%20rebuild%20the%20lower-resolution%20pyramid%0A%20%20%20%20%20%20levels%20from%20the%20data%20you%20just%20wrote%20at%20level%200.%0A%0A%20%20%20%20A%20**%60Label%60**%20stores%20**integer%20segmentation%20masks**.%20It%20shares%20the%0A%20%20%20%20image's%20multiscale%20pyramid%20but%20sometimes%20has%20no%20channel%20axis.%20The%20API%0A%20%20%20%20mirrors%20%60Image%60%20exactly%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20ome_zarr_container.list_labels()%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20discover%20labels%0A%20%20%20%20label%20%3D%20ome_zarr_container.get_label(%22nuclei%22)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20full%20resolution%0A%20%20%20%20label%20%3D%20ome_zarr_container.get_label(%22nuclei%22%2C%20path%3D%221%22)%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20specific%20level%0A%20%20%20%20label%20%3D%20ome_zarr_container.get_label(%22nuclei%22%2C%20pixel_size%3Dimage.pixel_size)%20%23%20matching%20resolution%0A%20%20%20%20%60%60%60%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%20%23%20TODOs%20let's%20play%20with%20the%20sample%20OME-Zarr%20container%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%203.3%20Tables%0A%0A%20%20%20%20Tables%20in%20ngio%20are%20described%20along%20**three%20independent%20axes**%20%E2%80%94%20any%0A%20%20%20%20combination%20is%20valid%2C%20so%20you%20can%20pick%20the%20storage%20you%20need%20without%0A%20%20%20%20giving%20up%20the%20in-memory%20view%20or%20the%20semantic%20structure%20you%20want.%0A%0A%20%20%20%20-%20**Backend**%20*(on-disk%20storage)*%3A%20%60AnnData%60%20(default)%2C%20%60CSV%60%2C%0A%20%20%20%20%20%20%60Parquet%60%2C%20%60JSON%60%2C%20%E2%80%A6your%20own.%0A%20%20%20%20-%20**Object**%20*(in-memory%20view)*%3A%20%60AnnData%60%2C%20%60pandas.DataFrame%60%2C%0A%20%20%20%20%20%20%60polars.LazyFrame%60.%20Every%20table%20exposes%20all%20three%20via%20%60.anndata%60%2C%0A%20%20%20%20%20%20%60.dataframe%60%2C%20and%20%60.lazy_frame%60%20regardless%20of%20the%20backend.%0A%20%20%20%20-%20**Type**%20*(expected%20layout)*%3A%20%60FeatureTable%60%2C%20%60RoiTable%60%2C%0A%20%20%20%20%20%20%60MaskingRoiTable%60%2C%20%60ConditionTable%60%2C%20%E2%80%A6your%20own.%0A%0A%20%20%20%20A%20short%20note%20on%20two%20ROI%20table%20flavours%20we%20will%20use%20later%3A%0A%0A%20%20%20%20-%20A%20**%60RoiTable%60**%20lists%20arbitrary%20regions%20(e.g.%20one%20ROI%20per%0A%20%20%20%20%20%20field-of-view).%0A%20%20%20%20-%20A%20**%60MaskingRoiTable%60**%20is%20**derived%20from%20a%20label%20image**%20%E2%80%94%20one%0A%20%20%20%20%20%20bounding%20ROI%20per%20object%20%E2%80%94%20and%20is%20what%20you%20want%20for%20%22zoom%20into%20one%0A%20%20%20%20%20%20nucleus%22%20workflows.%0A%0A%20%20%20%20The%20three%20axes%20meet%20at%20the%20read%2Fwrite%20idiom%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20ome_zarr_container.add_table(%0A%20%20%20%20%20%20%20%20name%3D%22my_features%22%2C%0A%20%20%20%20%20%20%20%20table%3DFeatureTable(feature_df%2C%20reference_label%3D%22basic_segmentation%22)%2C%0A%20%20%20%20%20%20%20%20backend%3D%22parquet%22%2C%20%20%20%20%20%20%20%20%20%20%23%20any%20backend%0A%20%20%20%20%20%20%20%20overwrite%3DTrue%2C%0A%20%20%20%20)%0A%0A%20%20%20%20table%20%3D%20ome_zarr_container.get_feature_table(%22my_features%22)%20%20%23%20type-aware%20access%0A%20%20%20%20table.dataframe%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20or%20.lazy_frame%20%2F%20.anndata%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Use%20%60ome_zarr_container.list_tables()%60%20(optionally%20with%0A%20%20%20%20%60filter_types%3D%22masking_roi_table%22%60%2C%20%60%22feature_table%22%60%2C%20%E2%80%A6)%20to%20discover%0A%20%20%20%20what's%20already%20on%20a%20container.%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%20ngio.tables%20import%20RoiTable%2C%20MaskingRoiTable%2C%20FeatureTable%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20%23%20TODOs%20Create%20a%20couple%20of%20sample%20tables%0A%20%20%20%20return%20FeatureTable%2C%20pd%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%203.4%20Deriving%20new%20Images%20and%20Labels%0A%0A%20%20%20%20%60derive_image%60%20creates%20a%20**new%20OME-Zarr%20container**%20that%20clones%20the%0A%20%20%20%20pyramid%20structure%2C%20pixel%20sizes%2C%20and%20channel%20metadata%20of%20the%20source%20%E2%80%94%0A%20%20%20%20ready%20to%20be%20filled%20with%20processed%20data%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20derived%20%3D%20ome_zarr_container.derive_image(%22output.zarr%22%2C%20overwrite%3DTrue)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Typical%20write-back%20pattern%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20out_image%20%3D%20derived.get_image()%0A%20%20%20%20out_image.set_array(processed_data)%20%20%20%23%20write%20to%20level%200%0A%20%20%20%20out_image.consolidate()%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20rebuild%20the%20coarser%20pyramid%20levels%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20%3E%20**Why%20call%20%60consolidate()%60%3F**%20When%20you%20write%20to%20level%200%2C%20the%20lower%0A%20%20%20%20%3E%20pyramid%20levels%20(1%2C%202%2C%20%E2%80%A6)%20still%20hold%20the%20*old*%20data.%20%60consolidate()%60%0A%20%20%20%20%3E%20re-downsamples%20level%200%20into%20all%20remaining%20levels%20so%20the%20pyramid%20is%0A%20%20%20%20%3E%20internally%20consistent.%20You%20can%20skip%20it%20if%20you%20only%20ever%20read%20level%0A%20%20%20%20%3E%200%2C%20but%20viewers%20will%20show%20stale%20data%20at%20lower%20resolutions.%0A%0A%20%20%20%20%60derive_label%60%20is%20the%20analogous%20helper%20for%20adding%20a%20new%20**empty%20label**%0A%20%20%20%20to%20an%20existing%20container%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20new_label%20%3D%20ome_zarr_container.derive_label(%22my_segmentation%22%2C%20overwrite%3DTrue)%0A%20%20%20%20new_label.set_array(mask)%0A%20%20%20%20new_label.consolidate()%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Below%20we%20derive%20a%20new%20container%20that%20project%20along%20the%20Z%20axes%20(we'll%0A%20%20%20%20store%20a%202-D%20Maximum%20Intensity%20Projection%20in%20it)%20and%20bumps%20the%20metadata%0A%20%20%20%20to%20NGFF%20v0.5.%20The%20shape%20of%20a%20derived%20container%20can%20differ%20from%20the%0A%20%20%20%20source%20but%20must%20keep%20the%20**same%20number%20of%20axes**%20(channels%20excepted).%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%204%20Putting%20it%20together%20%E2%80%94%20a%20basic%202D%20nuclei%20pipeline%0A%0A%20%20%20%20We%20now%20chain%20three%20steps%20that%20mirror%20a%20typical%202-D%0A%20%20%20%20nuclei-counting%20workflow%3A%0A%0A%20%20%20%201.%20**Derive**%20%E2%80%94%20setup%20a%20new%20ome-zarr%20container%0A%20%20%20%202.%20**Maximum%20Intensity%20Projection**%20%E2%80%94%20collapse%20the%20Z%20stack%20into%20a%202-D%20image.%0A%20%20%20%203.%20**Basic%20segmentation**%20%E2%80%94%20detect%20each%20nucleus.%0A%20%20%20%204.%20**Feature%20extraction**%20%E2%80%94%20measure%20size%2C%20intensity%2C%20shape%20per%20object.%0A%20%20%20%205.%20**Interactive%20feature%20exploration**%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(ngio%2C%20np%2C%20plt)%3A%0A%20%20%20%20from%20matplotlib%20import%20colors%20as%20_colors%0A%20%20%20%20from%20matplotlib.patches%20import%20Rectangle%20as%20_Rectangle%0A%0A%20%20%20%20_BVC_TEAL%20%3D%20%22%233DBDB8%22%0A%20%20%20%20_cycled%20%3D%20np.random.rand(1000%2C%203)%0A%20%20%20%20_cycled%5B0%5D%20%3D%20%5B0%2C%200%2C%200%5D%20%20%23%20background%20is%20always%20black%0A%20%20%20%20_LABEL_CMAP%20%3D%20_colors.ListedColormap(_cycled)%0A%0A%0A%20%20%20%20def%20_add_scale_bar(ax%2C%20pixel_size_um%2C%20length_um%3D50.0)%3A%0A%20%20%20%20%20%20%20%20bar_px%20%3D%20length_um%20%2F%20pixel_size_um%0A%20%20%20%20%20%20%20%20x_max%20%3D%20ax.get_xlim()%5B1%5D%0A%20%20%20%20%20%20%20%20y_max%20%3D%20ax.get_ylim()%5B0%5D%20%20%23%20imshow%20origin%3D'upper'%3A%20bottom%20%3D%3D%20max%0A%20%20%20%20%20%20%20%20pad_x%20%3D%200.04%20*%20x_max%0A%20%20%20%20%20%20%20%20pad_y%20%3D%200.04%20*%20y_max%0A%20%20%20%20%20%20%20%20x0%20%3D%20x_max%20-%20pad_x%20-%20bar_px%0A%20%20%20%20%20%20%20%20y0%20%3D%20y_max%20-%20pad_y%0A%20%20%20%20%20%20%20%20ax.plot(%0A%20%20%20%20%20%20%20%20%20%20%20%20%5Bx0%2C%20x0%20%2B%20bar_px%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%5By0%2C%20y0%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22white%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20linewidth%3D3%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20solid_capstyle%3D%22butt%22%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20ax.text(%0A%20%20%20%20%20%20%20%20%20%20%20%20x0%20%2B%20bar_px%20%2F%202%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y0%20-%200.015%20*%20y_max%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20f%22%7Blength_um%3Ag%7D%20%C2%B5m%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20color%3D%22white%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20ha%3D%22center%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20va%3D%22bottom%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20fontsize%3D9%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20fontweight%3D%22bold%22%2C%0A%20%20%20%20%20%20%20%20)%0A%0A%0A%20%20%20%20def%20plot_image(%0A%20%20%20%20%20%20%20%20ome_zarr%2C%0A%20%20%20%20%20%20%20%20path%3D%220%22%2C%0A%20%20%20%20%20%20%20%20title%3D%22%22%2C%0A%20%20%20%20%20%20%20%20label%3DNone%2C%0A%20%20%20%20%20%20%20%20roi_table%3DNone%2C%0A%20%20%20%20%20%20%20%20figsize%3D(8%2C%208)%2C%0A%20%20%20%20%20%20%20%20scale_bar_um%3D50.0%2C%0A%20%20%20%20%20%20%20%20**kwargs%2C%0A%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20image%20%3D%20ome_zarr.get_image(path%3Dpath)%0A%20%20%20%20%20%20%20%20img%20%3D%20image.get_as_numpy(**kwargs)%0A%0A%20%20%20%20%20%20%20%20fig%2C%20ax%20%3D%20plt.subplots(figsize%3Dfigsize)%0A%20%20%20%20%20%20%20%20ax.imshow(img%2C%20cmap%3D%22gray%22)%0A%20%20%20%20%20%20%20%20ax.set_title(title)%0A%20%20%20%20%20%20%20%20ax.axis(%22off%22)%0A%0A%20%20%20%20%20%20%20%20if%20label%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20lbl%20%3D%20ome_zarr.get_label(label%2C%20path%3Dpath)%0A%20%20%20%20%20%20%20%20%20%20%20%20label_kwargs%20%3D%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20k%3A%20v%20for%20k%2C%20v%20in%20kwargs.items()%20if%20k%20!%3D%20%22channel_selection%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%20%20%20%20lbl_img%20%3D%20lbl.get_as_numpy(**label_kwargs)%0A%20%20%20%20%20%20%20%20%20%20%20%20ax.imshow(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20lbl_img%2C%20cmap%3D_LABEL_CMAP%2C%20interpolation%3D%22nearest%22%2C%20alpha%3D0.5%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20if%20roi_table%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20table%20%3D%20ome_zarr.get_generic_roi_table(roi_table)%0A%20%20%20%20%20%20%20%20%20%20%20%20for%20roi%20in%20table.rois()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20px_roi%20%3D%20roi.to_pixel(pixel_size%3Dimage.pixel_size)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xs%2C%20ys%20%3D%20px_roi.get(%22x%22)%2C%20px_roi.get(%22y%22)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xs%20is%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20or%20ys%20is%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20or%20xs.start%20is%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20or%20ys.start%20is%20None%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20continue%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ax.add_patch(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_Rectangle(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(xs.start%2C%20ys.start)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20xs.length%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ys.length%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20fill%3DFalse%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20edgecolor%3D_BVC_TEAL%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20linewidth%3D0.8%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20if%20scale_bar_um%20is%20not%20None%20and%20getattr(image.pixel_size%2C%20%22x%22%2C%20None)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_add_scale_bar(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ax%2C%20pixel_size_um%3Dimage.pixel_size.x%2C%20length_um%3Dscale_bar_um%0A%20%20%20%20%20%20%20%20%20%20%20%20)%0A%0A%20%20%20%20%20%20%20%20fig.tight_layout()%0A%20%20%20%20%20%20%20%20plt.show()%0A%0A%0A%20%20%20%20def%20compare_containers(%0A%20%20%20%20%20%20%20%20orig%3A%20ngio.OmeZarrContainer%2C%20deriv%3A%20ngio.OmeZarrContainer%0A%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20attrs%20%3D%20(%22levels%22%2C%20%22level_paths%22%2C%20%22is_3d%22%2C%20%22channel_labels%22)%0A%20%20%20%20%20%20%20%20rows%20%3D%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%7C%20Attribute%20%7C%20Original%20%7C%20Derived%20%7C%20%20%7C%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%22%7C---%7C---%7C---%7C---%3A%7C%22%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20for%20attr%20in%20attrs%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20o%2C%20d%20%3D%20getattr(orig%2C%20attr)%2C%20getattr(deriv%2C%20attr)%0A%20%20%20%20%20%20%20%20%20%20%20%20marker%20%3D%20%22%E2%9C%85%20same%22%20if%20o%20%3D%3D%20d%20else%20%22%F0%9F%94%84%20changed%22%0A%20%20%20%20%20%20%20%20%20%20%20%20rows.append(f%22%7C%20%60%7Battr%7D%60%20%7C%20%60%7Bo%7D%60%20%7C%20%60%7Bd%7D%60%20%7C%20%7Bmarker%7D%20%7C%22)%0A%20%20%20%20%20%20%20%20orig_img%20%3D%20orig.get_image()%0A%20%20%20%20%20%20%20%20deriv_img%20%3D%20deriv.get_image()%0A%20%20%20%20%20%20%20%20for%20attr%20in%20(%22dimensions%22%2C%20%22shape%22%2C%20%22axes%22%2C%20%22dtype%22)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20o%2C%20d%20%3D%20getattr(orig_img%2C%20attr)%2C%20getattr(deriv_img%2C%20attr)%0A%20%20%20%20%20%20%20%20%20%20%20%20marker%20%3D%20%22%E2%9C%85%20same%22%20if%20o%20%3D%3D%20d%20else%20%22%F0%9F%94%84%20changed%22%0A%20%20%20%20%20%20%20%20%20%20%20%20rows.append(f%22%7C%20%60image.%7Battr%7D%60%20%7C%20%60%7Bo%7D%60%20%7C%20%60%7Bd%7D%60%20%7C%20%7Bmarker%7D%20%7C%22)%0A%0A%20%20%20%20%20%20%20%20o%20%3D%20orig_img.meta.version%0A%20%20%20%20%20%20%20%20d%20%3D%20deriv.meta.version%0A%20%20%20%20%20%20%20%20marker%20%3D%20%22%E2%9C%85%20same%22%20if%20o%20%3D%3D%20d%20else%20%22%F0%9F%94%84%20changed%22%0A%20%20%20%20%20%20%20%20rows.append(f%22%7C%20%60image.meta.version%60%20%7C%20%60%7Bo%7D%60%20%7C%20%60%7Bd%7D%60%20%7C%20%7Bmarker%7D%20%7C%22)%0A%0A%20%20%20%20%20%20%20%20o%20%3D%20orig_img.zarr_array.metadata.zarr_format%0A%20%20%20%20%20%20%20%20d%20%3D%20deriv_img.zarr_array.metadata.zarr_format%0A%20%20%20%20%20%20%20%20marker%20%3D%20%22%E2%9C%85%20same%22%20if%20o%20%3D%3D%20d%20else%20%22%F0%9F%94%84%20changed%22%0A%20%20%20%20%20%20%20%20rows.append(f%22%7C%20%60zarr_version%60%20%7C%20%60%7Bo%7D%60%20%7C%20%60%7Bd%7D%60%20%7C%20%7Bmarker%7D%20%7C%22)%0A%20%20%20%20%20%20%20%20return%20%22%5Cn%22.join(rows)%0A%0A%20%20%20%20return%20compare_containers%2C%20plot_image%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%204.1%20Derive%20a%20new%20container%0A%0A%20%20%20%20We%20will%20create%20a%20new%20OME-Zarr%20container%20that%20holds%20the%20Maximum%20Intensity%20Projection.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(data_dir%2C%20image%2C%20ome_zarr_container)%3A%0A%20%20%20%20%23%20Source%20axes%20are%20(c%2C%20z%2C%20y%2C%20x).%20The%20derived%20MIP%20keeps%20the%20same%20layout%0A%20%20%20%20%23%20but%20reduces%20both%20c%20and%20z%20to%20length%201%20(we%20only%20keep%20DAPI-MIP%20and%0A%20%20%20%20%23%20collapse%20Z).%20YX%20is%20taken%20from%20the%20source%20so%20it%20stays%20in%20lockstep%0A%20%20%20%20%23%20with%20the%20sample%20dataset.%0A%20%20%20%20_y%2C%20_x%20%3D%20image.shape%5B-2%5D%2C%20image.shape%5B-1%5D%0A%20%20%20%20derived_ome_zarr%20%3D%20ome_zarr_container.derive_image(%0A%20%20%20%20%20%20%20%20data_dir%20%2F%20%22derived.zarr%22%2C%0A%20%20%20%20%20%20%20%20shape%3D(1%2C%201%2C%20_y%2C%20_x)%2C%0A%20%20%20%20%20%20%20%20channels_meta%3D%5B%22DAPI-MIP%22%5D%2C%0A%20%20%20%20%20%20%20%20ngff_version%3D%220.5%22%2C%0A%20%20%20%20%20%20%20%20overwrite%3DTrue%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(derived_ome_zarr%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(compare_containers%2C%20derived_ome_zarr%2C%20mo%2C%20ome_zarr_container)%3A%0A%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%22**Original%20vs%20derived%20container.**%20The%20derived%20container%20keeps%20the%20%22%0A%20%20%20%20%20%20%20%20%22multiscale%20%2B%20channel%20structure%20of%20the%20source%20but%20updates%20shape%2C%20%22%0A%20%20%20%20%20%20%20%20%22channel%20labels%2C%20and%20NGFF%20version%3A%5Cn%5Cn%22%0A%20%20%20%20%20%20%20%20%2B%20compare_containers(ome_zarr_container%2C%20derived_ome_zarr)%0A%20%20%20%20)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%204.2%20Maximum%20Intensity%20Projection%0A%0A%20%20%20%20We%20project%20the%20source%20image%20along%20the%20Z%20axis%20to%20get%20a%20single%202-D%0A%20%20%20%20image%20per%20channel%2C%20then%20write%20the%20result%20back%20into%20our%20derived%0A%20%20%20%20container.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(derived_ome_zarr%2C%20ome_zarr_container%2C%20plot_image)%3A%0A%20%20%20%20image_origin%20%3D%20ome_zarr_container.get_image()%0A%20%20%20%20image_data_lazy%20%3D%20image_origin.get_as_dask()%20%20%23%20axes%3A%20(c%2C%20z%2C%20y%2C%20x)%0A%0A%20%20%20%20%23%20Max%20intensity%20projection%20on%20the%20fly%20with%20dask%2C%20without%20loading%20the%0A%20%20%20%20%23%20whole%20image%20into%20RAM.%20%60axis%3D1%60%20is%20Z%20(axes%20are%20CZYX%2C%20so%20axis%200%20%3D%20C%2C%0A%20%20%20%20%23%20axis%201%20%3D%20Z).%0A%20%20%20%20image_data_lazy_mip%20%3D%20image_data_lazy.max(axis%3D1)%0A%0A%20%20%20%20%23%20%60set_array%60%20requires%20the%20destination%20axis%20layout%20(C%2C%20Z%2C%20Y%2C%20X)%20%E2%80%94%20we%0A%20%20%20%20%23%20collapsed%20Z%20but%20the%20derived%20container%20still%20has%20a%20length-1%20Z%20axis%2C%0A%20%20%20%20%23%20so%20re-introduce%20a%20singleton%20axis%20at%20position%201.%0A%20%20%20%20image_data_lazy_mip%20%3D%20image_data_lazy_mip%5B%3A%2C%20None%2C%20...%5D%0A%0A%20%20%20%20derived_image%20%3D%20derived_ome_zarr.get_image()%0A%20%20%20%20derived_image.set_array(image_data_lazy_mip)%0A%20%20%20%20derived_image.consolidate()%20%20%23%20build%20the%20pyramid%20levels%0A%0A%20%20%20%20plot_image(%0A%20%20%20%20%20%20%20%20derived_ome_zarr%2C%0A%20%20%20%20%20%20%20%20title%3D%22Maximum%20Intensity%20Projection%20(derived%20image%2C%20level%200)%22%2C%0A%20%20%20%20%20%20%20%20axes_order%3D%22yx%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(derived_image%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%204.3%20Basic%20segmentation%0A%0A%20%20%20%20%60basic_segmentation%60%20chains%20a%20small%20set%20of%20%60scikit-image%60%20primitives.%0A%20%20%20%20The%20five%20steps%2C%20in%20plain%20language%3A%0A%0A%20%20%20%201.%20**Smooth**%20the%20image%20with%20a%20Gaussian%20filter%20to%20suppress%20noise.%0A%20%20%20%202.%20**Threshold**%20with%20Otsu's%20method%20to%20get%20a%20foreground%20mask.%0A%20%20%20%203.%20**Find%20object%20centres**%20via%20the%20distance%20transform%20%2B%20local-peak%0A%20%20%20%20%20%20%20detection.%0A%20%20%20%204.%20**Watershed**%20from%20those%20centres%20to%20split%20touching%20nuclei.%0A%20%20%20%205.%20**Clean%20up**%20by%20dropping%20objects%20below%20500%20pixels.%0A%0A%20%20%20%20We%20then%20write%20the%20resulting%20label%20image%20back%20to%20the%20container%20as%0A%20%20%20%20%60basic_segmentation%60%2C%20and%20build%20a%20%60MaskingRoiTable%60%20from%20it%20%E2%80%94%20one%0A%20%20%20%20bounding%20box%20per%20nucleus%2C%20which%20we%20will%20reuse%20in%20%C2%A74.4.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(derived_image%2C%20derived_ome_zarr%2C%20np%2C%20plot_image)%3A%0A%20%20%20%20from%20scipy%20import%20ndimage%20as%20ndi%0A%20%20%20%20from%20skimage.feature%20import%20peak_local_max%0A%20%20%20%20from%20skimage.filters%20import%20threshold_otsu%0A%20%20%20%20from%20skimage.morphology%20import%20remove_small_objects%0A%20%20%20%20from%20skimage.segmentation%20import%20watershed%0A%0A%0A%20%20%20%20def%20basic_segmentation(image_data)%3A%0A%20%20%20%20%20%20%20%20%23%20Smooth%20%E2%86%92%20Otsu%20threshold%20%E2%86%92%20distance%20transform%20%E2%86%92%20seeded%20watershed%20%E2%86%92%20cleanup%0A%20%20%20%20%20%20%20%20image_data%20%3D%20ndi.gaussian_filter(image_data%2C%20sigma%3D4)%0A%20%20%20%20%20%20%20%20mask%20%3D%20image_data%20%3E%20threshold_otsu(image_data)%0A%20%20%20%20%20%20%20%20distance%20%3D%20ndi.distance_transform_edt(mask)%0A%20%20%20%20%20%20%20%20coords%20%3D%20peak_local_max(distance%2C%20min_distance%3D20%2C%20labels%3Dmask)%0A%20%20%20%20%20%20%20%20markers%20%3D%20np.zeros(distance.shape%2C%20dtype%3Dnp.int32)%0A%20%20%20%20%20%20%20%20markers%5Btuple(coords.T)%5D%20%3D%20np.arange(1%2C%20len(coords)%20%2B%201)%0A%20%20%20%20%20%20%20%20seg%20%3D%20watershed(-distance%2C%20markers%2C%20mask%3Dmask).astype(np.uint16)%0A%20%20%20%20%20%20%20%20seg%20%3D%20remove_small_objects(seg%2C%20max_size%3D500)%0A%20%20%20%20%20%20%20%20return%20seg%0A%0A%0A%20%20%20%20image_data%20%3D%20derived_image.get_as_numpy(%0A%20%20%20%20%20%20%20%20channel_selection%3D%22DAPI-MIP%22%2C%20axes_order%3D%22yx%22%0A%20%20%20%20)%0A%20%20%20%20segmentation_mask%20%3D%20basic_segmentation(image_data)%0A%0A%20%20%20%20%23%20Derive%20a%20new%20label%20and%20write%20the%20segmentation%20mask%20back%20to%20the%20OME-Zarr%0A%20%20%20%20derived_label%20%3D%20derived_ome_zarr.derive_label(%0A%20%20%20%20%20%20%20%20%22basic_segmentation%22%2C%20overwrite%3DTrue%0A%20%20%20%20)%0A%20%20%20%20derived_label.set_array(segmentation_mask%2C%20axes_order%3D%22yx%22)%0A%20%20%20%20derived_label.consolidate()%0A%0A%20%20%20%20plot_image(%0A%20%20%20%20%20%20%20%20derived_ome_zarr%2C%0A%20%20%20%20%20%20%20%20title%3D%22Basic%20segmentation%20(derived%20label%2C%20level%200)%22%2C%0A%20%20%20%20%20%20%20%20label%3D%22basic_segmentation%22%2C%0A%20%20%20%20%20%20%20%20axes_order%3D%22yx%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Build%20one%20bounding%20ROI%20per%20labelled%20nucleus%20and%20store%20it%20on%20the%20container.%0A%20%20%20%20roi_table%20%3D%20derived_ome_zarr.build_masking_roi_table(%0A%20%20%20%20%20%20%20%20label%3D%22basic_segmentation%22%0A%20%20%20%20)%0A%20%20%20%20derived_ome_zarr.add_table(%0A%20%20%20%20%20%20%20%20name%3D%22basic_segmentation_ROI_table%22%2C%0A%20%20%20%20%20%20%20%20table%3Droi_table%2C%0A%20%20%20%20%20%20%20%20overwrite%3DTrue%2C%0A%20%20%20%20%20%20%20%20backend%3D%22csv%22%2C%0A%20%20%20%20)%0A%0A%20%20%20%20plot_image(%0A%20%20%20%20%20%20%20%20derived_ome_zarr%2C%0A%20%20%20%20%20%20%20%20title%3D%22Per-object%20ROIs%20from%20the%20masking%20ROI%20table%22%2C%0A%20%20%20%20%20%20%20%20roi_table%3D%22basic_segmentation_ROI_table%22%2C%0A%20%20%20%20%20%20%20%20axes_order%3D%22yx%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(derived_label%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%204.4%20Feature%20extraction%0A%0A%20%20%20%20For%20each%20segmented%20nucleus%20we%20measure%20a%20handful%20of%20region%20properties%0A%20%20%20%20with%20%60scikit-image.regionprops_table%60%2C%20and%20store%20them%20as%20a%20typed%0A%20%20%20%20%60FeatureTable%60%20on%20the%20container.%20The%20%60reference_label%60%20argument%20links%0A%20%20%20%20back%20to%20the%20label%20image%20from%20which%20the%20measurements%20come%20from.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(derived_ome_zarr)%3A%0A%20%20%20%20%23%20List%20what's%20already%20on%20the%20container%20%E2%80%94%20we%20will%20add%20to%20this.%0A%20%20%20%20derived_ome_zarr.list_tables()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(FeatureTable%2C%20derived_image%2C%20derived_label%2C%20derived_ome_zarr%2C%20np%2C%20pd)%3A%0A%20%20%20%20from%20skimage.measure%20import%20regionprops_table%0A%0A%0A%20%20%20%20def%20compute_region_props(%0A%20%20%20%20%20%20%20%20segmentation_mask%3A%20np.ndarray%2C%20image_data%3A%20np.ndarray%0A%20%20%20%20)%3A%0A%20%20%20%20%20%20%20%20props%20%3D%20regionprops_table(%0A%20%20%20%20%20%20%20%20%20%20%20%20segmentation_mask%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20intensity_image%3Dimage_data%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20properties%3D(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22label%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22area%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22mean_intensity%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22max_intensity%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22min_intensity%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22centroid%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22eccentricity%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22solidity%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20return%20pd.DataFrame(props)%0A%0A%0A%20%20%20%20segmentation_mask2%20%3D%20derived_label.get_as_numpy(axes_order%3D%22yx%22)%0A%20%20%20%20image_data2%20%3D%20derived_image.get_as_numpy(%0A%20%20%20%20%20%20%20%20channel_selection%3D%22DAPI-MIP%22%2C%20axes_order%3D%22yx%22%0A%20%20%20%20)%0A%20%20%20%20feature_df%20%3D%20compute_region_props(segmentation_mask2%2C%20image_data2)%0A%0A%20%20%20%20feature_table%20%3D%20FeatureTable(feature_df%2C%20reference_label%3D%22basic_segmentation%22)%0A%20%20%20%20derived_ome_zarr.add_table(%0A%20%20%20%20%20%20%20%20name%3D%22basic_segmentation_features%22%2C%0A%20%20%20%20%20%20%20%20table%3Dfeature_table%2C%0A%20%20%20%20%20%20%20%20backend%3D%22csv%22%2C%0A%20%20%20%20%20%20%20%20overwrite%3DTrue%2C%0A%20%20%20%20)%0A%0A%20%20%20%20%23%20Only%20for%20marimo%20display%0A%20%20%20%20feature_table.lazy_frame.collect()%20%20%23%20compute%20the%20table%20(if%20there%20are%20lazy%20computations)%0A%20%20%20%20return%20(feature_df%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%23%204.5%20Interactive%20feature%20exploration%0A%0A%20%20%20%20Pick%20any%20two%20numeric%20columns%20to%20scatter%20the%20per-object%20features.%0A%20%20%20%20**Click%20a%20single%20point**%20in%20the%20scatter%20on%20the%20left%20to%20render%20the%0A%20%20%20%20matching%20segmented%20object%20on%20the%20right%20(use%20the%20%60Zoom%60%20slider%20to%0A%20%20%20%20widen%20the%20field%20of%20view%20around%20it).%20The%20selected%20object's%0A%20%20%20%20segmentation%20boundary%20is%20outlined%20in%20BVC%20teal.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(derived_ome_zarr)%3A%0A%20%20%20%20derived_image_plot%20%3D%20derived_ome_zarr.get_image()%0A%20%20%20%20derived_label_plot%20%3D%20derived_ome_zarr.get_label(%22basic_segmentation%22)%0A%0A%20%20%20%20feature_table_plot%20%3D%20derived_ome_zarr.get_feature_table(%0A%20%20%20%20%20%20%20%20%22basic_segmentation_features%22%0A%20%20%20%20)%0A%20%20%20%20masking_roi_table_plot%20%3D%20derived_ome_zarr.get_masking_roi_table(%0A%20%20%20%20%20%20%20%20%22basic_segmentation_ROI_table%22%0A%20%20%20%20)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20derived_image_plot%2C%0A%20%20%20%20%20%20%20%20derived_label_plot%2C%0A%20%20%20%20%20%20%20%20feature_table_plot%2C%0A%20%20%20%20%20%20%20%20masking_roi_table_plot%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(feature_df%2C%20mo)%3A%0A%20%20%20%20numeric_cols%20%3D%20list(feature_df.select_dtypes(%22number%22).columns)%0A%20%20%20%20x_axis%20%3D%20mo.ui.dropdown(numeric_cols%2C%20value%3D%22area%22%2C%20label%3D%22X%20axis%22)%0A%20%20%20%20y_axis%20%3D%20mo.ui.dropdown(numeric_cols%2C%20value%3D%22mean_intensity%22%2C%20label%3D%22Y%20axis%22)%0A%20%20%20%20zoom%20%3D%20mo.ui.slider(1.0%2C%2015.0%2C%20value%3D2.0%2C%20step%3D0.1%2C%20label%3D%22Zoom%20(preview)%22)%0A%20%20%20%20mo.hstack(%5Bx_axis%2C%20y_axis%2C%20zoom%5D%2C%20justify%3D%22start%22)%0A%20%20%20%20return%20x_axis%2C%20y_axis%2C%20zoom%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(feature_table_plot%2C%20mo%2C%20x_axis%2C%20y_axis)%3A%0A%20%20%20%20import%20altair%20as%20alt%0A%0A%20%20%20%20feature_df_alt%20%3D%20feature_table_plot.dataframe%0A%0A%20%20%20%20feature_chart%20%3D%20mo.ui.altair_chart(%0A%20%20%20%20%20%20%20%20alt.Chart(feature_df_alt)%0A%20%20%20%20%20%20%20%20.mark_circle(size%3D40%2C%20opacity%3D0.9%2C%20color%3D%22%233DBDB8%22)%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dalt.X(f%22%7Bx_axis.value%7D%3AQ%22%2C%20title%3Dx_axis.value)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dalt.Y(f%22%7By_axis.value%7D%3AQ%22%2C%20title%3Dy_axis.value)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20tooltip%3Dlist(feature_df_alt.columns)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.properties(width%3D700%2C%20height%3D400)%0A%20%20%20%20%20%20%20%20.interactive()%2C%0A%20%20%20%20%20%20%20%20chart_selection%3D%22point%22%2C%0A%20%20%20%20)%0A%20%20%20%20return%20(feature_chart%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20BVC_TEAL%2C%0A%20%20%20%20derived_image_plot%2C%0A%20%20%20%20derived_label_plot%2C%0A%20%20%20%20feature_chart%2C%0A%20%20%20%20masking_roi_table_plot%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20plt%2C%0A%20%20%20%20zoom%2C%0A)%3A%0A%20%20%20%20_selection%20%3D%20feature_chart.value%0A%20%20%20%20_selection%20%3D%20(%0A%20%20%20%20%20%20%20%20_selection.reset_index()%0A%20%20%20%20%20%20%20%20if%20_selection%20is%20not%20None%20and%20len(_selection)%0A%20%20%20%20%20%20%20%20else%20None%0A%20%20%20%20)%0A%20%20%20%20_selected_labels%20%3D%20(%0A%20%20%20%20%20%20%20%20_selection%5B%22label%22%5D.tolist()%0A%20%20%20%20%20%20%20%20if%20_selection%20is%20not%20None%20and%20len(_selection)%0A%20%20%20%20%20%20%20%20else%20%5B%5D%0A%20%20%20%20)%0A%0A%20%20%20%20if%20len(_selected_labels)%20!%3D%201%3A%0A%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%22No%20point%20or%20multiple%20points%20selected.%20Please%20select%20a%20single%20point%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%22on%20the%20scatter%20plot%20to%20preview%20the%20corresponding%20segmented%20object.%22%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20_selected_labels%20%3D%20%5B1%5D%0A%0A%20%20%20%20_lbl%20%3D%20_selected_labels%5B0%5D%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(6%2C%206))%0A%20%20%20%20_roi%20%3D%20masking_roi_table_plot.get_label(_lbl).zoom(zoom.value)%0A%20%20%20%20_img%20%3D%20derived_image_plot.get_roi_as_numpy(%0A%20%20%20%20%20%20%20%20_roi%2C%20channel_selection%3D%22DAPI-MIP%22%2C%20axes_order%3D%22yx%22%0A%20%20%20%20)%0A%20%20%20%20_mask%20%3D%20derived_label_plot.get_roi_as_numpy(_roi%2C%20axes_order%3D%22yx%22)%0A%20%20%20%20_ax.imshow(_img%2C%20cmap%3D%22gray%22)%0A%20%20%20%20_ax.contour(_mask%20%3D%3D%20_lbl%2C%20levels%3D%5B0.5%5D%2C%20colors%3DBVC_TEAL%2C%20linewidths%3D1.5)%0A%20%20%20%20_ax.set_title(f%22label%20%7B_lbl%7D%22)%0A%20%20%20%20_ax.axis(%22off%22)%0A%20%20%20%20_fig.tight_layout()%0A%20%20%20%20preview%20%3D%20_fig%0A%0A%20%20%20%20mo.hstack(%5Bfeature_chart%2C%20preview%5D%2C%20align%3D%22start%22%2C%20widths%3D%5B1%2C%201%5D)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%205%20Next%20steps%0A%0A%20%20%20%20That%20covers%20ngio's%20core%3A%20containers%2C%20images%2C%20labels%2C%20tables%2C%20and%20the%0A%20%20%20%20derive%20APIs%20that%20let%20you%20write%20a%20full%20pipeline%20back%20into%20a%20single%0A%20%20%20%20OME-Zarr%20file.%0A%0A%20%20%20%20**Next%3A**%20%5B%602_iterators.py%60%5D(.%2F2_iterators.py)%20covers%20ngio's%0A%20%20%20%20**iterators**%20%E2%80%94%20the%20pattern%20that%20lets%20you%20scale%20this%20same%20MIP%20%E2%86%92%0A%20%20%20%20segment%20%E2%86%92%20measure%20pipeline%20to%20images%20that%20don't%20fit%20in%20RAM.%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%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
66002548f2d37ecf12d8cc2e21fdd704