Download code/rule_16.py from huawei-ad/code_hw_object_v2: direct link, hf CLI and curl.
- Browser
- Download file 8.85 kB
-
https://huggingface.co/huawei-ad/code_hw_object_v2/resolve/main/code/rule_16.py
- Command line
-
hf download hf://huawei-ad/code_hw_object_v2/code/rule_16.py
-
curl -L -o rule_16.py https://huggingface.co/huawei-ad/code_hw_object_v2/resolve/main/code/rule_16.py
8.85 kB
| import os, json | |
| import numpy as np | |
| from pathlib import Path | |
| from typing import Iterable, Optional, Union | |
| from ultralytics import YOLO | |
| import re | |
| promotion_keywords = [ | |
| r"领", r"申请", r"立即",r"马上",r"即刻",r"立即",r"下载", | |
| r"一定",r"现发", | |
| r"大牌美食", | |
| ] | |
| extra_keywords = [ | |
| r"立即领取",r"点击领取",r"申请我的额度",r"免费观看", | |
| r"火热选购", | |
| r"立即前往",r"立即下载",r"立即投保",r"立即参与",r"立即解救",r"立即领取",r"立即抢购",r"立即购买",r"立即签到", | |
| r"测一测",r"一定要买",r"现摘现发", | |
| r"登录",r"上滑", | |
| ] | |
| compiled_patterns = [re.compile(p, flags=re.IGNORECASE) for p in (promotion_keywords + extra_keywords)] | |
| compiled_extra_patterns = [re.compile(p, flags=re.IGNORECASE) for p in extra_keywords] | |
| def extract_texts(ocr_result) -> str: | |
| # 支持 str / [str] / dict / list[dict/...] | |
| texts = [] | |
| texts = ocr_result[0]['rec_texts'] | |
| scores = ocr_result[0]['rec_scores'] | |
| coordss = ocr_result[0]['rec_polys'] | |
| ocr_result = [ | |
| (pts, (txt, conf)) | |
| for pts, txt, conf in zip(coordss, texts, scores) | |
| ] | |
| for res in ocr_result: | |
| coords, (text, confidence) = res | |
| if confidence > 0.5: | |
| texts.append(text) | |
| merged = " ".join([t for t in texts if t]).strip() | |
| merged = re.sub(r"\s+", "", merged) | |
| print(merged) | |
| return merged | |
| def merge_by_rows(ocr_result, y_threshold=40): | |
| blocks = [] | |
| texts = ocr_result[0]['rec_texts'] | |
| scores = ocr_result[0]['rec_scores'] | |
| coordss = ocr_result[0]['rec_polys'] | |
| ocr_result = [ | |
| (pts, (txt, conf)) | |
| for pts, txt, conf in zip(coordss, texts, scores) | |
| ] | |
| for pts, (txt, conf) in ocr_result: | |
| if conf < 0.5 or not txt.strip(): | |
| continue | |
| ys = [p[1] for p in pts] | |
| xs = [p[0] for p in pts] | |
| blocks.append({ | |
| "text": txt.strip(), | |
| "x_min": min(xs), | |
| "y_max": max(ys) | |
| }) | |
| if not blocks: | |
| return "" | |
| blocks.sort(key=lambda b: b["y_max"]) | |
| lines, cur = [], [blocks[0]] | |
| for blk in blocks[1:]: | |
| if abs(blk["y_max"] - cur[0]["y_max"]) <= y_threshold: | |
| cur.append(blk) | |
| else: | |
| lines.append(cur) | |
| cur = [blk] | |
| lines.append(cur) | |
| merged = [] | |
| for line in lines: | |
| line.sort(key=lambda b: b["x_min"]) | |
| merged.append("".join(b["text"] for b in line)) | |
| return " ".join(merged) | |
| # ========== 依赖函数(全部保留,原样引入即可) ========== | |
| def is_guide_button_with_yolo_image( | |
| image_np: np.ndarray, | |
| model: Union[str, YOLO], | |
| ocr, | |
| out_dir: Union[str, Path] = "yolo_outputs", | |
| name: str = "predict", | |
| imgsz: int = 1024, | |
| device: Union[int, str] = 1, | |
| iou: float = 0.45, | |
| max_det: int = 300, | |
| half: bool = False, | |
| verbose: bool = True, | |
| idx: Optional[Union[int, str]] = None # 新增:指定保存文件名 | |
| ) -> bool: | |
| """ | |
| 用 YOLO 对单张 numpy 图像进行检测,并保存可视化与 JSON。 | |
| 返回:是否包含促销元素(True/False) | |
| """ | |
| class_thresholds = { | |
| 0: 1.0, # 优惠券 | |
| 1: 0.75, # 促销贴纸 | |
| 2: 1.0, # 元宝 | |
| 3: 1.0, # 大礼物盒子 | |
| 4: 0.95, # 爆炸 | |
| 5: 0.95, # 礼物盒子 | |
| 6: 0.95, # 福袋 | |
| 7: 0.95, # 红包 | |
| 8: 0.95, # 金币 | |
| 9: 0.95, # 金币簇 | |
| 10: 0.35, # 长条促销贴纸 | |
| 11: 0.35, # 长条贴纸 | |
| } | |
| # 加载模型 | |
| if isinstance(model, str): | |
| model = YOLO(model) | |
| out_dir = Path(out_dir) | |
| out_dir.mkdir(parents=True, exist_ok=True) | |
| results = model.predict( | |
| source=[image_np], | |
| imgsz=imgsz, | |
| conf=0.25, | |
| iou=iou, | |
| device=device, | |
| max_det=max_det, | |
| half=half, | |
| save=True, | |
| project=str(out_dir), | |
| name=name, | |
| exist_ok=True, | |
| verbose=verbose | |
| ) | |
| promo_found = False | |
| det_list = [] | |
| names_dict = getattr(model, "names", None) | |
| for result in results: | |
| save_dir = Path(result.save_dir) | |
| if result.boxes is None: | |
| continue | |
| boxes = result.boxes.xyxy.cpu().numpy() | |
| classes = result.boxes.cls.cpu().numpy().astype(int) | |
| scores = result.boxes.conf.cpu().numpy() | |
| if verbose: | |
| print("—— detections ——") | |
| h, w = image_np.shape[:2] | |
| total_img_area = h * w # 图像总面积 | |
| for (x1, y1, x2, y2), cid, score in zip(boxes, classes, scores): | |
| cname = names_dict.get(int(cid), str(int(cid))) if isinstance(names_dict, dict) else str(int(cid)) | |
| if verbose: | |
| print(f"cls={cname}({cid}) conf={score:.3f}") | |
| det_list.append([cname]) | |
| det_list.append([float(x1), float(y1), float(x2), float(y2), int(cid), float(score)]) | |
| th = class_thresholds.get(cid, None) | |
| if th is None: | |
| continue | |
| # ================= 修改部分开始 ================= | |
| # 处理非长条类型的类别 (即 0-9) | |
| if cid not in (10, 11): | |
| if cid == 1: | |
| # 计算当前检测框的面积 | |
| box_area = (x2 - x1) * (y2 - y1) | |
| # 计算占比 | |
| ratio = box_area / total_img_area if total_img_area > 0 else 0 | |
| # 逻辑:如果是类别1,置信度达标 且 面积占比小于 0.01,判定为True | |
| if score >= th and ratio < 0.01: | |
| if verbose: | |
| print(f" [Match] Class 1 (Area Ratio: {ratio:.4f} < 0.01)") | |
| promo_found = True | |
| else: | |
| # 其他类别 (0, 2-9) 保持原逻辑:只看置信度 | |
| if score >= th: | |
| promo_found = True | |
| continue | |
| # ================= 修改部分结束 ================= | |
| # 下面是处理类别 10, 11 的逻辑 (原代码保持不变) | |
| if cid in (10,11) and score >= 0.875: | |
| promo_found = True | |
| continue | |
| if score >= th: | |
| if ocr is not None: | |
| # 裁剪检测框区域 | |
| xi1, yi1 = max(0, int(x1)), max(0, int(y1)) | |
| xi2, yi2 = min(w - 1, int(x2)), min(h - 1, int(y2)) | |
| if xi2 > xi1 and yi2 > yi1: | |
| crop = image_np[yi1:yi2, xi1:xi2] | |
| try: | |
| ocr_result = ocr.ocr(crop) | |
| # text_merged = extract_texts(ocr_result) | |
| text_merged = merge_by_rows(ocr_result) | |
| print(text_merged) | |
| # 正则匹配:命中任一关键词则判定为促销 | |
| if any(p.search(text_merged) for p in compiled_patterns): | |
| if verbose: | |
| print(f"[OCR-MATCH] {cname} 命中促销关键词") | |
| promo_found = True | |
| continue | |
| except Exception as e: | |
| if verbose: | |
| print(f"[OCR-ERROR] {e}") | |
| # OCR 失败时不改变 promo_found,保持谨慎 | |
| # —— 重命名保存的图片 —— | |
| if idx is not None: | |
| default_path = save_dir / "image0.jpg" | |
| target_path = save_dir / f"{idx}.jpg" | |
| if default_path.exists(): | |
| default_path.rename(target_path) | |
| if verbose: | |
| print(f"[保存] 可视化检测图: {target_path}") | |
| # 保存检测结果 JSON | |
| json_path = save_dir / f"detections_{idx if idx is not None else '0'}.json" | |
| with open(json_path, "w", encoding="utf-8") as f: | |
| json.dump(det_list, f, ensure_ascii=False, indent=2) | |
| if verbose: | |
| print(f"[保存] 检测数组: {json_path}") | |
| # ==== 检测框全部没命中,再做一次全局OCR,匹配extra_keywords ==== | |
| if ocr is not None and not promo_found: | |
| try: | |
| global_ocr_result = ocr.ocr(image_np) | |
| global_text = merge_by_rows(global_ocr_result) | |
| print("[全局OCR]", global_text) | |
| if any(p.search(global_text) for p in compiled_extra_patterns): | |
| if verbose: | |
| print("[全局OCR-MATCH] 命中extra_keywords促销关键词") | |
| return True | |
| except Exception as e: | |
| if verbose: | |
| print(f"[全局OCR-ERROR] {e}") | |
| return promo_found |