The first naive version of finger detection was tempting: if the frame is red, the finger is there. It worked on my desk for about five minutes, then failed the moment the phone angle changed. A red object, a dark room, or a leaking edge of light could all pass one simple check.

So the practical question became narrower: what does a usable fingertip frame look like, and how do we reject the frames that merely look reddish?
I stopped trusting one number
A covered camera frame is not only red. It has enough brightness, not too much brightness, a red channel that leads green and blue, and enough pixels where red is dominant. MonoPump combines those clues instead of betting on a single threshold.
This diagram could not be rendered. Its source is still available below.
View diagram source
flowchart TD
A[Sample center region] --> B[Compute average RGB]
B --> C[Brightness in usable range?]
C -- no --> D[tooDark or tooBright]
C -- yes --> E[Red ratio high enough?]
E -- no --> F[no finger]
E -- yes --> G[Red dominance and red pixels strong?]
G -- weak --> H[weak coverage]
G -- strong --> I[good coverage]
The center crop is intentional. The edges of the preview are where light leaks, finger curvature, and motion produce unstable color. Sampling the center makes the detector behave more like a tiny contact sensor.
The metrics that matter
MonoPump computes a small set of cheap metrics directly from the BGRA pixel buffer. That matters because this runs every frame.
brightness = (avgRed + avgGreen + avgBlue) / 3
redRatio = avgRed / max((avgGreen + avgBlue) / 2, 1)
redDominance = avgRed / max(avgGreen, avgBlue)
dominantRedPixelFraction = redDominantPixels / sampledPixels
Brightness catches the obvious failures. Red ratio catches the fingertip color shift. Red-dominant pixel fraction prevents a few bright red pixels from fooling the detector.
Good, weak, none is better than yes or no
The UI needs more than a Boolean. If the app only says not detected, the user has no idea whether to press harder, reduce pressure, cover the flash, or simply wait. MonoPump classifies coverage into practical states: none, too dark, too bright, weak coverage, and good.
This diagram could not be rendered. Its source is still available below.
View diagram source
stateDiagram-v2
[*] --> none
none --> weakCoverage: red appears but signal is weak
weakCoverage --> good: red dominance becomes stable
good --> weakCoverage: pressure or alignment changes
weakCoverage --> none: red ratio drops
none --> tooDark: brightness below lower bound
none --> tooBright: brightness above upper bound
The final trick is frame smoothing. One bad frame should not reset the whole measurement, and one good frame should not start it. Loose thresholds improve success rate but admit bad signals; strict thresholds improve confidence but frustrate real users. The better boundary is starting only when the next seconds are likely to produce a useful waveform.