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:

  1. How GridImage differs from Image

  2. Access grid properties (rows, columns)

  3. Run detection, then query per-colony grid assignments

  4. Extract a single well as a subimage

  5. Count colonies per grid section

  6. 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.