Per-platform colour-border sensitivity (Teams violet, Meet glow)

Cross-platform research (Grant) flagged that the colour-border cue differs by app;
checking the real brand colours against the detector found a concrete bug: the
global 0.5 saturation threshold MISSES Teams' violet ring (#6264A7 ≈ 0.41, light
variants ~0.27) entirely and Meet's lighter blue glow (#8ab4f8 ≈ 0.44). Those
adapters would have detected nothing.

- FrameSampler.saturatedPoints: add a tunable threshold + optional hue-band gate
  (degrees) so a lowered threshold doesn't pick up warm video.
- GridCallAnalyzer.Config: colorSaturation / colorMinBrightness / colorHueRange,
  plumbed to the colour-border path (defaults preserve prior behaviour).
- MeetAdapter sat→0.35 (catch the glow); TeamsAdapter sat→0.22 + hue 215–275°
  (catch the faint violet, reject other colours); ZoomAdapter sat 0.45 + hue
  40–150° (vivid green/yellow). Values are first-pass pending real-fixture
  calibration; the hue gate is the main calibration lever.

Tests: Teams now detects the faint violet ring and rejects a green one; Meet/Zoom
vivid cases still pass. 27/27 XCTest.
This commit is contained in:
Grant Gilliam
2026-06-06 10:51:12 -05:00
parent 3785f6bdd0
commit 63cf3026ff
6 changed files with 72 additions and 8 deletions
@@ -117,6 +117,24 @@ final class GridCallAnalyzerTests: XCTestCase {
XCTAssertEqual(speaking, ["ALEX"])
}
func testTeamsDetectsFaintVioletRing() {
// Teams' brand violet #6264A7 is only ~0.41 saturation below the 0.5
// default that Meet/Zoom inherited. The Teams adapter lowers the threshold,
// so it must still pick the ring.
let violet = CGColor(red: 0.384, green: 0.392, blue: 0.655, alpha: 1)
let obs = TeamsAdapter().analyze(cgImage: coloredFrame(speakingIndex: 2, border: violet), at: 0) // DMITRI
let speaking = Set(obs.filter { $0.speaking }.map { $0.name })
XCTAssertEqual(speaking, ["DMITRI"])
}
func testTeamsHueGateRejectsWrongColourBorder() {
// A green border (wrong hue for Teams) must NOT register the violet hue
// gate is what keeps the lowered threshold from catching warm/other content.
let green = CGColor(red: 0.2, green: 0.85, blue: 0.3, alpha: 1)
let obs = TeamsAdapter().analyze(cgImage: coloredFrame(speakingIndex: 0, border: green), at: 0)
XCTAssertTrue(obs.filter { $0.speaking }.isEmpty)
}
func testWhiteBorderDetectorIgnoresColouredBorder() {
// Signal looks only for the white border, so a coloured (Meet) border must
// not register as a Signal speaker.