Bloom-Counter Stress Test: The Total Is Right, but the Structure Is Wrong

Bloom-Counter's 99.7% recall comes from co-tuned simulator and detector; stress tests reveal structural failures under lighting and occlusion.
The open-source Bloom-Counter claims 99.7% detection recall, but stress testing reveals this stems from co-tuned simulator and detector, with scoring only totals and not structure. Keeping the detector unchanged, the author breaks the simulator's two hidden assumptions—stable lighting and non-overlapping flowers—to measure failure modes. Darkening and shadow yield 0.0% total error yet one-sixth to one-quarter of buds are wrongly downgraded; occlusion shatters buds into bloom-sized fragments, reaching +81% total error at 0.8× coverage; white balance + CLAHE fixes global shifts but harms overexposed and noisy images; area splitting works only for 0.8–1.2× overlaps. Four practical recommendations follow, with the lack of real annotated data as the biggest open question.
The Structural Flaw Behind a "Perfect" Score
The open-source project Bloom-Counter advertises an impressive number in its README: 6 simulated photos, 357 flowers, 99.7% detection recall. But after stress testing, the author Guangyi Zhao reached an uncomfortable conclusion: this 99.7% proves nothing.
The reason is buried in the code. A comment on line 23 of simulator/generate.py states it bluntly: the flowers' BGR colors are "tuned to fall within the detector's HSV petal range." The generator draws flowers to the detector's taste, and the detector counts them. This is not measurement; it's collusion. Another detail is equally damning: the simulator's _place function uses rejection sampling to guarantee that no two flowers ever overlap, while the scoring formula in examples/demo.py is (detected buds + blooms) / (ground-truth buds + blooms)—just totals, with no per-object matching and no breakdown by category.
A real greenhouse inverts both of these assumptions: light swings throughout the day, leaf occlusion is the norm, and flowers grow in clusters. The author therefore keeps the detector unchanged and systematically breaks the simulator's two hidden assumptions, measuring how it fails first, how badly, and whether two cheap remedies actually help.

