{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Tutorial 5: Working with Grid Plates\n", "\n", "So far we have worked with `GridImage` objects without paying much attention\n", "to the *grid* part. In this tutorial you will learn what makes a `GridImage`\n", "special — it knows the row-and-column layout of your plate, so you can\n", "inspect individual wells, count colonies per grid section, and visualize\n", "the grid structure.\n", "\n", "**What you will learn:**\n", "\n", "1. How `GridImage` differs from `Image`\n", "2. Access grid properties (rows, columns)\n", "3. Run detection, then query per-colony grid assignments\n", "4. Extract a single well as a subimage\n", "5. Count colonies per grid section\n", "6. Visualize the grid overlay" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Imports" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import phenotypic as pht\n", "from phenotypic.data import load_yeast_plate\n", "from phenotypic.enhance import GaussianBlur, CLAHE\n", "from phenotypic.detect import OtsuDetector" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Load the Plate\n", "\n", "`load_yeast_plate()` returns a `GridImage` — an `Image` that also knows\n", "about the grid layout. Our *Rhodotorula* plate is arranged in a standard\n", "96-well format: 8 rows by 12 columns." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plate = load_yeast_plate()\n", "print(f\"Type: {type(plate).__name__}\")\n", "print(f\"Rows: {plate.grid.nrows}\")\n", "print(f\"Columns: {plate.grid.ncols}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Detect Colonies First\n", "\n", "Grid features like colony assignments and section counts require detected\n", "objects. Let's run the same enhance-and-detect pipeline from earlier tutorials." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "pipeline = pht.ImagePipeline(\n", " ops=[GaussianBlur(sigma=2.0), CLAHE(clip_limit=0.01), OtsuDetector()]\n", ")\n", "plate = pipeline.apply(plate)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Visualize the Grid Overlay\n", "\n", "The overlay view becomes especially powerful on grid plates — you can see\n", "the detected colonies *and* the grid boundaries at the same time." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "plate.dash(overlay=True, show_grid=True)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The dashed lines show the grid boundaries. Each rectangular region is one\n", "grid section (corresponding to one well on the physical plate)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Query Grid Assignments\n", "\n", "The `.grid.info()` method returns a DataFrame with one row per detected\n", "colony, including its grid position — which row, which column, and which\n", "flattened section number it belongs to." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "info = plate.grid.info()\n", "info.head(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Key columns:\n", "\n", "- **RowNum** / **ColNum** — grid row and column (0-indexed)\n", "- **SectionNum** — flattened section index (0 to nrows × ncols − 1)\n", "- **CenterRR** / **CenterCC** — colony centroid in pixel coordinates\n", "- **MinRR**, **MaxRR**, **MinCC**, **MaxCC** — bounding box" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Extract a Single Well\n", "\n", "You can pull out any grid section as a standalone subimage using bracket\n", "indexing on the `.grid` accessor. Let's look at the well in row 0, column 0\n", "(top-left corner)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "well = plate.grid[0, 0]\n", "well.dash()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "You can also extract an entire row or column with slicing:\n", "\n", "```python\n", "first_row = plate.grid[0, :] # All 12 wells in row 0\n", "third_col = plate.grid[:, 2] # All 8 wells in column 2\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Count Colonies per Grid Section\n", "\n", "How many colonies are in each well? `.grid.get_section_counts()` gives you\n", "a quick summary." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "counts = plate.grid.get_section_counts()\n", "counts.head(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The index is the section number and the value is the colony count. Sections\n", "with zero colonies are omitted by default." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Summary\n", "\n", "You now know how to work with grid plates in PhenoTypic:\n", "\n", "- **`plate.grid.nrows`** / **`.ncols`** — grid dimensions\n", "- **`plate.grid.info()`** — per-colony DataFrame with grid row, column, and section\n", "- **`plate.grid[row, col]`** — extract a single well as a subimage\n", "- **`plate.grid.get_section_counts()`** — colony counts per section\n", "- **`plate.dash(overlay=True, show_grid=True)`** — visual grid overlay\n", "\n", "Grid awareness is what makes PhenoTypic especially powerful for arrayed\n", "colony assays — every colony knows which well it belongs to.\n", "\n", "**Next up:** [Tutorial 6: Batch Processing](06_batch_processing.ipynb) —\n", "process many plates at once using the command-line interface." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "name": "python", "version": "3.11.0" } }, "nbformat": 4, "nbformat_minor": 4 }