Spaces:
Running
Running
File size: 7,676 Bytes
a183ff9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 | import os
import sys
# Khóa cứng vô hiệu hóa oneDNN / MKLDNN để chống crash trên Windows
os.environ["FLAGS_use_onednn"] = "0"
os.environ["FLAGS_enable_mkldnn"] = "0"
import cv2
import json
import numpy as np
from PIL import Image, ImageDraw
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, os.path.join(PROJECT_ROOT, "PaddleOCR"))
sys.path.insert(0, os.path.join(PROJECT_ROOT, "scripts"))
# Import PaddleOCR TextDetector
import tools.infer.predict_det as predict_det
import tools.infer.utility as utility
def get_polygon_area(poly):
p = np.array(poly, dtype=np.float32)
return cv2.contourArea(p)
def get_polygon_intersection_area(poly1, poly2):
p1 = np.array(poly1, dtype=np.float32).reshape(-1, 1, 2)
p2 = np.array(poly2, dtype=np.float32).reshape(-1, 1, 2)
ret, intersection = cv2.intersectConvexConvex(p1, p2)
if ret > 0:
return float(ret)
return 0.0
def calculate_iou(poly1, poly2):
inter = get_polygon_intersection_area(poly1, poly2)
area1 = get_polygon_area(poly1)
area2 = get_polygon_area(poly2)
union = area1 + area2 - inter
if union <= 0:
return 0.0
return inter / union
def main():
print("🔍 Bắt đầu đánh giá hiệu năng DBNet Detector...")
det_root = os.path.join(PROJECT_ROOT, "data", "detector")
val_label_path = os.path.join(det_root, "det_val_label.txt")
evidence_dir = os.path.join(PROJECT_ROOT, "output", "evidence", "detector")
contact_sheets_dir = os.path.join(evidence_dir, "contact_sheets")
os.makedirs(contact_sheets_dir, exist_ok=True)
if not os.path.exists(val_label_path):
print(f"❌ Không tìm thấy tệp nhãn validation: {val_label_path}")
return
# Khởi tạo TextDetector
sys_argv_backup = sys.argv
sys.argv = [sys.argv[0]]
args = utility.parse_args()
sys.argv = sys_argv_backup
args.det_model_dir = os.path.join(PROJECT_ROOT, "data", "pretrain_models", "ch_PP-OCRv4_det_infer")
args.use_gpu = False
args.enable_mkldnn = False
args.det_limit_side_len = 960
print("⚙️ Đang nạp mô hình DBNet...")
detector = predict_det.TextDetector(args)
# Đọc nhãn val
val_lines = []
with open(val_label_path, "r", encoding="utf-8") as f:
for line in f:
if line.strip():
val_lines.append(line.strip().split("\t"))
total_gt = 0
total_dt = 0
recalled_gt = 0
precise_dt = 0
usable_crops = 0
all_ious = []
print(f"📈 Đang đánh giá trên {len(val_lines)} trang tài liệu...")
for idx, (img_rel_path, label_str) in enumerate(val_lines):
img_path = os.path.join(det_root, img_rel_path)
if not os.path.exists(img_path):
print(f" ❌ Không tìm thấy ảnh: {img_path}")
continue
gt_boxes = json.loads(label_str)
img_cv2 = cv2.imread(img_path)
# Chạy detector
dt_boxes, _ = detector(img_cv2)
# PIL Image phục vụ vẽ overlays
pil_img = Image.open(img_path).convert("RGB")
draw = ImageDraw.Draw(pil_img)
# Mapping GT và DT boxes
gt_matched = [False] * len(gt_boxes)
dt_matched = [False] * len(dt_boxes)
print(f"\n📄 Trang {idx+1:02d} ({os.path.basename(img_rel_path)}):")
# Tính IoU và Recall/Precision
for gt_idx, gt in enumerate(gt_boxes):
gt_poly = gt["points"]
best_iou = 0.0
best_dt_idx = -1
best_inter = 0.0
for dt_idx, dt_poly in enumerate(dt_boxes):
iou = calculate_iou(gt_poly, dt_poly)
if iou > best_iou:
best_iou = iou
best_dt_idx = dt_idx
best_inter = get_polygon_intersection_area(gt_poly, dt_poly)
print(f" GT {gt_idx:2d} -> Max IoU với DT: {best_iou:.4f}")
# Sử dụng ngưỡng IoU >= 0.50 (chuẩn Pascal VOC) để đánh giá tài liệu biến dạng
if best_iou >= 0.50:
recalled_gt += 1
gt_matched[gt_idx] = True
dt_matched[best_dt_idx] = True
all_ious.append(best_iou)
# 2. Crop usability check (GT nằm trọn trong DT: giao >= 95% diện tích GT)
gt_area = get_polygon_area(gt_poly)
if gt_area > 0 and (best_inter / gt_area) >= 0.95:
usable_crops += 1
# Tính Precision
for dt_idx, dt_poly in enumerate(dt_boxes):
best_iou = 0.0
for gt_idx, gt in enumerate(gt_boxes):
iou = calculate_iou(gt["points"], dt_poly)
if iou > best_iou:
best_iou = iou
if best_iou >= 0.50:
precise_dt += 1
total_gt += len(gt_boxes)
total_dt += len(dt_boxes)
# Vẽ Overlay Contact Sheet
# - Màu xanh lá: Predicted polygon chính xác (IoU >= 0.5)
# - Màu đỏ nét đứt: Ground-truth bị bỏ sót (Recall fail)
# - Màu vàng: Predicted polygon sai lệch (False positive)
for dt_idx, dt_poly in enumerate(dt_boxes):
pts = [tuple(p) for p in dt_poly]
pts.append(pts[0])
color = (0, 180, 0) if dt_matched[dt_idx] else (200, 200, 0)
draw.line(pts, fill=color, width=2)
for gt_idx, gt in enumerate(gt_boxes):
if not gt_matched[gt_idx]:
pts = [tuple(p) for p in gt["points"]]
pts.append(pts[0])
draw.line(pts, fill=(200, 0, 0), width=2)
# Lưu ảnh Contact Sheet
contact_sheet_name = f"contact_sheet_{os.path.basename(img_rel_path)}"
pil_img.save(os.path.join(contact_sheets_dir, contact_sheet_name))
# Tính tổng hợp chỉ số
recall_rate = (recalled_gt / total_gt) * 100 if total_gt > 0 else 0.0
precision_rate = (precise_dt / total_dt) * 100 if total_dt > 0 else 0.0
usability_rate = (usable_crops / total_gt) * 100 if total_gt > 0 else 0.0
mean_iou = np.mean(all_ious) if all_ious else 0.0
results = {
"evaluation_summary": {
"total_val_pages": len(val_lines),
"total_ground_truth_lines": total_gt,
"total_detected_lines": total_dt,
"mean_iou": round(float(mean_iou), 4),
"line_recall": round(recall_rate, 2),
"line_precision": round(precision_rate, 2),
"crop_usability_rate": round(usability_rate, 2)
}
}
# Ghi báo cáo JSON
results_path = os.path.join(evidence_dir, "iou_matching.json")
with open(results_path, "w", encoding="utf-8") as f:
json.dump(results, f, indent=4)
print("\n================== DETECTOR EVALUATION REPORT ==================")
print(f"Line Recall : {recall_rate:.2f}% ({recalled_gt}/{total_gt})")
print(f"Line Precision : {precision_rate:.2f}% ({precise_dt}/{total_dt})")
print(f"Mean IoU : {mean_iou:.4f}")
print(f"Crop Usability Rate : {usability_rate:.2f}% ({usable_crops}/{total_gt})")
print(f"Saved matching report to {results_path}")
print(f"Saved contact sheets under {contact_sheets_dir}")
print("================================================================")
if __name__ == '__main__':
main()
|