Method: Stricter Scoring Exposes Failures Earlier
The detector is the untouched ColorSegmentationDetector: min_area=120 square pixels, bloom_area_min=900 square pixels (connected components reaching 900 are counted as buds, below that as blooms), and morphological kernel 7. It thresholds the pink, white, and yellow petal ranges in HSV space, applies closing then opening operations, and counts connected components.
The scenes come from the repository's built-in simulator: four 1280×960 images (seeds 101/202/303/404), 210 targets in total—134 buds and 76 blooms—each with ground-truth center and radius.
Scoring abandoned the total-ratio approach in favor of greedy center-distance matching: a detection center must fall within max(38 px, 1.1 × radius) of a same-label ground-truth target to count as a hit. The system outputs precision, recall, and F1, and separately reports bud recall and bloom recall. As a control, on unperturbed scenes this strict scoring still yields precision = recall = F1 = 1.000 with zero false positives—a perfect baseline, precisely because the two are co-tuned, which is exactly the problem.
Two stress-test groups: lighting (gamma 0.6/1.6/2.2, exposure −50 and overexposure +60, left-to-right shadow gradient, half-frame shadow, warm color cast, low light with Gaussian noise) and occlusion (leaf-colored ellipses covering the true centers of 60% of targets, with coverage radii of 0.5/0.8/1.1× the target radius; plus additional buds drawn near existing buds at offsets of 2.2 to 0.45× radius, simulating clustering).
HSV color space and threshold segmentation
The HSV space in which the detector operates is a representation that decomposes color into hue (H), saturation (S), and value (V). Compared with RGB, HSV is closer to human color perception and separates color information (H, S) from brightness information (V), so it is commonly used for color-based object segmentation. In this case, the detector sets threshold ranges for the H, S, and V channels separately for pink, white, and yellow petals; pixels falling within the ranges are marked as foreground. The advantage of this method is its computational simplicity and lack of need for training data, but its drawbacks are obvious: the thresholds require manual tuning and are sensitive to lighting changes—a shift in the V channel directly causes pixels to fall outside the threshold range. Understanding this makes it clear why, in the subsequent lighting stress tests, darkening first "eats away" at the petal mask from the edges.
Lighting: First Degraded, Then Gone
The most alarming set of numbers is this: under three conditions—exposure −50, shadow gradient, and half-frame shadow—the total error is 0.0%. By the repository demo's scoring, these three runs are perfect. But in reality, one-sixth to one-quarter of the buds have already been lost, with twenty-odd ghost blooms appearing on top.
The mechanism is not hard to trace. Darkening first eats away at the petal mask from the edges: the V channel drops below the lower bound of the pink range, the mask shrinks, and the connected-component area shrinks with it. Once the area falls below the 900-square-pixel line, the target does not disappear—it is reclassified as a bloom. This classifier degrades first, then deletes: at gamma 2.2 only 45 buds remain, while "blooms" swell to 179, with the total off by only +6.7%.
Warm color cast fails from the opposite direction: buds are intact (white and yellow ranges provide a backstop), but bloom recall drops to exactly zero—blooms exist only in the pink range, and the color cast pushes their hue out of bounds. All 76 blooms are wiped out, and the 25 detected "blooms" are fragment false positives. Overexposure fails yet another way: recall stays at 1.000, but precision drops to 0.933, with 15 extra false positives—faded leaf fragments masquerading as blooms. Brightening does not lose targets; it manufactures them.
The good news is real too: gamma 0.6 brightening, darkening to gamma 1.6, and low light with noise all leave the detector unaffected—its robust range is wider than the author expected. The failure threshold lies somewhere between 1.9 and 2.2, but sampling is sparse and the 1.9 run also included noise, so the cliff's exact location cannot be pinpointed.
Gray-world white balance and CLAHE
Gray-world white balance assumes that in a normal image, the average of all colors should tend toward gray. Based on this assumption, the algorithm computes the means of the RGB channels of the image and applies gain adjustments to each channel so that the three means become equal, thereby correcting global color cast. CLAHE (Contrast Limited Adaptive Histogram Equalization) is a local contrast enhancement technique: it divides the image into small tiles, performs histogram equalization on each tile, and uses a clipping threshold to limit contrast amplification, avoiding excessive noise enhancement. The combination is often used as preprocessing to counteract uneven lighting and color temperature variation. But as the article points out, white balance cannot help images with clipped highlights—the information is already lost, and no gain can recover it; and CLAHE amplifies noise in low-light, noisy images, producing large numbers of false positives.
Preprocessing: Fixes Global Shifts, Not Clipping or Noise
Gray-world white balance plus V-channel CLAHE sorts cleanly by failure type: global brightness and hue shifts are almost entirely recovered—gamma 2.2 recall goes from 0.576 back to 1.000, warm color cast from 0.638 back to 1.000, and both shadow cases from about 0.9 back to 1.000.
The cost is equally clear: on images that were already normal, it conjures 13–18 ghost blooms out of thin air. On overexposed images the highlights are already clipped, the information is gone, and recall actually drops from perfect to 0.319. On low-light, noisy images it produces 155 false-positive blooms. The author honestly notes that white balance and CLAHE were tested as a single pipeline and were never ablated separately, so it's impossible to tell which step is causing trouble in the bad cases. But the operational conclusion holds: gate the preprocessing, first measuring the frame's global-shift statistics and enabling it only when a shift is present.
Occlusion: Small Targets Die First, Fragments Pose as Blooms
At half-radius coverage, all unoccluded targets are still found, but the 31% that are occluded are gone—and it's the blooms that fall. Bloom radii are only 11–17 pixels; once the center is covered, the residual fragments fall below min_area=120 and are discarded as noise. Buds are larger and can survive with a ring of petals remaining.
At 0.8× radius coverage, the strangest number of the entire study appears: total error +81%. Occlusion doesn't just delete; it also creates. Partially covered buds shatter into multiple pieces, each falling into the 120–900 square-pixel range, and each is recorded as a bloom: 297 blooms detected against a ground truth of only 76. A photo with dense foliage will simultaneously undercount buds and overcount blooms, and the overcounting is the larger error.
The author ran a parameter ablation, lowering min_area from 120 to 40: under light occlusion, recall rises from 0.776 to 0.981 and bloom recall from 0.382 to 0.947, at the cost of false positives rising from 4 to 49 and total error flipping from −20.5% to +21.4%. Under heavy occlusion it buys almost nothing and adds fragments. This is a precision-for-recall trade that pays off only in the light-occlusion regime—not a fix.
Connected-component analysis and area-threshold classification
Connected-component analysis identifies sets of connected pixels in a binary mask as individual objects and computes their area, bounding box, centroid, and other attributes. This detector uses connected-component area for classification: components with area greater than or equal to 900 square pixels are recorded as buds, those between 120 and 900 as blooms, and those below 120 are discarded as noise. This area-based rule is simple and intuitive but has structural flaws: when a target is partially occluded, a bud may shatter into multiple small pieces, each with an area that happens to fall in the bloom range, causing one bud to be miscounted as several blooms; when a target darkens, mask shrinkage can also push a bud's area below 900, downgrading it to a bloom. As a result, the area-threshold classifier is highly prone to category migration rather than simple missed detections under lighting and occlusion changes.
Overlap: Clusters Always Undercounted, Area Splitting Works Only in One Regime
Once two flowers touch, morphological closing welds them into a single connected component, undercounting 42–57 buds at every offset. The area-based splitting estimate does work in the 0.8–1.2× radius range (−49 → −4, −54 → +10). But at larger offsets it overcorrects (+27 at 2.2×). The overcorrection is mostly the estimator's own fault: it scales each bud box by area, while a single bud's radius naturally fluctuates between 30 and 49 pixels, so one large isolated bud gets rounded up to "one and a half." The larger the offset, the fewer real merges there are, and the more this systematic inflation dominates.
Combined stress (gamma 1.6 plus 0.8× leaf coverage) pushes recall down to 0.429, and preprocessing can only bring it to 0.500. Preprocessing can fix global shifts but cannot fix what is physically hidden—this boundary is worth stating clearly.
Four Conclusions for Practitioners
One: A counting pipeline's acceptance metrics must be per-category, per-object-matched precision/recall, plus an explicit error on the bud-to-bloom ratio. Looking only at totals would certify three conditions in the author's experiments as 0.0% error, when one-sixth to one-quarter of the buds are already wrong.
Two: Under the two most common degradations—darkening and moderate occlusion—the area-threshold classifier's main failure mode is category migration, not missed detection. Watching for drift in category composition raises the alarm earlier than watching totals. Remember the boundary too: under extreme underexposure and 1.1× coverage, targets genuinely disappear (occluded recall 0.014), and there is no migration to observe.
Three: Preprocessing should be conditionally triggered, driven by measured global shifts. On clipped or noisy inputs, it is a net harm.
Four: Small-category targets need their own protection strategy under occlusion. Lowering min_area pays off only under light occlusion. A candidate beyond single frames is cross-day tracking—a bloom hidden by today's leaf was visible yesterday and can be recovered from the time series. This idea is not validated by any experiment in this article (all are static single frames; wind sway and viewpoint changes were not tested), but the failure modes point to it and it moves the video/multi-frame deduplication item up the roadmap.
Why not just switch to a trained detector? The BloomDetector interface is already there; swapping the backend changes nothing else. But without real annotated photos there is nothing to train on. The value of this study is this: map the baseline's failure boundaries under lighting and occlusion, so that any future backend must pass the same set of destructive tests and be compared side by side on the numbers.
Limitations and Open Questions
Everything is synthetic: procedurally drawn flowers, flat-colored leaf occluders, globally parameterized lighting. Real greenhouse light has directionality and spectrum; real leaves have texture and translucency. These numbers should be read as lower bounds on failure direction and magnitude, not as field accuracy. Lighting transformations are whole-image; local specular reflections and lens flare are not modeled. The white balance/CLAHE pair was never ablated separately. The 2.2× offset overlap group is contaminated by three-way merges, so neither end of that row is a clean reading. Four scenes, 210 targets, no confidence intervals computed. The splitting estimator's per-unit area is taken from the median of this batch and must be recalibrated by variety and camera distance; this transfer has not been tested. Validation on real photos is still absent—both the trained backend and the real annotated set on the roadmap remain untouched, and this is the biggest open question on this line.
One-line Summary
Bloom-Counter's 99.7% recall stems from the simulator and detector being co-tuned; stress testing reveals structural failures under lighting and occlusion.
Paragraph Summary
The open-source project Bloom-Counter claims 99.7% detection recall, but the author's stress testing shows this figure stems from the simulator and detector being co-tuned, and that scoring only totals rather than structure cannot prove real performance. Keeping the detector unchanged, the author systematically breaks the simulator's two hidden assumptions—stable lighting and non-overlapping flowers—to measure failure modes. Results: under darkening and shadow, total error is 0.0%, yet one-sixth to one-quarter of buds are wrongly downgraded to blooms; occlusion shatters buds into bloom-sized fragments, with total error reaching +81% at 0.8× radius coverage; white balance + CLAHE preprocessing fixes global lighting shifts but is a net harm on overexposed and noisy images; area splitting works only for overlaps in the 0.8–1.2× radius range. The author draws four practical recommendations and identifies the lack of real annotated data as the biggest open question.
Related articles

Chain of Thought Isn't Just for AI: How Humans Can Use CoT to Improve Thinking Quality
Chain of Thought isn't just a prompt trick for large models. Starting from a Hacker News discussion, this article analyzes why CoT works for human thinking too, breaks down actionable ways to transfer it, and highlights the key differences between human CoT and machine CoT.
Cloudflare Teams Up with OpenAI: Closi…
Cloudflare Teams Up with OpenAI: Closing the Loop from Vulnerability Discovery to Defense with AI
Cloudflare uses OpenAI Daybreak to investigate security vulnerabilities and generate reviewable remediation plans, while developers use Codex to boost coding efficiency and quality.

Orca Multi-Agent Orchestration in Practice: A Guide to Parallel Collaboration with Claude and Codex
Orca is Stable AI's free open-source agent workbench supporting 34 agents like Claude Code and Codex in parallel. Learn its Git worktree isolation, multi-model orchestration, and mobile management.