|
|
@@ -1,17 +1,23 @@
|
|
|
"""Kalibreerimise abivahend AS7341 värviandurile.
|
|
|
|
|
|
Loeb ClickHouse'ist (vaikimisi uus67.varvus) ja toetab:
|
|
|
- * capture-white: salvesta valge referents (viimased N mõõtmist) -> calibration.json
|
|
|
- * show: näita viimaseid mõõtmisi (toores hex vs kalibreeritud hex)
|
|
|
+ * capture-dark: salvesta tume referents (andur kaetud) -> calibration.json
|
|
|
+ * capture-white: salvesta valge referents (valge pind, SAMA valgustus) -> calibration.json
|
|
|
+ * show: näita viimaseid mõõtmisi (toores vs tume+valge kalibreeritud)
|
|
|
|
|
|
-Kalibreeritud värvus = iga kanal jagatud valge referentsiga (reflectance),
|
|
|
-mis eemaldab valgustuse värvitooni ja anduri kanalite erinevuse.
|
|
|
+Kalibreeritud reflekteeruvus: R = (S - D) / (W - D) iga kanali kohta.
|
|
|
+ * Tume (D) eemaldab tumedavoolu ja plaadi toite-LED-i (roheline) panuse.
|
|
|
+ * Valge (W) eemaldab valgustuse spektri ja kanalite tundlikkuse erinevuse.
|
|
|
+
|
|
|
+Protseduur:
|
|
|
+ 1. Fikseeri valgustus ja geomeetria (nurk, kaugus) kogu seeria jooksul.
|
|
|
+ 2. Kata andur (täiesti pime) -> capture-dark --last 5
|
|
|
+ 3. Mõõda valge pind (nt valge paber/PTFE) -> capture-white --last 5
|
|
|
+ 4. Mõõda proovid -> show --last 10
|
|
|
|
|
|
Käivitamine (projekti .venv):
|
|
|
CH_URL=http://100.87.1.24:8125 CH_USER=zigbee CH_PASS=... \
|
|
|
- .venv/bin/python varvusensor/collector/calibrate.py capture-white --last 5
|
|
|
- CH_URL=http://100.87.1.24:8125 CH_USER=zigbee CH_PASS=... \
|
|
|
- .venv/bin/python varvusensor/collector/calibrate.py show --last 10
|
|
|
+ .venv/bin/python varvusensor/collector/calibrate.py capture-dark --last 5
|
|
|
"""
|
|
|
from __future__ import annotations
|
|
|
|
|
|
@@ -55,43 +61,62 @@ def fetch_rows(last: int) -> list[dict]:
|
|
|
return [json.loads(line) for line in ch_query(sql).strip().splitlines() if line]
|
|
|
|
|
|
|
|
|
-def capture_white(last: int) -> int:
|
|
|
+def load_calib() -> dict:
|
|
|
+ if not CALIB_FILE.exists():
|
|
|
+ return {}
|
|
|
+ try:
|
|
|
+ return json.loads(CALIB_FILE.read_text())
|
|
|
+ except Exception:
|
|
|
+ return {}
|
|
|
+
|
|
|
+
|
|
|
+def save_calib(data: dict) -> None:
|
|
|
+ CALIB_FILE.write_text(json.dumps(data, indent=2, ensure_ascii=False))
|
|
|
+
|
|
|
+
|
|
|
+def capture_reference(kind: str, last: int) -> int:
|
|
|
rows = fetch_rows(last)
|
|
|
if not rows:
|
|
|
- print("Andmeid pole — mõõda esmalt valget pinda.")
|
|
|
+ print("Andmeid pole — mõõda esmalt (kaetud andur / valge pind).")
|
|
|
return 1
|
|
|
ref: dict[str, float] = {}
|
|
|
for field in FIELDS:
|
|
|
values = [r[field] for r in rows if r.get(field) is not None]
|
|
|
ref[field] = round(statistics.mean(values), 3) if values else 0.0
|
|
|
- payload = {"reference_rows": len(rows), "white": ref}
|
|
|
- CALIB_FILE.write_text(json.dumps(payload, indent=2, ensure_ascii=False))
|
|
|
- print(f"Valge referents salvestatud ({len(rows)} reast) -> {CALIB_FILE}")
|
|
|
+ data = load_calib()
|
|
|
+ data[kind] = ref
|
|
|
+ data[f"{kind}_rows"] = len(rows)
|
|
|
+ save_calib(data)
|
|
|
+ label = "Tume" if kind == "dark" else "Valge"
|
|
|
+ print(f"{label} referents salvestatud ({len(rows)} reast) -> {CALIB_FILE}")
|
|
|
print(json.dumps(ref, indent=2))
|
|
|
+ if kind == "white" and "dark" not in data:
|
|
|
+ print("NB! Tume referents puudub — soovitatav teha enne capture-dark (LED/tumedavool).")
|
|
|
return 0
|
|
|
|
|
|
|
|
|
-def load_white() -> dict[str, float] | None:
|
|
|
- if not CALIB_FILE.exists():
|
|
|
- return None
|
|
|
- return json.loads(CALIB_FILE.read_text()).get("white")
|
|
|
-
|
|
|
-
|
|
|
-def reflectance(channels: dict[str, float], white: dict[str, float]) -> dict[str, float]:
|
|
|
+def reflectance(
|
|
|
+ channels: dict[str, float], dark: dict[str, float], white: dict[str, float]
|
|
|
+) -> dict[str, float]:
|
|
|
out: dict[str, float] = {}
|
|
|
for band in BANDS:
|
|
|
- ref = white.get(band, 0.0)
|
|
|
value = channels.get(band)
|
|
|
- if value is not None and ref:
|
|
|
- out[band] = value / ref
|
|
|
+ d = dark.get(band, 0.0) or 0.0
|
|
|
+ w = white.get(band, 0.0) or 0.0
|
|
|
+ if value is not None and (w - d) > 0:
|
|
|
+ out[band] = max(0.0, (value - d) / (w - d))
|
|
|
return out
|
|
|
|
|
|
|
|
|
def show(last: int) -> int:
|
|
|
rows = fetch_rows(last)
|
|
|
- white = load_white()
|
|
|
+ data = load_calib()
|
|
|
+ dark = data.get("dark") or {}
|
|
|
+ white = data.get("white")
|
|
|
if white is None:
|
|
|
- print("(kalibratsioon puudub — näitan ainult toorest; tee capture-white)")
|
|
|
+ print("(valge kalibratsioon puudub — näitan ainult toorest; tee capture-white)")
|
|
|
+ elif not dark:
|
|
|
+ print("(tume referents puudub — kasutan D=0; soovitatav capture-dark)")
|
|
|
for row in rows:
|
|
|
channels = {b: row[b] for b in BANDS if row.get(b) is not None}
|
|
|
raw = color.analyze(channels)
|
|
|
@@ -100,7 +125,7 @@ def show(last: int) -> int:
|
|
|
f"(x={raw['x_chrom']:.3f} y={raw['y_chrom']:.3f})"
|
|
|
)
|
|
|
if white is not None:
|
|
|
- cal = color.analyze(reflectance(channels, white))
|
|
|
+ cal = color.analyze(reflectance(channels, dark, white))
|
|
|
line += (
|
|
|
f" | kalib. {cal['rgb_hex']} "
|
|
|
f"(x={cal['x_chrom']:.3f} y={cal['y_chrom']:.3f})"
|
|
|
@@ -112,13 +137,17 @@ def show(last: int) -> int:
|
|
|
def main() -> int:
|
|
|
parser = argparse.ArgumentParser(description="AS7341 kalibreerimine")
|
|
|
sub = parser.add_subparsers(dest="cmd", required=True)
|
|
|
+ cd = sub.add_parser("capture-dark", help="salvesta tume referents (andur kaetud)")
|
|
|
+ cd.add_argument("--last", type=int, default=5)
|
|
|
cw = sub.add_parser("capture-white", help="salvesta valge referents")
|
|
|
cw.add_argument("--last", type=int, default=5)
|
|
|
sh = sub.add_parser("show", help="näita viimaseid mõõtmisi")
|
|
|
sh.add_argument("--last", type=int, default=10)
|
|
|
args = parser.parse_args()
|
|
|
+ if args.cmd == "capture-dark":
|
|
|
+ return capture_reference("dark", args.last)
|
|
|
if args.cmd == "capture-white":
|
|
|
- return capture_white(args.last)
|
|
|
+ return capture_reference("white", args.last)
|
|
|
if args.cmd == "show":
|
|
|
return show(args.last)
|
|
|
return 1
|