From 7249e73e7cb0a8b2547d51930c267ae8c9417853 Mon Sep 17 00:00:00 2001 From: Olive Vaughn Date: Mon, 28 Sep 2026 09:06:30 -0400 Subject: [PATCH] Keep mouth crops independent of teeth settings --- frontend/src/arthur/events/footage.cljs | 3 +-- .../src/arthur/flow/measure/interior.cljs | 21 +++++++++--------- .../arthur/flow/measure/interior_test.cljs | 22 +++++++++++++++++++ 3 files changed, 34 insertions(+), 12 deletions(-) create mode 100644 frontend/test/arthur/flow/measure/interior_test.cljs diff --git a/frontend/src/arthur/events/footage.cljs b/frontend/src/arthur/events/footage.cljs index d6771b0..bc1d025 100644 --- a/frontend/src/arthur/events/footage.cljs +++ b/frontend/src/arthur/events/footage.cljs @@ -42,8 +42,7 @@ (.drawImage ctx image 0 0) (let [face (detect/detect! model canvas) ring (when face (mapv #(nth face %) lm/LIPS-INNER)) - box (when ring - (interior/crop ring wh (:cavity-erode take/knobs))) + box (when ring (interior/crop ring wh)) measured (if box (interior/measure take/knobs box (.getImageData ctx (:x box) (:y box) diff --git a/frontend/src/arthur/flow/measure/interior.cljs b/frontend/src/arthur/flow/measure/interior.cljs index f6f94c8..f821508 100644 --- a/frontend/src/arthur/flow/measure/interior.cljs +++ b/frontend/src/arthur/flow/measure/interior.cljs @@ -10,17 +10,17 @@ :y (+ y (* amount (- (:y p) y)))}) ring))) (defn crop - "A clamped source-pixel box for the eroded cavity ring. nil means too small - for a useful contrast measurement." - [ring [width height] cavity-erode] - (let [shape (scaled-ring ring (- 1 cavity-erode)) - x0 (max 0 (js/Math.floor (* width (reduce min (map :x shape))))) - y0 (max 0 (js/Math.floor (* height (reduce min (map :y shape))))) - x1 (min width (js/Math.ceil (* width (reduce max (map :x shape))))) - y1 (min height (js/Math.ceil (* height (reduce max (map :y shape))))) + "A clamped source-pixel box for the raw inner-lip ring. The box is independent + of teeth settings so its pixels can be reused when those settings change. nil + means too small for a useful contrast measurement." + [ring [width height]] + (let [x0 (max 0 (js/Math.floor (* width (reduce min (map :x ring))))) + y0 (max 0 (js/Math.floor (* height (reduce min (map :y ring))))) + x1 (min width (js/Math.ceil (* width (reduce max (map :x ring))))) + y1 (min height (js/Math.ceil (* height (reduce max (map :y ring))))) w (- x1 x0) h (- y1 y0)] (when (and (>= w 5) (>= h 5)) - {:x x0 :y y0 :w w :h h :shape shape + {:x x0 :y y0 :w w :h h :shape ring :source-width width :source-height height}))) (defn- point-in-poly? [points x y] @@ -133,7 +133,8 @@ image-data] (let [pixels (.-data image-data) polygon (mapv (fn [p] {:x (- (* (:x p) source-width) x) - :y (- (* (:y p) source-height) y)}) shape) + :y (- (* (:y p) source-height) y)}) + (scaled-ring shape (- 1 cavity-erode))) hist (js/Uint32Array. 256) luminance (js/Uint8Array. (* w h)) redness (js/Float32Array. (* w h)) diff --git a/frontend/test/arthur/flow/measure/interior_test.cljs b/frontend/test/arthur/flow/measure/interior_test.cljs new file mode 100644 index 0000000..9df9772 --- /dev/null +++ b/frontend/test/arthur/flow/measure/interior_test.cljs @@ -0,0 +1,22 @@ +(ns arthur.flow.measure.interior-test + (:require [cljs.test :refer [deftest is]] + [arthur.flow.measure.interior :as interior])) + +(def ^:private ring + [{:x 0.2 :y 0.2} {:x 0.8 :y 0.2} + {:x 0.8 :y 0.8} {:x 0.2 :y 0.8}]) + +(defn- region-size [box cavity-erode] + (let [image #js {:data (js/Uint8ClampedArray. (* 4 (:w box) (:h box)))} + result (interior/measure + {:cavity-erode cavity-erode :tongue-reject 0.1 :blob-grow 0 + :top-bias 0 :teeth-verts 8 :min-area 1 :debug? true} + box image)] + (count (filter pos? (array-seq (get-in result [:debug :region])))))) + +(deftest the-crop-is-reusable-while-cavity-erosion-changes-the-measurement + (let [box (interior/crop ring [100 100])] + (is (= ring (:shape box))) + (is (= [20 20 60 60] ((juxt :x :y :w :h) box))) + (is (= 3600 (region-size box 0))) + (is (= 2304 (region-size box 0.2)))))