Spaces:
Sleeping
Sleeping
| import itertools | |
| # LCS 相似度計算(忽略大小寫) | |
| def lcs_score(a: str, b: str) -> float: | |
| a = a.lower() | |
| b = b.lower() | |
| m, n = len(a), len(b) | |
| dp = [[0] * (n + 1) for _ in range(m + 1)] | |
| for i in range(m): | |
| for j in range(n): | |
| if a[i] == b[j]: | |
| dp[i + 1][j + 1] = dp[i][j] + 1 | |
| else: | |
| dp[i + 1][j + 1] = max(dp[i][j + 1], dp[i + 1][j]) | |
| lcs_len = dp[-1][-1] | |
| return lcs_len / max(m, n) | |
| def match_ocr_to_front_back_by_permuted_ocr(ocr_texts, df, threshold=0.8): | |
| best_front = {"score": 0.0, "text": "", "match": None, "row": None} | |
| best_back = {"score": 0.0, "text": "", "match": None, "row": None} | |
| # === 特例:藥袋內容快速比對 === | |
| combined_all = ''.join(ocr_texts).upper() | |
| keywords = {"ACETYLCYSTEINE", "ACTEIN"} | |
| if any(kw in combined_all for kw in keywords): | |
| matched_rows = df[df["文字"].str.contains("ACETYLCYSTEINE|ACTEIN", case=False, na=False)] | |
| if not matched_rows.empty: | |
| match_row = matched_rows.iloc[0] | |
| return { | |
| "front": { | |
| "score": 1.0, | |
| "text": "藥袋特例", | |
| "match": "ACETYLCYSTEINE / ACTEIN", | |
| "row": match_row | |
| } | |
| } | |
| # === 排列 OCR 結果再逐一比對 === | |
| permutations = itertools.permutations(ocr_texts) | |
| for perm in permutations: | |
| combined_ocr = ''.join(perm).upper() | |
| for _, row in df.iterrows(): | |
| text_field = str(row.get("文字", "")).strip() | |
| parts = text_field.split('|') | |
| front_text = "" | |
| back_text = "" | |
| for p in parts: | |
| if ':' in p: | |
| k, v = p.split(':', 1) | |
| key = k.strip().upper() | |
| val = v.strip().upper() | |
| if key == "F": | |
| front_text = val | |
| elif key == "B": | |
| back_text = val | |
| # 比對 F | |
| if front_text: | |
| score_f = lcs_score(combined_ocr, front_text) | |
| print(f"[DEBUG-F] 比對 {combined_ocr} ↔ {front_text} ➜ score = {score_f:.3f}") | |
| if score_f > best_front["score"]: | |
| best_front.update({"score": score_f, "text": combined_ocr, "match": front_text, "row": row}) | |
| # 比對 B | |
| if back_text: | |
| score_b = lcs_score(combined_ocr, back_text) | |
| print(f"[DEBUG-B] 比對 {combined_ocr} ↔ {back_text} ➜ score = {score_b:.3f}") | |
| if score_b > best_back["score"]: | |
| best_back.update({"score": score_b, "text": combined_ocr, "match": back_text, "row": row}) | |
| # === 判斷是否達門檻 === | |
| result = {} | |
| if best_front["score"] >= threshold: | |
| # print("最佳正面比對結果:", best_front["match"], f"(score={best_front['score']:.3f})") | |
| result["front"] = best_front | |
| if best_back["score"] >= threshold: | |
| # print("最佳背面比對結果:", best_back["match"], f"(score={best_back['score']:.3f})") | |
| result["back"] = best_back | |
| # === 不達門檻時,取分數最高的結果 === | |
| if not result: | |
| if best_front["score"] >= 0.5: | |
| # print("⚠沒有達門檻,但採用最接近的 FRONT 結果") | |
| result["front"] = best_front | |
| elif best_back["score"] >= 0.5: | |
| # print("⚠沒有達門檻,但採用最接近的 BACK 結果") | |
| result["back"] = best_back | |
| return result if result else None | |
| def match_top_n_ocr_to_front_back(ocr_texts, df, threshold=0.8, top_n=3): | |
| results = [] | |
| combined_all = ''.join(ocr_texts).upper() | |
| keywords = {"ACETYLCYSTEINE", "ACTEIN"} | |
| if any(kw in combined_all for kw in keywords): | |
| matched_rows = df[df["文字"].str.contains("ACETYLCYSTEINE|ACTEIN", case=False, na=False)] | |
| if not matched_rows.empty: | |
| match_row = matched_rows.iloc[0] | |
| return [{ | |
| "score": 1.0, | |
| "text": "藥袋特例", | |
| "match": "ACETYLCYSTEINE / ACTEIN", | |
| "row": match_row, | |
| "side": "front" | |
| }] | |
| permutations = itertools.permutations(ocr_texts) | |
| for perm in permutations: | |
| combined_ocr = ''.join(perm).upper() | |
| for _, row in df.iterrows(): | |
| text_field = str(row.get("文字", "")).strip() | |
| parts = text_field.split('|') | |
| front_text, back_text = "", "" | |
| for p in parts: | |
| if ':' in p: | |
| k, v = p.split(':', 1) | |
| key = k.strip().upper() | |
| val = v.strip().upper() | |
| if key == "F": | |
| front_text = val | |
| elif key == "B": | |
| back_text = val | |
| # 比對 F | |
| if front_text: | |
| score_f = lcs_score(combined_ocr, front_text) | |
| if score_f >= 0.5: | |
| results.append({ | |
| "score": score_f, | |
| "text": combined_ocr, | |
| "match": front_text, | |
| "row": row, | |
| "side": "front" | |
| }) | |
| # print(f"[DEBUG-F] 比對 {combined_ocr} ↔ {front_text} ➜ score = {score_f:.3f}") | |
| # 比對 B | |
| if back_text: | |
| score_b = lcs_score(combined_ocr, back_text) | |
| # print(f"[DEBUG-B] 比對 {combined_ocr} ↔ {back_text} ➜ score = {score_b:.3f}") | |
| if score_b >= 0.5: | |
| results.append({ | |
| "score": score_b, | |
| "text": combined_ocr, | |
| "match": back_text, | |
| "row": row, | |
| "side": "back" | |
| }) | |
| # 優先保留高於 threshold 的,再補滿 top_n | |
| filtered = [r for r in results if r["score"] >= threshold] | |
| if len(filtered) >= top_n: | |
| return sorted(filtered, key=lambda r: -r["score"])[:top_n] | |
| else: | |
| return sorted(results, key=lambda r: -r["score"])[:top_n] | |