| |
| """ |
| render.py |
| ํ์ ๊ฒฐ๊ณผ โ ๊ฒ์ฌ๊ด์ฉ ์ถ๋ ฅ (๋ํ ์ฌ๋ผ์ด์ค ์ค๋ฒ๋ ์ด + ๋ณด๊ณ ์ + RAG ์กฐ์น ์ง์นจ). |
| |
| ํ์ ๊ท์น(๋ฐ์ ๊น๋ฆฐ ๋ชจ๋ธ์ด ๋ฌ๋ผ์): |
| - ๋ค๊ณต์ฑ(porosity) : seg ๋ง์คํฌ โ ๋นจ๊ฐ ๋ฐํฌ๋ช
(๋ํ ์ฌ๋ผ์ด์ค์๋ง lazy ๊ณ์ฐ) |
| - ๋ ์ง(resin) : ๊ฒ์ถ bbox โ ๋นจ๊ฐ ์ฌ๊ฐํ |
| - ํฝ์ฐฝ(swelling) : ์์น ์์(๋ถ๋ฅ๊ธฐ) โ ํ์ ์ ํจ |
| |
| ์ขํ๊ณ: ๊ฒ์ถ ๋ฐ์ค/ํ์ ์ด๋ฏธ์ง ๋ชจ๋ 'recipe ํฌ๋กญ' ์ขํ๊ณ๋ผ ๊ทธ๋๋ก ๊ฒน์ณ์ง๋ค. |
| """ |
| import os |
| import sys |
|
|
| import cv2 |
| import numpy as np |
|
|
| import recipe |
| from decide import DEFECT_CANON |
| from data import open_slice |
| from seg_tiles import seg_porosity_mask |
|
|
| |
| |
| def _find_rag_dir(): |
| here = os.path.dirname(os.path.abspath(__file__)) |
| cands = [os.environ.get("GLUE_RAG_DIR"), |
| os.path.join(here, "rag"), |
| os.path.join(here, "teammate", "rag"), |
| os.path.join(os.path.dirname(here), "teammate", "rag")] |
| for c in cands: |
| if c and os.path.exists(os.path.join(c, "generate_guidance.py")): |
| return c |
| return cands[-1] |
|
|
| _RAG_DIR = _find_rag_dir() |
| if _RAG_DIR not in sys.path: |
| sys.path.insert(0, _RAG_DIR) |
|
|
|
|
| |
| |
| |
| def _crop_rgb(slices, idx): |
| """idx ์ฌ๋ผ์ด์ค์ ํฌ๋กญ ์ด๋ฏธ์ง(๊ฒ์ถ/ํ์ ๊ณตํต ์ขํ๊ณ) โ RGB ndarray.""" |
| sl = next((s for s in slices if s["idx"] == idx), None) |
| if sl is None: |
| return None |
| return np.array(recipe.to_rgb(open_slice(sl))) |
|
|
|
|
| def pick_slice(result, slices): |
| """๋ํ ์ฌ๋ผ์ด์ค = ๊ฒ์ถ ์ ๋ขฐ๋๊ฐ ๊ฐ์ฅ ๋์ ์ฌ๋ผ์ด์ค. |
| ๊ฒ์ถ ๋ฐ์ค๊ฐ ์์ผ๋ฉด(์: swelling ๋จ๋
) (None, None) โ ์์น ํ์ ์๋ต. |
| ๋ฐํ: (slice_idx, crop_rgb ndarray).""" |
| best_idx, best_conf = None, -1.0 |
| for box in result["porosity"]["boxes"] + result["resin"]["boxes"]: |
| cf, si = box[4], int(box[5]) |
| if cf > best_conf: |
| best_conf, best_idx = cf, si |
| if best_idx is None: |
| return None, None |
| return best_idx, _crop_rgb(slices, best_idx) |
|
|
|
|
| def _blend_mask(img, mask, color=(255, 0, 0), alpha=0.45): |
| """๋ง์คํฌ ์์ญ ๋ฐํฌ๋ช
์(RGB).""" |
| overlay = img.copy() |
| overlay[mask.astype(bool)] = color |
| return cv2.addWeighted(overlay, alpha, img, 1 - alpha, 0) |
|
|
|
|
| def make_overlay(crop_rgb, result, cur, seg_model=None): |
| """๋ํ ์ฌ๋ผ์ด์ค(cur) ์์ ๊ทธ ์ฌ๋ผ์ด์ค์ ๊ฒฐํจ๋ง ๊ทธ๋ฆฐ๋ค.""" |
| if crop_rgb is None: |
| return None |
| img = crop_rgb.copy() |
|
|
| |
| drew_mask = False |
| if seg_model is not None and result["porosity"]["flag"]: |
| try: |
| mask, _ = seg_porosity_mask(seg_model, crop_rgb, result["cell_type"]) |
| if mask.any(): |
| img = _blend_mask(img, mask) |
| drew_mask = True |
| except Exception as e: |
| print(f"[seg ์๋ต] {e}", file=sys.stderr) |
| if not drew_mask: |
| for x1, y1, x2, y2, cf, si in result["porosity"]["boxes"]: |
| if si == cur: |
| cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2) |
|
|
| |
| for x1, y1, x2, y2, cf, si in result["resin"]["boxes"]: |
| if si == cur: |
| cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 128, 0), 2) |
| |
| return img |
|
|
|
|
| |
| |
| |
| def make_report(result, decision): |
| sw = result["swelling"] |
| return { |
| "๋ฐฐํฐ๋ฆฌ": result["battery_id"], |
| "์
ํํ": result["cell_type"], |
| "ํ์ ": decision["zone"], |
| "๊ฒฐํจ ์ข
๋ฅ": [DEFECT_CANON[d] for d in decision["defects"]] or ["์์"], |
| "์ต๊ณ ์ ๋ขฐ๋": round(decision["worst_conf"], 3), |
| "swelling ๋น์จ": f"{sw['ratio']*100:.0f}% ({sw['n_swell']}/{result['n_slices']})", |
| "์ฌ์ ": decision["reasons"], |
| } |
|
|
|
|
| def recommend(result, decision): |
| """RAG(generate_guidance) โ IATA ๊ท์ ๊ธฐ๋ฐ ์กฐ์น ์ง์นจ(ํ/์). |
| ๊ฒฐํจ ์์ผ๋ฉด '์ด์์์' ํ๊ธฐ(API ํธ์ถ ์์). ํค/๋คํธ์ํฌ ์์ผ๋ฉด ๊ฒฐ์ ์ ๋งคํ์ผ๋ก ํด๋ฐฑ.""" |
| import generate_guidance as gg |
|
|
| defects = [DEFECT_CANON[d] for d in decision["defects"]] |
| if not defects: |
| return f"{gg.NORMAL_KO}\n\n{gg.NORMAL_EN}" |
|
|
| battery = {"battery_id": result["battery_id"], |
| "form": result["cell_type"], "defects": defects} |
| try: |
| kb, by_id = gg.load_kb() |
| chunks = gg.retrieve(defects, kb, by_id) |
| return gg.generate(battery, chunks) |
| except Exception as e: |
| |
| try: |
| kb, by_id = gg.load_kb() |
| mapping = kb["meta"]["defect_to_chunk"] |
| refs = [] |
| for d in defects: |
| for cid in mapping.get(d, []): |
| c = by_id.get(cid) |
| if c: |
| refs.append(f"- [{c['source']} ยท {c['ref']}] {c['text_ko']}") |
| body = "\n".join(dict.fromkeys(refs)) or "(๋งคํ ์กฐํญ ์์)" |
| return (f"โ ๏ธ AI ์ง์นจ ์์ฑ ๋ถ๊ฐ({e}) โ ๊ท์ ๋งคํ๋ง ํ์.\n" |
| f"ํ์ง ๊ฒฐํจ: {', '.join(defects)}\n{body}") |
| except Exception: |
| return f"โ ๏ธ ์ง์นจ ์์ฑ ๋ถ๊ฐ: {e}" |
|
|