%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%20%20%20%20%22pandas%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%2002%3C%2Fdiv%3E%0A%0A%20%20%20%20%23%20Iterators%20in%20ngio%0A%0A%20%20%20%20%23%23%23%20Goals%20of%20this%20notebook%0A%20%20%20%20-%20Understand%20when%20ngio%20iterators%20are%20needed%20and%20what%20problems%20they%20solve.%0A%20%20%20%20-%20Learn%20how%20to%20set%20up%20and%20use%20a%20ngio%20iterator.%0A%20%20%20%20-%20Use%20%60SegmentationIterator%60%20to%20build%20an%20instance%20segmentation%2C%20both%20with%20the%0A%20%20%20%20%20%20stateless%20%60map_as_numpy%60%20API%20and%20the%20stateful%20%60iter_as_numpy%60%20loop.%0A%0A%20%20%20%20%3E%20**Note%3A**%20iterators%20in%20ngio%20are%20very%20new%20and%20the%20API%20surface%20may%20still%0A%20%20%20%20%3E%20shift%20between%20releases.%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%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%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%20(plt%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(plt)%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%20%20%20%20import%20numpy%20as%20np%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%20%20%20%20return%20np%2C%20plot_image%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20iter_builder_image%20%3D%20mo.image(%0A%20%20%20%20%20%20%20%20src%3D%22https%3A%2F%2Fraw.githubusercontent.com%2FBioVisionCenter%2Fngio-workshop%2Frefs%2Fheads%2Fmain%2Fnotebooks%2Fassets%2Fiterators-builder.png%22%0A%20%20%20%20)%0A%0A%20%20%20%20iter_run_graph%20%3D%20mo.mermaid(r%22%22%22%0A%20%20%20%20graph%20LR%0A%20%20%20%20%20%20%20%20%25%25%20%3D%3D%3D%20STYLE%20DEFINITIONS%20%3D%3D%3D%0A%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%20%20%20%20%20%20%20%20classDef%20imageStyle%20fill%3A%234A8FD4%2Cstroke%3A%232E5C8A%2Cstroke-width%3A2px%2Ccolor%3A%23fff%0A%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%25%25%20%3D%3D%3D%20GRAPH%20STRUCTURE%20%3D%3D%3D%0A%20%20%20%20%20%20%20%20READ%5B%22read_patch%22%5D%20--%3E%20FN%5B%22my_fn%22%5D%0A%20%20%20%20%20%20%20%20FN%20--%3E%20WRITE%5B%22write_patch%22%5D%0A%20%20%20%20%20%20%20%20WRITE%20--%3E%7Cnext%20patch%7C%20READ%0A%20%20%20%20%20%20%20%20WRITE%20--%3E%7Call%20patches%20done%7C%20CONS%5B%22consolidate%22%5D%0A%0A%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%20class%20READ%2CWRITE%20imageStyle%0A%20%20%20%20%20%20%20%20class%20FN%20multiscaleStyle%0A%20%20%20%20%20%20%20%20class%20CONS%20tableStyle%0A%20%20%20%20%22%22%22)%0A%0A%0A%20%20%20%20iter_t_run_graph%20%3D%20mo.mermaid(r%22%22%22%0A%20%20%20%20graph%20LR%0A%20%20%20%20%20%20%20%20%25%25%20%3D%3D%3D%20STYLE%20DEFINITIONS%20%3D%3D%3D%0A%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%20%20%20%20%20%20%20%20classDef%20imageStyle%20fill%3A%234A8FD4%2Cstroke%3A%232E5C8A%2Cstroke-width%3A2px%2Ccolor%3A%23fff%0A%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%25%25%20%3D%3D%3D%20GRAPH%20STRUCTURE%20%3D%3D%3D%0A%20%20%20%20%20%20%20%20READ%5B%22read_patch%22%5D%20--%3E%20TF1%5BIn-Transform%5D%0A%20%20%20%20%20%20%20%20TF1%20--%3E%20TF2%5B...%5D%0A%20%20%20%20%20%20%20%20TF2%20--%3E%20FN%5B%22my_fn%22%5D%0A%20%20%20%20%20%20%20%20FN%20--%3E%20TF3%5B...%5D%0A%20%20%20%20%20%20%20%20TF3%20--%3E%20TF4%5BOut-Transform%5D%0A%20%20%20%20%20%20%20%20TF4%20--%3E%20WRITE%5B%22write_patch%22%5D%0A%20%20%20%20%20%20%20%20WRITE%20--%3E%7Cnext%20patch%7C%20READ%0A%0A%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%20class%20READ%2CWRITE%20imageStyle%0A%20%20%20%20%20%20%20%20class%20FN%20multiscaleStyle%0A%20%20%20%20%20%20%20%20class%20CONS%20tableStyle%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%20iter_builder_image%2C%20iter_run_graph%2C%20iter_t_run_graph%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(iter_builder_image%2C%20iter_run_graph%2C%20mo)%3A%0A%20%20%20%20mo.md(rf%22%22%22%0A%20%20%20%20%23%23%201%20Why%20iterators%3F%0A%0A%20%20%20%20-%20**Reusable%20processing%20units**%20%E2%80%94%20make%20it%20very%20simple%20to%20write%20image%20processing%20routine%20that%20generalize.%0A%20%20%20%20-%20**Larger-than-RAM%20images**%20%E2%80%94%20process%20the%20images%20in%20small%20patches.%0A%20%20%20%20-%20**(Future)%20Free%20optimization**%20%E2%80%94%20parallelization%2C%20efficient%20read%2Fwrite.%0A%0A%20%20%20%20%3E%20**Warning!**%20Iterators%20are%20very%20much%20work%20in%20progress!%0A%0A%20%20%20%20%23%23%23%201.1%20Two%20phases%0A%0A%20%20%20%20An%20iterator%20has%20two%20phases%20%E2%80%94%20a%20**builder%20phase**%20where%20you%20declare%20*what*%0A%20%20%20%20to%20iterate%20over%2C%20and%20an%20**execution%20phase**%20where%20the%20actual%20processing%0A%20%20%20%20function%20runs%20patch%20by%20patch.%0A%0A%20%20%20%20%23%23%23%201.2%20Builder%20phase%0A%0A%20%20%20%20%7Biter_builder_image%7D%0A%0A%20%20%20%20*The%20schematic%20above%20shows%20how%20a%20builder%20is%20composed%3A%20you%20start%20from%20an%0A%20%20%20%20input%20image%20(and%2C%20for%20write-back%20iterators%2C%20an%20output%20image%20or%20label)%2C%0A%20%20%20%20then%20chain%20region%20selectors%20(%60.product%60%2C%20%60.by_yx%60%2C%20%60.by_zyx%60%2C%20%E2%80%A6)%20to%0A%20%20%20%20decide%20which%20patches%20the%20execution%20phase%20will%20visit.*%0A%0A%20%20%20%20Instantiate%20an%20iterator%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20iterator%20%3D%20SomeIterator(input_image%3D...%2C%20output%3D...)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Build%20the%20regions%20it%20will%20visit%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20iterator%20%3D%20(%0A%20%20%20%20%20%20%20%20iterator%0A%20%20%20%20%20%20%20%20.product(roi_table)%20%20%20%23%20one%20patch%20per%20ROI%20in%20roi_table%20(e.g.%20one%20per%20FOV)%0A%20%20%20%20%20%20%20%20.by_zyx()%20%20%20%20%20%20%20%20%20%20%20%20%20%23%20for%20each%20ROI%2C%20hand%20out%20a%203-D%20ZYX%20block%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%20%20%20%20%20%20%23%20%20%20(use%20.by_yx()%20for%202-D%20YX%20patches%20instead)%0A%20%20%20%20)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20%60.product(...)%60%20restricts%20the%20iteration%20to%20a%20list%20of%20spatial%20regions%0A%20%20%20%20(any%20%60RoiTable%60%20or%20list%20of%20%60Roi%60%20objects).%20%60.by_yx()%60%20%2F%20%60.by_zyx()%60%20then%0A%20%20%20%20declares%20the%20**patch%20shape**%20the%20user%20code%20will%20receive%20%E2%80%94%20broadcasting%0A%20%20%20%20over%20the%20remaining%20axes%20(time%2C%20z%2C%20%E2%80%A6).%0A%0A%20%20%20%20%23%23%23%201.3%20Execution%20phase%0A%0A%20%20%20%20%7Biter_run_graph%7D%0A%0A%20%20%20%20Run%20**stateless**%20%E2%80%94%20apply%20a%20pure%20function%20to%20every%20patch.%20ngio%20handles%0A%20%20%20%20reading%2C%20applying%2C%20and%20writing%20back%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20iterator.map_as_numpy(my_fn)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Run%20**stateful**%20%E2%80%94%20drive%20the%20loop%20yourself%20when%20you%20need%20state%20across%0A%20%20%20%20patches%20(e.g.%20a%20running%20counter)%2C%20and%20call%20the%20writer%20manually%3A%0A%0A%20%20%20%20%60%60%60python%0A%20%20%20%20for%20patch%2C%20writer%20in%20iterator.iter_as_numpy()%3A%0A%20%20%20%20%20%20%20%20result%20%3D%20my_stateful_fn(patch)%0A%20%20%20%20%20%20%20%20writer(patch%3Dresult)%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20%23%23%23%201.4%20Built-in%20iterators%0A%0A%20%20%20%20ngio%20ships%20four%20iterator%20types.%20This%20notebook%20focuses%20on%0A%20%20%20%20%60SegmentationIterator%60%3B%20the%20other%20three%20follow%20the%20same%20builder%20%2F%0A%20%20%20%20execution%20pattern.%0A%0A%20%20%20%20%7C%20Iterator%20%7C%20Use%20case%20%7C%0A%20%20%20%20%7C---%7C---%7C%0A%20%20%20%20%7C%20%60ImageProcessingIterator%60%20%7C%20Image-to-image%20operations%20(filtering%2C%20intensity%20correction%2C%20%E2%80%A6)%20%7C%0A%20%20%20%20%7C%20%60SegmentationIterator%60%20%7C%20Image-to-label%20operations%20(segmentation)%20%7C%0A%20%20%20%20%7C%20%60MaskedSegmentationIterator%60%20%7C%20Image-to-label%20operations%20restricted%20to%20a%20masking%20ROI%20table%20%7C%0A%20%20%20%20%7C%20%60FeatureExtractorIterator%60%20%7C%20Read-only%20per-object%20measurements%20and%20aggregation%20%7C%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%20use%20the%20%60CardiomyocyteTinyMip%60%20sample%20dataset%20%E2%80%94%20a%20single%20well%20image%0A%20%20%20%20with%20one%20DAPI%20channel%2C%20one%20Z-plane%20(MIP)%2C%20and%20an%20%60FOV_ROI_table%60.%20The%0A%20%20%20%20FOV%20ROI%20table%20is%20one%20entry%20per%20microscope%20field%20of%20view%3B%20we%20will%20use%20it%0A%20%20%20%20as%20the%20default%20iteration%20grid%20when%20no%20custom%20ROIs%20are%20drawn.%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%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%22CardiomyocyteTinyMip%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%20%3D%20ngio.open_ome_zarr_container(image_path)%0A%0A%20%20%20%20image%20%3D%20ome_zarr.get_image()%0A%20%20%20%20fov_table%20%3D%20ome_zarr.get_roi_table(%22FOV_ROI_table%22)%0A%20%20%20%20return%20fov_table%2C%20image%2C%20ngio%2C%20ome_zarr%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(fov_table%2C%20image%2C%20mo)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20**Sample%20image%20opened.**%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%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%60channel_labels%60%20%7C%20%60%7Bimage.channel_labels%7D%60%20%7C%0A%20%20%20%20%7C%20%60FOV_ROI_table%60%20regions%20%7C%20%60%7B%5Br.name%20for%20r%20in%20fov_table.rois()%5D%7D%60%20%7C%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%20SegmentationIterator%20%E2%80%94%20stateless%20%60map_as_numpy%60%0A%0A%20%20%20%20We%20will%20run%20the%20same%20%60basic_segmentation%60%20function%20used%20in%20notebook%201%0A%20%20%20%20over%20a%20%60SegmentationIterator%60.%20First%20we%20let%20the%20user%20pick%20the%20ROIs%20the%0A%20%20%20%20iterator%20should%20visit%2C%20then%20we%20hand%20the%20function%20to%20%60map_as_numpy(...)%60.%0A%0A%20%20%20%20%23%23%23%203.1%20Draw%20your%20own%20ROIs%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(np)%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%20%20%20%20return%20(basic_segmentation%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(image%2C%20mo)%3A%0A%20%20%20%20import%20base64%0A%20%20%20%20from%20io%20import%20BytesIO%0A%0A%20%20%20%20import%20altair%20as%20alt%0A%20%20%20%20import%20pandas%20as%20pd%0A%20%20%20%20from%20matplotlib%20import%20image%20as%20mpimg%0A%0A%20%20%20%20iter_raw%20%3D%20image.get_as_numpy(axes_order%3D%5B%22y%22%2C%20%22x%22%5D%2C%20c%3D0%2C%20z%3D0)%0A%20%20%20%20iter_H%2C%20iter_W%20%3D%20iter_raw.shape%0A%0A%20%20%20%20_ds%20%3D%20max(1%2C%20max(iter_H%2C%20iter_W)%20%2F%2F%201280)%0A%20%20%20%20_png_buf%20%3D%20BytesIO()%0A%20%20%20%20mpimg.imsave(_png_buf%2C%20iter_raw%5B%3A%3A_ds%2C%20%3A%3A_ds%5D%2C%20cmap%3D%22gray%22%2C%20format%3D%22png%22)%0A%20%20%20%20_data_url%20%3D%20(%0A%20%20%20%20%20%20%20%20%22data%3Aimage%2Fpng%3Bbase64%2C%22%20%2B%20base64.b64encode(_png_buf.getvalue()).decode()%0A%20%20%20%20)%0A%0A%20%20%20%20_chart_w%20%3D%20720%0A%20%20%20%20_chart_h%20%3D%20max(120%2C%20int(_chart_w%20*%20iter_H%20%2F%20iter_W))%0A%0A%20%20%20%20_scale_x%20%3D%20alt.Scale(domain%3D%5B0%2C%20iter_W%5D%2C%20nice%3DFalse)%0A%20%20%20%20_scale_y%20%3D%20alt.Scale(domain%3D%5B0%2C%20iter_H%5D%2C%20reverse%3DTrue%2C%20nice%3DFalse)%0A%0A%20%20%20%20_img_layer%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(pd.DataFrame(%5B%7B%22url%22%3A%20_data_url%2C%20%22x%22%3A%200%2C%20%22y%22%3A%200%7D%5D))%0A%20%20%20%20%20%20%20%20.mark_image(width%3D_chart_w%2C%20height%3D_chart_h%2C%20align%3D%22left%22%2C%20baseline%3D%22top%22)%0A%20%20%20%20%20%20%20%20.encode(%0A%20%20%20%20%20%20%20%20%20%20%20%20url%3D%22url%3AN%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20x%3Dalt.X(%22x%3AQ%22%2C%20scale%3D_scale_x%2C%20title%3D%22x%20(px)%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dalt.Y(%22y%3AQ%22%2C%20scale%3D_scale_y%2C%20title%3D%22y%20(px)%22)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%0A%20%20%20%20_anchor_layer%20%3D%20(%0A%20%20%20%20%20%20%20%20alt.Chart(pd.DataFrame(%7B%22x%22%3A%20%5B0%2C%20iter_W%5D%2C%20%22y%22%3A%20%5B0%2C%20iter_H%5D%7D))%0A%20%20%20%20%20%20%20%20.mark_point(opacity%3D0)%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(%22x%3AQ%22%2C%20scale%3D_scale_x)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20y%3Dalt.Y(%22y%3AQ%22%2C%20scale%3D_scale_y)%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20)%0A%0A%20%20%20%20_base_chart%20%3D%20alt.layer(_img_layer%2C%20_anchor_layer).properties(%0A%20%20%20%20%20%20%20%20width%3D_chart_w%2C%20height%3D_chart_h%0A%20%20%20%20)%0A%0A%20%20%20%20iter_roi_chart%20%3D%20mo.ui.altair_chart(_base_chart%2C%20chart_selection%3D%22interval%22)%0A%20%20%20%20iter_add_btn%20%3D%20mo.ui.run_button(label%3D%22Add%20ROI%22%2C%20kind%3D%22success%22)%0A%20%20%20%20iter_reset_btn%20%3D%20mo.ui.run_button(label%3D%22Reset%22%2C%20kind%3D%22warn%22)%0A%20%20%20%20iter_get_rois%2C%20iter_set_rois%20%3D%20mo.state(%5B%5D)%0A%20%20%20%20return%20(%0A%20%20%20%20%20%20%20%20iter_H%2C%0A%20%20%20%20%20%20%20%20iter_W%2C%0A%20%20%20%20%20%20%20%20iter_add_btn%2C%0A%20%20%20%20%20%20%20%20iter_get_rois%2C%0A%20%20%20%20%20%20%20%20iter_raw%2C%0A%20%20%20%20%20%20%20%20iter_reset_btn%2C%0A%20%20%20%20%20%20%20%20iter_roi_chart%2C%0A%20%20%20%20%20%20%20%20iter_set_rois%2C%0A%20%20%20%20)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(%0A%20%20%20%20fov_table%2C%0A%20%20%20%20image%2C%0A%20%20%20%20iter_H%2C%0A%20%20%20%20iter_W%2C%0A%20%20%20%20iter_add_btn%2C%0A%20%20%20%20iter_get_rois%2C%0A%20%20%20%20iter_raw%2C%0A%20%20%20%20iter_reset_btn%2C%0A%20%20%20%20iter_roi_chart%2C%0A%20%20%20%20iter_set_rois%2C%0A%20%20%20%20mo%2C%0A%20%20%20%20ngio%2C%0A%20%20%20%20plt%2C%0A)%3A%0A%20%20%20%20from%20matplotlib.patches%20import%20Rectangle%0A%20%20%20%20from%20ngio.tables%20import%20RoiTable%0A%0A%0A%20%20%20%20def%20_brush_extent(selections)%3A%0A%20%20%20%20%20%20%20%20if%20not%20selections%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20None%0A%20%20%20%20%20%20%20%20for%20_val%20in%20selections.values()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20isinstance(_val%2C%20dict)%20and%20%22x%22%20in%20_val%20and%20%22y%22%20in%20_val%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_xs%2C%20_ys%20%3D%20_val%5B%22x%22%5D%2C%20_val%5B%22y%22%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20if%20len(_xs)%20%3D%3D%202%20and%20len(_ys)%20%3D%3D%202%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20return%20tuple(_xs)%2C%20tuple(_ys)%0A%20%20%20%20%20%20%20%20return%20None%0A%0A%0A%20%20%20%20_prev%20%3D%20iter_get_rois()%0A%20%20%20%20if%20iter_reset_btn.value%3A%0A%20%20%20%20%20%20%20%20_rois%20%3D%20%5B%5D%0A%20%20%20%20%20%20%20%20iter_set_rois(_rois)%0A%20%20%20%20elif%20iter_add_btn.value%3A%0A%20%20%20%20%20%20%20%20_extent%20%3D%20_brush_extent(iter_roi_chart.selections)%0A%20%20%20%20%20%20%20%20if%20_extent%20is%20not%20None%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20(_xa%2C%20_xb)%2C%20(_ya%2C%20_yb)%20%3D%20_extent%0A%20%20%20%20%20%20%20%20%20%20%20%20_x0%20%3D%20max(0%2C%20int(round(min(_xa%2C%20_xb))))%0A%20%20%20%20%20%20%20%20%20%20%20%20_x1%20%3D%20min(iter_W%2C%20int(round(max(_xa%2C%20_xb))))%0A%20%20%20%20%20%20%20%20%20%20%20%20_y0%20%3D%20max(0%2C%20int(round(min(_ya%2C%20_yb))))%0A%20%20%20%20%20%20%20%20%20%20%20%20_y1%20%3D%20min(iter_H%2C%20int(round(max(_ya%2C%20_yb))))%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20_x1%20%3E%20_x0%20and%20_y1%20%3E%20_y0%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_rois%20%3D%20_prev%20%2B%20%5B(_x0%2C%20_y0%2C%20_x1%20-%20_x0%2C%20_y1%20-%20_y0)%5D%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20iter_set_rois(_rois)%0A%20%20%20%20%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_rois%20%3D%20_prev%0A%20%20%20%20%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_rois%20%3D%20_prev%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20_rois%20%3D%20_prev%0A%0A%20%20%20%20if%20_rois%3A%0A%20%20%20%20%20%20%20%20iter_roi_table%20%3D%20RoiTable(%0A%20%20%20%20%20%20%20%20%20%20%20%20rois%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ngio.Roi(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name%3Df%22user_roi_%7B_i%7D%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20slices%3D%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20ngio.RoiSlice(%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%20%20%20%20axis_name%3D%22x%22%2C%20start%3Dfloat(_x)%2C%20length%3Dfloat(_w)%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)%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%20ngio.RoiSlice(%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%20%20%20%20axis_name%3D%22y%22%2C%20start%3Dfloat(_y)%2C%20length%3Dfloat(_h)%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)%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%20ngio.RoiSlice(axis_name%3D%22z%22%2C%20start%3D0.0%2C%20length%3D1.0)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20space%3D%22pixel%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20).to_world(pixel_size%3Dimage.pixel_size)%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20for%20_i%2C%20(_x%2C%20_y%2C%20_w%2C%20_h)%20in%20enumerate(_rois%2C%20start%3D1)%0A%20%20%20%20%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20iter_roi_table%20%3D%20fov_table%0A%0A%20%20%20%20_fig%2C%20_ax%20%3D%20plt.subplots(figsize%3D(7%2C%20max(2.0%2C%207.0%20*%20iter_H%20%2F%20iter_W)))%0A%20%20%20%20_ax.imshow(iter_raw%2C%20cmap%3D%22gray%22)%0A%20%20%20%20_ax.axis(%22off%22)%0A%20%20%20%20if%20_rois%3A%0A%20%20%20%20%20%20%20%20_ax.set_title(f%22ROI%20preview%20%E2%80%94%20%7Blen(_rois)%7D%20drawn%22)%0A%20%20%20%20%20%20%20%20for%20_x%2C%20_y%2C%20_w%2C%20_h%20in%20_rois%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20_ax.add_patch(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20Rectangle(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20(_x%2C%20_y)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_w%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20_h%2C%0A%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%20edgecolor%3D%22%233DBDB8%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20linewidth%3D1.8%2C%0A%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)%0A%20%20%20%20else%3A%0A%20%20%20%20%20%20%20%20_ax.set_title(%22No%20ROIs%20drawn%20%E2%80%94%20falling%20back%20to%20FOV_ROI_table%22)%0A%20%20%20%20_fig.tight_layout()%0A%0A%20%20%20%20mo.vstack(%0A%20%20%20%20%20%20%20%20%5B%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.md(%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%23%23%23%20Let's%20build%20a%20custom%20ROI%20table%20%5Cn%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22Brush%20a%20rectangle%20on%20the%20image%2C%20then%20click%20**Add%20ROI**%20to%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22store%20it.%20Repeat%20to%20build%20a%20custom%20ROI%20table%3B%20**Reset**%20clears%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22the%20list.%20With%20no%20ROIs%20drawn%2C%20the%20iterator%20falls%20back%20to%20%22%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%22%60FOV_ROI_table%60%20so%20downstream%20cells%20still%20run.%22%0A%20%20%20%20%20%20%20%20%20%20%20%20)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20iter_roi_chart%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20mo.hstack(%5Biter_add_btn%2C%20iter_reset_btn%5D%2C%20justify%3D%22start%22)%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20_fig%2C%0A%20%20%20%20%20%20%20%20%5D%0A%20%20%20%20)%0A%20%20%20%20return%20(iter_roi_table%2C)%0A%0A%0A%40app.cell%0Adef%20_(iter_roi_table)%3A%0A%20%20%20%20iter_roi_table.rois()%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(image%2C%20iter_roi_table%2C%20ome_zarr)%3A%0A%20%20%20%20from%20ngio.experimental.iterators%20import%20SegmentationIterator%0A%0A%20%20%20%20nuc_label%20%3D%20ome_zarr.derive_label(%22nuclei_seg%22%2C%20overwrite%3DTrue)%0A%0A%20%20%20%20seg_iterator%20%3D%20(%0A%20%20%20%20%20%20%20%20SegmentationIterator(%0A%20%20%20%20%20%20%20%20%20%20%20%20input_image%3Dimage%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_label%3Dnuc_label%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20channel_selection%3D%22DAPI%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20axes_order%3D%5B%22y%22%2C%20%22x%22%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.product(iter_roi_table)%0A%20%20%20%20%20%20%20%20.by_yx()%0A%20%20%20%20)%0A%20%20%20%20%23%20seg_iterator.require_no_regions_overlap()%0A%20%20%20%20return%20SegmentationIterator%2C%20nuc_label%2C%20seg_iterator%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(seg_iterator)%3A%0A%20%20%20%20seg_iterator.rois%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%20Naive%20run%20%E2%80%94%20let%20labels%20collide%0A%0A%20%20%20%20We%20hand%20the%20%60basic_segmentation%60%20function%20straight%20to%0A%20%20%20%20%60seg_iterator.map_as_numpy(...)%60.%20Each%20patch%20is%20segmented%20independently%0A%20%20%20%20and%20written%20back%20to%20%60nuclei_seg%60.%0A%0A%20%20%20%20Every%20patch%20therefore%20reuses%20the%20same%20IDs%2C%20so%20the%0A%20%20%20%20global%20%60nuclei_seg%60%20would%20likely%20need%20a%20relabeling.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(basic_segmentation%2C%20ome_zarr%2C%20plot_image%2C%20seg_iterator)%3A%0A%20%20%20%20seg_iterator.map_as_numpy(basic_segmentation)%0A%0A%20%20%20%20plot_image(%0A%20%20%20%20%20%20%20%20ome_zarr%2C%0A%20%20%20%20%20%20%20%20label%3D%22nuclei_seg%22%2C%0A%20%20%20%20%20%20%20%20title%3D%22Naive%20segmentation%20%E2%80%94%20labels%20collide%20across%20patches%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_(iter_t_run_graph%2C%20mo)%3A%0A%20%20%20%20mo.md(rf%22%22%22%0A%20%20%20%20%23%23%23%203.3%20Unique%20labels%20via%20an%20output%20transform%0A%0A%20%20%20%20%60SegmentationIterator%60%20accepts%20a%20list%20of%20%60output_transforms%60%20that%20run%20on%0A%20%20%20%20every%20patch%20*before*%20it%20is%20written%20to%20disk.%20%0A%0A%20%20%20%20%7Biter_t_run_graph%7D%0A%0A%20%20%20%20A%20transform%20is%20any%20object%20matching%20ngio's%20%60TransformProtocol%60.%20For%20a%20numpy%20%60map_as_numpy%60%20run%2C%20only%0A%20%20%20%20%60set_as_numpy_transform(array%2C%20slicing_ops%2C%20axes_ops)%60%20needs%20to%20be%0A%20%20%20%20implemented%3B%20the%20other%20protocol%20methods%20(%60get_as_numpy_transform%60%2C%0A%20%20%20%20%60*_as_dask_transform%60)%20are%20only%20consulted%20on%20read%20paths%20or%20in%20dask%20runs.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20SegmentationIterator%2C%0A%20%20%20%20basic_segmentation%2C%0A%20%20%20%20image%2C%0A%20%20%20%20iter_roi_table%2C%0A%20%20%20%20np%2C%0A%20%20%20%20nuc_label%2C%0A%20%20%20%20ome_zarr%2C%0A%20%20%20%20plot_image%2C%0A)%3A%0A%20%20%20%20%23%20%60UniqueLabelOffset%60%20keeps%20a%20running%20%60max_label%60%20across%20calls%20and%20shifts%0A%20%20%20%20%23%20each%20patch's%20labels%20above%20the%20previous%20high-water%20mark%20%E2%80%94%20turning%20the%0A%20%20%20%20%23%20per-patch%20IDs%20into%20globally%20unique%20ones.%0A%0A%0A%20%20%20%20class%20UniqueLabelOffset%3A%0A%20%20%20%20%20%20%20%20%22%22%22Shift%20every%20patch's%20labels%20above%20the%20running%20max%20so%20IDs%20stay%20unique.%22%22%22%0A%0A%20%20%20%20%20%20%20%20def%20__init__(self)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20self.max_label%20%3D%200%0A%0A%20%20%20%20%20%20%20%20def%20set_as_numpy_transform(self%2C%20array%2C%20slicing_ops%2C%20axes_ops)%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20mask%20%3D%20array%20%3E%200%0A%20%20%20%20%20%20%20%20%20%20%20%20array%20%3D%20np.where(mask%2C%20array%20%2B%20self.max_label%2C%200)%0A%20%20%20%20%20%20%20%20%20%20%20%20if%20mask.any()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20self.max_label%20%3D%20int(array.max())%0A%20%20%20%20%20%20%20%20%20%20%20%20return%20array%0A%0A%0A%20%20%20%20naive_unique_count%20%3D%20len(%0A%20%20%20%20%20%20%20%20np.unique(nuc_label.get_as_numpy(axes_order%3D%5B%22y%22%2C%20%22x%22%5D%2C%20z%3D0))%0A%20%20%20%20)%0A%0A%20%20%20%20nuc_label_unique%20%3D%20ome_zarr.derive_label(%22nuclei_seg_unique%22%2C%20overwrite%3DTrue)%0A%0A%20%20%20%20seg_iterator_unique%20%3D%20(%0A%20%20%20%20%20%20%20%20SegmentationIterator(%0A%20%20%20%20%20%20%20%20%20%20%20%20input_image%3Dimage%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_label%3Dnuc_label_unique%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20channel_selection%3D%22DAPI%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20axes_order%3D%5B%22y%22%2C%20%22x%22%5D%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_transforms%3D%5BUniqueLabelOffset()%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.product(iter_roi_table)%0A%20%20%20%20%20%20%20%20.by_yx()%0A%20%20%20%20)%0A%0A%20%20%20%20seg_iterator_unique.map_as_numpy(basic_segmentation)%0A%0A%20%20%20%20unique_label_count%20%3D%20len(%0A%20%20%20%20%20%20%20%20np.unique(nuc_label_unique.get_as_numpy(axes_order%3D%5B%22y%22%2C%20%22x%22%5D%2C%20z%3D0))%0A%20%20%20%20)%0A%0A%20%20%20%20plot_image(%0A%20%20%20%20%20%20%20%20ome_zarr%2C%0A%20%20%20%20%20%20%20%20label%3D%22nuclei_seg_unique%22%2C%0A%20%20%20%20%20%20%20%20title%3D%22Unique%20labels%20via%20output%20transform%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%20naive_unique_count%2C%20unique_label_count%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo%2C%20naive_unique_count%2C%20unique_label_count)%3A%0A%20%20%20%20mo.md(f%22%22%22%0A%20%20%20%20**Distinct%20label%20IDs%20across%20all%20patches**%0A%0A%20%20%20%20%7C%20Run%20%7C%20Unique%20labels%20%7C%0A%20%20%20%20%7C---%7C---%3A%7C%0A%20%20%20%20%7C%20Naive%20%60map_as_numpy%60%20%7C%20%60%7Bnaive_unique_count%7D%60%20%7C%0A%20%20%20%20%7C%20With%20%60UniqueLabelOffset%60%20%7C%20%60%7Bunique_label_count%7D%60%20%7C%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.4%20Stateful%20iteration%20%E2%80%94%20%60iter_as_numpy%60%0A%0A%20%20%20%20%60map_as_numpy%60%20is%20the%20right%20call%20when%20each%20patch%20is%20independent.%20When%0A%20%20%20%20you%20need%20state%20across%20patches%20%E2%80%94%20a%20running%20counter%2C%20a%20cross-patch%0A%20%20%20%20statistic%2C%20an%20early-exit%20condition%20%E2%80%94%20switch%20to%20%60iter_as_numpy%60%2C%20which%0A%20%20%20%20yields%20a%20%60(patch%2C%20writer)%60%20pair%20per%20ROI%20and%20lets%20you%20call%20the%20writer%0A%20%20%20%20yourself.%0A%0A%20%20%20%20The%20example%20below%20reproduces%20%C2%A73.3's%20%22globally%20unique%20labels%22%20result%0A%20%20%20%20*without*%20using%20a%20transform%3A%20the%20running%20%60max_label%60%20lives%20in%20the%20loop%0A%20%20%20%20body.%20Two%20ways%20to%20solve%20the%20same%20problem%20%E2%80%94%20pick%20whichever%20fits%20the%0A%20%20%20%20surrounding%20code%20best.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(%0A%20%20%20%20SegmentationIterator%2C%0A%20%20%20%20basic_segmentation%2C%0A%20%20%20%20image%2C%0A%20%20%20%20iter_roi_table%2C%0A%20%20%20%20np%2C%0A%20%20%20%20ome_zarr%2C%0A%20%20%20%20plot_image%2C%0A)%3A%0A%20%20%20%20nuc_label_loop%20%3D%20ome_zarr.derive_label(%22nuclei_seg_loop%22%2C%20overwrite%3DTrue)%0A%0A%20%20%20%20seg_iterator_loop%20%3D%20(%0A%20%20%20%20%20%20%20%20SegmentationIterator(%0A%20%20%20%20%20%20%20%20%20%20%20%20input_image%3Dimage%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20output_label%3Dnuc_label_loop%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20channel_selection%3D%22DAPI%22%2C%0A%20%20%20%20%20%20%20%20%20%20%20%20axes_order%3D%5B%22y%22%2C%20%22x%22%5D%2C%0A%20%20%20%20%20%20%20%20)%0A%20%20%20%20%20%20%20%20.product(iter_roi_table)%0A%20%20%20%20%20%20%20%20.by_yx()%0A%20%20%20%20)%0A%0A%20%20%20%20max_label%20%3D%200%0A%20%20%20%20for%20patch%2C%20writer%20in%20seg_iterator_loop.iter_as_numpy()%3A%0A%20%20%20%20%20%20%20%20seg%20%3D%20basic_segmentation(patch)%0A%20%20%20%20%20%20%20%20mask%20%3D%20seg%20%3E%200%0A%20%20%20%20%20%20%20%20seg%20%3D%20np.where(mask%2C%20seg%20%2B%20max_label%2C%200).astype(seg.dtype)%0A%20%20%20%20%20%20%20%20if%20mask.any()%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20max_label%20%3D%20int(seg.max())%0A%20%20%20%20%20%20%20%20writer(patch%3Dseg)%0A%0A%20%20%20%20plot_image(%0A%20%20%20%20%20%20%20%20ome_zarr%2C%0A%20%20%20%20%20%20%20%20label%3D%22nuclei_seg_loop%22%2C%0A%20%20%20%20%20%20%20%20title%3D%22Stateful%20iter_as_numpy%20%E2%80%94%20running%20max_label%20in%20the%20loop%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%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
83029fbadfe88968f9355f9a951a29f6