code wiki / (root) / nx_deadair.nx

nx_deadair.nx source

↩ module page · 65 lines · 2741 B

1// nx_deadair.nx -- per-interval temporal-activity metric for cam-stream 2// dead-air detection / TiVo-style trimming. R0 of the MEDIA-STUDIO arc. 3// 4// "Dead air" = the subject/scene is not changing (static hold, frozen 5// recording, empty room). We measure the FRACTION of pixels whose 6// frame-to-frame luma change exceeds a sensor-noise floor, expressed in 7// per-mille (parts per thousand, 0..1000). 8// 9// Why a noise floor is REQUIRED (not optional): 10// Real sensor noise makes nearly every pixel differ by a small amount 11// frame-to-frame. A naive sum/count of |a-b| therefore reads a static 12// but noisy frame as "active" -- a false positive that would defeat 13// trimming. Flooring counts a pixel as "changed" only when its luma 14// delta exceeds the noise floor, so only genuine subject/scene motion 15// survives. The gate proves this with floor=0 vs floor=N contrast. 16// 17// Thresholds are CALLER-SUPPLIED -- the metric carries NO magic numbers. 18// Callers source noise_floor and active_permille from svc-config. 19// 20// genealogy_id: frame_differencing + scene_change_detection 21// (commercial/dead-air advance lineage -- attribution 22// UNVERIFIED, record-hint only; do not cite as sourced) 23// lineage_id: frame_difference + noise_floor + changed_pixel_fraction 24// 25// nx_safety_envelope: 26// intended_use: "Temporal activity classification for recording 27// trim/cut. Foundation for the media-studio editor." 28// verdict: NOT_YET_EVALUATED 29// license_tier: ORIGINAL 30 31import "syscalls.nx" 32import "nx_image.nx" 33 34const NX_DEADAIR_ACTIVE: i64 = 1 35const NX_DEADAIR_DEAD: i64 = 0 36 37// Changed-pixel fraction (per-mille) over a same-size 1-channel pair. 38// A pixel counts as changed when |luma(a) - luma(b)| > noise_floor. 39// Returns 0..1000. Pass noise_floor=0 to disable flooring (naive diff). 40func nx_deadair_activity_permille(a: *Image, b: *Image, noise_floor: i64) -> i64 { 41 let w: i64 = a.width 42 let h: i64 = a.height 43 let total: i64 = w * h 44 if total <= 0 { return 0 } 45 var changed: i64 = 0 46 var y: i64 = 0 47 while y < h { 48 var x: i64 = 0 49 while x < w { 50 var d: i64 = nx_image_get(a, x, y, 0) - nx_image_get(b, x, y, 0) 51 if d < 0 { d = -d } 52 if d > noise_floor { changed = changed + 1 } 53 x = x + 1 54 } 55 y = y + 1 56 } 57 return changed * 1000 / total 58} 59 60// Classify one interval from its activity: 1 = ACTIVE, 0 = DEAD-AIR. 61// active_permille is the data-driven threshold (>= => ACTIVE). 62func nx_deadair_classify(activity_permille: i64, active_permille: i64) -> i64 { 63 if activity_permille >= active_permille { return NX_DEADAIR_ACTIVE } 64 return NX_DEADAIR_DEAD 65}