Count Seeds While Keeping the Image Assumptions Visible
Turn a small tray image into a binary mask, label connected regions, and investigate when a region count stops matching the number of seeds.
14 sections · 4 questions · 3 coding challenges
The arrow keys move between sections too.
Turning Pixel Regions Into Measurable Objects
BOTIE
Our tray uses invented grayscale pixels and a simple four-neighbour rule. A region is an image structure; interpreting it as one seed requires additional assumptions about separation, lighting, and noise.
Sources for this history
When Does One Bright Region Mean One Seed?
Botie photographs seeds on a dark tray and needs a count. Bright pixels look promising, but one seed spans several pixels, dust can look like a seed, and two touching seeds can become one region.
Let's build a count from the photograph. We will select candidate pixels, group connected ones, and examine whether a size rule separates seeds from small specks. At each stage we will keep the intermediate image visible. That lets us investigate a wrong count instead of treating the final number as sufficient evidence.
The tray image is invented; a connected bright region is only a seed under the stated assumptions.
Step 1 of 3
Inspect grayscale values
A bright isolated pixel may be dust, not a seed.
Tray image
Step 2 of 3
Make a foreground mask
Use a strict threshold above five.
Foreground mask
Step 3 of 3
Count regions that pass the size rule
Four-neighbour regions of at least two pixels remain.
| Region | Pixels | Count as a seed? |
|---|---|---|
| Top | 4 | Yes |
| Isolated pixel | 1 | No |
| Lower left | 2 | Yes |
| Lower right | 2 | Yes |
Top
- Pixels
- 4
- Count as a seed?
- Yes
Isolated pixel
- Pixels
- 1
- Count as a seed?
- No
Lower left
- Pixels
- 2
- Count as a seed?
- Yes
Lower right
- Pixels
- 2
- Count as a seed?
- Yes
The count depends on the image assumptions as well as the arithmetic
Connectivity groups pixels before the area rule decides which regions to count.
Did we count seeds or merely bright regions?
Follow the image through thresholding, connectivity and the minimum-area rule. Change one setting and inspect which regions disappear before interpreting the final count as seeds.
BOTIE
Number of regions meeting the area rule
3
Follow the operations
1. Grayscale tray image
0 9 9 0 0 0 9 9 0 8 0 0 0 0 0 7 7 0 8 8 2. Strict intensity threshold
5
3. Strict threshold
0 1 1 0 0 0 1 1 0 1 0 0 0 0 0 1 1 0 1 1 4. Set a constant
4
5. Label connected foreground regions
0 1 1 0 0 0 1 1 0 2 0 0 0 0 0 3 3 0 4 4 6. Count pixels in each labelled region
- 04
- 11
- 22
- 32
7. Minimum component area
2
8. Threshold gate
- 01
- 10
- 21
- 31
9. Add a list
3
10. Result
3
Counts assume each accepted connected region corresponds to one seed. Touching objects and lighting changes can violate that assumption.
1. Define the Capture and Label Rules
Assume a fixed camera, dark tray, brighter seeds, and separate seed silhouettes. Record image dtype and intensity range. Choose threshold and minimum region size on separate labelled development images.
A hand-labelled seed count is not the same as a foreground mask. Keep the evaluation label appropriate to the question: count error, segmentation overlap, and object detection each measure different things.
These tiny arrays let us inspect mechanics before testing on real photographs.
2. Threshold Without Destroying the Input
Compare grayscale intensity with the chosen threshold to produce a Boolean foreground mask. Preserve the original image for investigating mistakes.
Uneven lighting can make a single global threshold inappropriate. Morphological cleanup can help some noise patterns but can also merge or erase seeds, so any cleanup needs its own validation.
Foreground rule: intensity > 5
Pixel 5 becomes background
Pixel 7 becomes foregroundThreshold equality is part of the rule and belongs in the tests.
3. Find Connected Regions
We will connect pixels across shared sides: up, down, left and right. Touching only at a corner does not connect them under this four-neighbour rule. Start from an unvisited foreground pixel and use a queue or stack to find the rest of its region.
The mask tells us which pixels are foreground. A visited array prevents us from processing one twice. The queue or stack holds coordinates we still need to inspect, while a counter records the region's size. Each structure has a different job, so we will keep them separate in the implementation.
Changing to eight-neighbour connectivity changes what counts as one region.
Count qualifying regions after pixels have been labelled
Suppose the connectivity step has already given each foreground region an ID, with zero reserved for background. Count the pixels belonging to each positive ID before applying a minimum area.
import numpy as np
labels = np.array([[0, 1, 1], [0, 0, 2], [3, 3, 0]])
region_ids = np.unique(labels[labels > 0])
areas = np.array([np.count_nonzero(labels == region) for region in region_ids])
print(areas.tolist())
print(int(np.count_nonzero(areas >= 2)))[2, 1, 2]
2There are three foreground regions, but only two meet the area rule. Practice starts from intensities and asks you to discover the connected regions yourself.
4. Reject Tiny Regions, Then Count
Filter connected regions by a minimum pixel size. In the supplied scene, the single bright pixel is removed while the three larger regions remain.
This rule can also remove a genuinely small seed. Touching seeds remain one connected component unless another separation method is added. Compare predicted and labelled counts across these failure cases.
Region sizes = [4,1,2,2]
Keep sizes >= 2
Accepted regions = 3The rule counts accepted regions, not physical objects by definition.
5. Write a Useful Failure Report
Evaluate on held-out tray images that vary in brightness, spacing, seed size, and dust. Record undercounts and overcounts separately and inspect masks when an error occurs.
Save threshold, connectivity, minimum area, and image preparation with the procedure. A new camera or resolution can change pixel areas even if the physical seeds are unchanged.
The project is complete when its procedure and limits can be reproduced, including images where it fails.
6. Make a Test Collection That Can Disagree with Us
Our small pixel example checks the arithmetic. A useful counting project also needs images where the assumption of separated bright seeds is wrong. Collect dim seeds, touching seeds, dust, shadows and seeds clipped by the image boundary. Record a human count for each image and describe the counting convention for partial seeds.
Separate development images, used to choose a threshold and minimum area, from untouched evaluation images. Several crops from the same photograph should stay together so almost identical regions do not appear on both sides. Report count errors by scene condition, not only one average.
7. Keep the Mask and Regions Beside the Count
When a count is wrong, the final integer rarely tells us why. Save the threshold mask, connected-region labels and each region’s area. A missing dim seed disappears in the mask. Two touching seeds can survive the mask but share one region. Dust can appear as tiny separate regions.
Our four-neighbour search joins pixels through shared edges. It deliberately leaves diagonal contact separate. The next challenge asks you to implement that search with a stack of pixel coordinates, a Boolean visited array and a list of region sizes. Those data structures each have a distinct job.
8. Know When a Threshold Cannot Supply the Missing Information
Lowering a threshold might recover a dim seed while admitting more background. Increasing a size cutoff can remove dust while discarding a small real seed. Neither adjustment can reliably separate two objects that form one bright region.
The next experiment might change lighting, use shape information to split a region, or train a segmentation model with labelled images. Preserve the failed examples when making that decision. They explain why a more complicated method is needed and give it a fair test.
Check yourself
Questions on Parts 1 to 8
Question 1 of 4
How many regions remain?
Use the information below.
Region sizes [4,1,2,2], minimum accepted size two.Try it with Botie
Build with Botie
Write and run Python