01Turning a multiplier into an offset
As the physics article shows, a banded photo is the true photo times a gain that depends only on the row and the colour channel. Two transformations make that easier to work with.
First, undo the camera's brightness curve. JPEG values are gamma-encoded, so a 50% brightness drop doesn't halve the pixel number. Bandaid converts every pixel to linear light with the sRGB formula, where light really does add and multiply.
Second, take the logarithm. Logs turn multiplication into addition:
Bandaid also sets two kinds of pixel aside: very dark ones, where JPEG noise swamps any signal, and clipped highlights, where the true value is unknown. Those carry no band information.
02Separating bands from the picture
Compare each row with the row above it. Every pixel's change is the scene's change plus the band's change, and the band's part is the same everywhere along the row. Most of the scene changes very little from one row to the next. The exceptions are edges: a shoulder, a table top, the bottom of a letter.
An average of those changes gets dragged around by every edge. The median (the middle value) ignores them as long as fewer than half the pixels in the row are on an edge. Bandaid also throws out any change bigger than about half a stop: a real band edge is spread over several rows, while scene edges are sudden. Add the medians up row by row and you get the band profile.
- Error vs true profile
- –
Bandaid computes these medians separately in 16 vertical strips (zones) across the frame. That gives the detector 16 independent views of the same bands, and lets the correction vary from one side of the frame to the other.
03Finding the rhythm
Most photos have some structure from row to row. What makes banding stand out is that it repeats at a fixed spacing. So Bandaid takes a Fourier transform of each zone's row-to-row changes: a breakdown of the signal into repeating waves at every possible spacing.
Natural images put most of their energy into slow changes. Using row-to-row differences rather than raw brightness flattens that out, so a steady band rhythm stands out as a sharp spike. Bandaid scores the spike in two ways:
- Prominence: how far the spike rises above the surrounding spectrum, measured as the median of nearby frequencies.
- Coverage: how many of the 16 zones show their own spike at the same spacing. Real banding covers wide areas. A striped shirt or a railing that happens to repeat is usually confined to a few zones.
The zones are combined by their power alone, ignoring where in its cycle each zone's pattern is. That matters for LED walls, where tiles band in step with each other's rhythm but not with each other's timing. Confidence is a smooth function of prominence and coverage. Below about 50%, the app treats the photo as clean and leaves it untouched unless you force the fix.
- Spacing found
- –
- Prominence
- –
- Coverage
- –
- Confidence
- –
Harmonics, and picking the true spacing
Sharp-edged bands aren't smooth waves. Like a square wave, they contain the main rhythm plus echoes at ½, ⅓ and ¼ of its spacing. Taking differences boosts those high-frequency echoes, so the tallest spike is sometimes an echo. Bandaid checks for the lowest clear spike that the tallest one is a whole-number multiple of, and uses that as the true spacing.
It also runs the whole detection on the photo rotated 90°. A portrait shot from a landscape-oriented sensor bands left to right, and the direction that scores higher wins.
04Measuring the bands locally
Knowing the spacing isn't enough. To divide out the bands, Bandaid needs their exact shape and position everywhere in the frame. Two steps get it there.
Fine-tuning the spacing. The spectrum gives the spacing to within a few percent. Over 60 or more bands, a 1% error drifts the pattern by more than half a band. Bandaid stacks every row onto a single cycle at a candidate spacing, for each zone. The right spacing gives the crispest stacked shape; a wrong one smears it. It tries 81 candidates and keeps the sharpest.
Lock-in demodulation. This is the trick borrowed from lab instruments. To measure a weak signal at a known frequency, you multiply it by a sine and a cosine at that frequency and then smooth the results heavily. Everything at other frequencies averages to nothing. What survives is the local strength and timing of the band rhythm. Bandaid does this per zone, for the main rhythm and up to 12 harmonics, so it captures sharp band shapes as well. Smoothing over about one band means it can follow changes, like a tile boundary, within a band or two.
- Left after one-shape fix
- –
- Left after local demodulation
- –
05Choosing what “correct” means
The measured band signal says how much each row was darkened relative to its neighbours, but not relative to what. Bandaid offers two reference points:
- Brightest (default): the rows that caught the whole light pulse are the true exposure. Bandaid finds the local bright-band level using the 80th percentile over one band cycle, rather than the maximum, so noise and overshoot don't inflate it. It then lifts every row up to that level.
- Average: flattens the bands around their local mean. The overall exposure stays the same, with the dark rows lifted and the bright rows lowered equally. This is useful if the brightest version looks too hot.
The red, green and blue corrections are computed separately, which is how magenta and green tints get removed along with the brightness bands.
06Mopping up, and a second light source
After the first correction, Bandaid measures again on the corrected image and adds any leftover, so whatever the first pass missed gets a second chance. Then it re-runs the detector on the result. If a different clear rhythm shows up, such as a second fixture on another dimming frequency, it removes that too, for up to three light sources. This is what cleared the 8-pixel leftover on one of the festival photos.
07Applying it to the full-resolution file
Analysis runs on a copy at most 3,000 pixels on its longest side. That keeps it fast and memory-safe on a 45-megapixel file, and bands are smooth enough that nothing is lost. The corrections are stored as smooth curves per zone and per colour. On export, Bandaid scales them to the original size, blends between neighbouring zones, and multiplies every pixel in linear light, using lookup tables so a large file takes about a second.
The camera's EXIF block (lens, settings, capture time, copyright) is copied into the new JPEG, with the orientation tag reset because the pixels have already been rotated upright. Everything happens in your browser; the photo never leaves your machine.
- Banding before
- –
- Banding after
- –
- Spacing
- –
- Time
- –
08Where it struggles
- Very faint, very local bands. A light band on a small patch of backdrop can sit below the detector's threshold, because loosening it would also start "fixing" clean photos. Use Fix even if no bands are detected for those.
- Things in front of a banded wall. The correction is smooth across each zone, so a face in the same strip as a heavily banded wall can pick up a faint lift. Real lighting usually bands the subject too, which hides this.
- Lost detail. Rows that caught almost no light are mostly noise. Brightening them brightens the noise too, and clipped highlights can't be rebuilt.
- Real stripes. Blinds, railings or a striped shirt filling the frame at a steady spacing can look like bands. The coverage check usually catches them.