Tutorial 5: Working with Grid Plates#
So far we have worked with GridImage objects without paying much attention to the grid part. In this tutorial you will learn what makes a GridImage special — it knows the row-and-column layout of your plate, so you can inspect individual wells, count colonies per grid section, and visualize the grid structure.
What you will learn:
How
GridImagediffers fromImageAccess grid properties (rows, columns)
Run detection, then query per-colony grid assignments
Extract a single well as a subimage
Count colonies per grid section
Visualize the grid overlay
Imports#
[1]:
import phenotypic as pht
from phenotypic.data import load_yeast_plate
from phenotypic.enhance import GaussianBlur, CLAHE
from phenotypic.detect import OtsuDetector
Load the Plate#
load_yeast_plate() returns a GridImage — an Image that also knows about the grid layout. Our Rhodotorula plate is arranged in a standard 96-well format: 8 rows by 12 columns.
[2]:
plate = load_yeast_plate()
print(f"Type: {type(plate).__name__}")
print(f"Rows: {plate.grid.nrows}")
print(f"Columns: {plate.grid.ncols}")
Type: GridImage
Rows: 2
Columns: 4
Detect Colonies First#
Grid features like colony assignments and section counts require detected objects. Let’s run the same enhance-and-detect pipeline from earlier tutorials.
[3]:
pipeline = pht.ImagePipeline(
ops=[GaussianBlur(sigma=2.0), CLAHE(clip_limit=0.01), OtsuDetector()]
)
plate = pipeline.apply(plate)
Visualize the Grid Overlay#
The overlay view becomes especially powerful on grid plates — you can see the detected colonies and the grid boundaries at the same time.
[4]:
plate.dash(overlay=True, show_grid=True)
Data type cannot be displayed: application/vnd.plotly.v1+json
The dashed lines show the grid boundaries. Each rectangular region is one grid section (corresponding to one well on the physical plate).
Query Grid Assignments#
The .grid.info() method returns a DataFrame with one row per detected colony, including its grid position — which row, which column, and which flattened section number it belongs to.
[5]:
info = plate.grid.info()
info.head(10)
[5]:
| Metadata_FileSuffix | Metadata_BitDepth | Metadata_ImageType | Metadata_ImageName | ObjectLabel | Bbox_CenterRR | Bbox_CenterCC | Bbox_MinRR | Bbox_MinCC | Bbox_MaxRR | Bbox_MaxCC | Bbox_IntensityWeightedCenterRR | Bbox_IntensityWeightedCenterCC | Bbox_DistWeightedCenterRR | Bbox_DistWeightedCenterCC | Grid_RowNum | Grid_ColNum | Grid_RowMajorIdx | Grid_ColMajorIdx | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 1 | 189.366746 | 190.568832 | 101 | 108 | 279 | 276 | 188.875087 | 190.632117 | 190.0 | 194.0 | 0 | 0 | 0 | 0 |
| 1 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 2 | 180.748935 | 1003.205256 | 107 | 930 | 258 | 1077 | 180.401078 | 1003.869793 | 180.0 | 1004.0 | 0 | 2 | 2 | 4 |
| 2 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 3 | 179.459997 | 1409.055778 | 107 | 1338 | 253 | 1481 | 179.084982 | 1409.821162 | 179.0 | 1409.0 | 0 | 3 | 3 | 6 |
| 3 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 4 | 184.220957 | 598.966905 | 116 | 534 | 251 | 664 | 184.048933 | 599.313350 | 184.0 | 599.0 | 0 | 1 | 1 | 2 |
| 4 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 5 | 164.000000 | 1074.000000 | 164 | 1074 | 165 | 1075 | 164.000000 | 1074.000000 | 164.0 | 1074.0 | 0 | 2 | 2 | 4 |
| 5 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 6 | 586.852613 | 1410.286595 | 510 | 1337 | 665 | 1487 | 586.827058 | 1410.886407 | 586.0 | 1411.0 | 1 | 3 | 7 | 7 |
| 6 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 7 | 588.945549 | 1001.784782 | 514 | 928 | 664 | 1077 | 589.000420 | 1002.339387 | 591.0 | 1002.0 | 1 | 2 | 6 | 5 |
| 7 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 8 | 588.671378 | 599.609505 | 519 | 533 | 658 | 665 | 588.629542 | 600.006412 | 587.0 | 599.0 | 1 | 1 | 5 | 3 |
| 8 | .png | 8 | GridImage | RhodotorulaYeastCenterCrop | 9 | 594.000000 | 532.000000 | 594 | 532 | 595 | 533 | 594.000000 | 532.000000 | 594.0 | 532.0 | 1 | 1 | 5 | 3 |
Key columns:
RowNum / ColNum — grid row and column (0-indexed)
SectionNum — flattened section index (0 to nrows × ncols − 1)
CenterRR / CenterCC — colony centroid in pixel coordinates
MinRR, MaxRR, MinCC, MaxCC — bounding box
Extract a Single Well#
You can pull out any grid section as a standalone subimage using bracket indexing on the .grid accessor. Let’s look at the well in row 0, column 0 (top-left corner).
[6]:
well = plate.grid[0, 0]
well.dash()
Data type cannot be displayed: application/vnd.plotly.v1+json
You can also extract an entire row or column with slicing:
first_row = plate.grid[0, :] # All 12 wells in row 0
third_col = plate.grid[:, 2] # All 8 wells in column 2
Count Colonies per Grid Section#
How many colonies are in each well? .grid.get_section_counts() gives you a quick summary.
[7]:
counts = plate.grid.get_section_counts()
counts.head(10)
[7]:
Grid_RowMajorIdx
2 2
5 2
0 1
1 1
3 1
6 1
7 1
Name: count, dtype: int64
The index is the section number and the value is the colony count. Sections with zero colonies are omitted by default.
Summary#
You now know how to work with grid plates in PhenoTypic:
``plate.grid.nrows`` / ``.ncols`` — grid dimensions
``plate.grid.info()`` — per-colony DataFrame with grid row, column, and section
``plate.grid[row, col]`` — extract a single well as a subimage
``plate.grid.get_section_counts()`` — colony counts per section
``plate.dash(overlay=True, show_grid=True)`` — visual grid overlay
Grid awareness is what makes PhenoTypic especially powerful for arrayed colony assays — every colony knows which well it belongs to.
Next up: Tutorial 6: Batch Processing — process many plates at once using the command-line interface.