Commit ·
3bda1a9
1
Parent(s): d0338a5
feat: add weight to model/
Browse files- code/file_process_v2.py +153 -0
- code/rule_16.py +250 -0
code/file_process_v2.py
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import cv2
|
| 3 |
+
#from paddleocr import PaddleOCRVL
|
| 4 |
+
|
| 5 |
+
import time
|
| 6 |
+
import csv
|
| 7 |
+
|
| 8 |
+
####
|
| 9 |
+
from rule_11 import is_fontsize_percentage_large_image
|
| 10 |
+
#from rule_14 import is_text_design_uncoordinated_image
|
| 11 |
+
from rule_16 import is_guide_button_with_yolo_image
|
| 12 |
+
|
| 13 |
+
'''
|
| 14 |
+
from rule_2 import is_low_hue_diversity_image
|
| 15 |
+
from rule_3 import is_promotion_image
|
| 16 |
+
from rule_4 import is_low_value_image
|
| 17 |
+
from rule_8 import is_promo_with_yolo_image
|
| 18 |
+
from rule_9 import is_subject_too_large_image
|
| 19 |
+
from rule_10 import is_outside_safe_area_image
|
| 20 |
+
'''
|
| 21 |
+
|
| 22 |
+
# 假定 ocr 及 is_promotion_image 已初始化/定义
|
| 23 |
+
from paddleocr import PaddleOCR
|
| 24 |
+
from ultralytics import YOLO
|
| 25 |
+
ocr = PaddleOCR(
|
| 26 |
+
|
| 27 |
+
# ==== ① 文档方向 + 纠偏预处理 ====
|
| 28 |
+
use_textline_orientation=False,
|
| 29 |
+
use_doc_orientation_classify=False,
|
| 30 |
+
use_doc_unwarping=False,
|
| 31 |
+
|
| 32 |
+
# ==== ② 检测模型 ====
|
| 33 |
+
text_detection_model_name="PP-OCRv5_server_det",
|
| 34 |
+
text_detection_model_dir="model/paddleocr/det/PP-OCRv5_server_det_infer",
|
| 35 |
+
#r"model/paddleocr/det/ch_PP-OCRv3_det_infer_new"
|
| 36 |
+
|
| 37 |
+
# ==== ③ 识别模型 ====
|
| 38 |
+
text_recognition_model_name="PP-OCRv5_server_rec",
|
| 39 |
+
text_recognition_model_dir="model/paddleocr/rec/PP-OCRv5_server_rec_infer",
|
| 40 |
+
#r"model/paddleocr/rec/ch_PP-OCRv3_rec_infer_new"
|
| 41 |
+
# ==== ⑦ 语言 ====
|
| 42 |
+
lang="ch"
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
yolo_model = YOLO("model/runs/detect/train8/weights/best.pt")
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
def main(input_folder):
|
| 49 |
+
count_total = 0
|
| 50 |
+
count_is_fontsize_percentage_large=0
|
| 51 |
+
count_is_text_design_uncoordinated =0
|
| 52 |
+
count_is_guide_button_with_yolo=0
|
| 53 |
+
'''
|
| 54 |
+
count_promo = 0
|
| 55 |
+
count_low_value=0
|
| 56 |
+
count_low_hue_diversity=0
|
| 57 |
+
count_is_promo_with_yolo=0
|
| 58 |
+
count_is_subject_too_large=0
|
| 59 |
+
count_is_outside_safe_area=0
|
| 60 |
+
'''
|
| 61 |
+
|
| 62 |
+
results_list = []
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
for filename in os.listdir(input_folder):
|
| 66 |
+
if not filename.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp','.webp','.jfif')):
|
| 67 |
+
continue
|
| 68 |
+
|
| 69 |
+
image_path = os.path.join(input_folder, filename)
|
| 70 |
+
img = cv2.imread(image_path)
|
| 71 |
+
if img is None:
|
| 72 |
+
print(f"[警告] 图片读取失败: {filename}")
|
| 73 |
+
continue
|
| 74 |
+
|
| 75 |
+
count_total += 1
|
| 76 |
+
|
| 77 |
+
img_result = {
|
| 78 |
+
"filename": filename,
|
| 79 |
+
"is_fontsize_percentage_large": None,
|
| 80 |
+
"is_text_design_uncoordinated": None,
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
'''
|
| 84 |
+
'''
|
| 85 |
+
is_fontsize_percentage_large= is_fontsize_percentage_large_image(img,ocr)
|
| 86 |
+
|
| 87 |
+
img_result["is_fontsize_percentage_large"] = is_fontsize_percentage_large
|
| 88 |
+
|
| 89 |
+
if is_fontsize_percentage_large:
|
| 90 |
+
count_is_fontsize_percentage_large += 1
|
| 91 |
+
result_text = "文字-占比过大"
|
| 92 |
+
else:
|
| 93 |
+
result_text = "文字-占比正常"
|
| 94 |
+
#print(f"文件: {filename}\t判断: {result_text}")
|
| 95 |
+
print(f"文件: {os.path.join(input_folder,filename)}\n判断: {result_text}")
|
| 96 |
+
'''
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
is_text_design_uncoordinated = is_text_design_uncoordinated_image(img,ocr)
|
| 100 |
+
img_result["is_text_design_uncoordinated"] = is_text_design_uncoordinated
|
| 101 |
+
if is_text_design_uncoordinated:
|
| 102 |
+
count_is_text_design_uncoordinated += 1
|
| 103 |
+
result_text = "文字搭配不协调"
|
| 104 |
+
else:
|
| 105 |
+
result_text = "文字搭配协调"
|
| 106 |
+
#print(f"文件: {filename}\t判断: {result_text}")
|
| 107 |
+
print(f"文件: {os.path.join(input_folder,filename)}\n判断: {result_text}")
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
is_guide_button_with_yolo = is_guide_button_with_yolo_image(img,model=yolo_model,idx=count_total,ocr=ocr)
|
| 111 |
+
img_result["is_text_design_uncoordinated"] = is_guide_button_with_yolo
|
| 112 |
+
if is_guide_button_with_yolo:
|
| 113 |
+
count_is_guide_button_with_yolo += 1
|
| 114 |
+
result_text = "存在引导按钮"
|
| 115 |
+
else:
|
| 116 |
+
result_text = "不存在"
|
| 117 |
+
#print(f"文件: {filename}\t判断: {result_text}")
|
| 118 |
+
print(f"文件: {os.path.join(input_folder,filename)}\n判断: {result_text}")
|
| 119 |
+
|
| 120 |
+
'''
|
| 121 |
+
|
| 122 |
+
results_list.append(img_result)
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
# 输出统计信息
|
| 126 |
+
if count_total > 0:
|
| 127 |
+
percentage = round(count_is_fontsize_percentage_large / count_total * 100, 2)
|
| 128 |
+
print(f"\n统计:总图片数={count_total},\
|
| 129 |
+
文字占比大={count_is_fontsize_percentage_large}|{round(count_is_fontsize_percentage_large / count_total * 100, 2)}%,\
|
| 130 |
+
文字搭配不协调={count_is_text_design_uncoordinated}|{round(count_is_text_design_uncoordinated/ count_total * 100, 2)}%,\
|
| 131 |
+
存在引导按钮={count_is_guide_button_with_yolo}|{round(count_is_guide_button_with_yolo/ count_total * 100, 2)}%,\
|
| 132 |
+
end")
|
| 133 |
+
else:
|
| 134 |
+
print("未检测到有效图片。")
|
| 135 |
+
|
| 136 |
+
output_csv = "image_rule_results.csv"
|
| 137 |
+
with open(output_csv, "w", newline='', encoding="utf-8") as csvfile:
|
| 138 |
+
fieldnames = [
|
| 139 |
+
"filename",
|
| 140 |
+
"is_fontsize_percentage_large",
|
| 141 |
+
"is_text_design_uncoordinated",
|
| 142 |
+
"is_guide_button_with_yolo",
|
| 143 |
+
]
|
| 144 |
+
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
| 145 |
+
writer.writeheader()
|
| 146 |
+
for row in results_list:
|
| 147 |
+
writer.writerow(row)
|
| 148 |
+
print(f"\n已保存统计表:{output_csv}")
|
| 149 |
+
|
| 150 |
+
if __name__ == '__main__':
|
| 151 |
+
input_folder = "stage2/hw_v2/文字占比" # 你的图片文件夹路径
|
| 152 |
+
main(input_folder)
|
| 153 |
+
|
code/rule_16.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os, json
|
| 2 |
+
import numpy as np
|
| 3 |
+
from pathlib import Path
|
| 4 |
+
from typing import Iterable, Optional, Union
|
| 5 |
+
from ultralytics import YOLO
|
| 6 |
+
import re
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
promotion_keywords = [
|
| 10 |
+
r"领", r"申请", r"立即",r"马上",r"即刻",r"立即",r"下载",
|
| 11 |
+
r"一定",r"现发",
|
| 12 |
+
r"大牌美食",
|
| 13 |
+
]
|
| 14 |
+
extra_keywords = [
|
| 15 |
+
r"立即领取",r"点击领取",r"申请我的额度",r"免费观看",
|
| 16 |
+
r"火热选购",
|
| 17 |
+
r"立即前往",r"立即下载",r"立即投保",r"立即参与",r"立即解救",r"立即领取",r"立即抢购",r"立即购买",r"立即签到",
|
| 18 |
+
r"测一测",r"一定要买",r"现摘现发",
|
| 19 |
+
r"登录",r"上滑",
|
| 20 |
+
|
| 21 |
+
]
|
| 22 |
+
compiled_patterns = [re.compile(p, flags=re.IGNORECASE) for p in (promotion_keywords + extra_keywords)]
|
| 23 |
+
compiled_extra_patterns = [re.compile(p, flags=re.IGNORECASE) for p in extra_keywords]
|
| 24 |
+
|
| 25 |
+
def extract_texts(ocr_result) -> str:
|
| 26 |
+
# 支持 str / [str] / dict / list[dict/...]
|
| 27 |
+
texts = []
|
| 28 |
+
texts = ocr_result[0]['rec_texts']
|
| 29 |
+
scores = ocr_result[0]['rec_scores']
|
| 30 |
+
coordss = ocr_result[0]['rec_polys']
|
| 31 |
+
ocr_result = [
|
| 32 |
+
(pts, (txt, conf))
|
| 33 |
+
for pts, txt, conf in zip(coordss, texts, scores)
|
| 34 |
+
]
|
| 35 |
+
|
| 36 |
+
for res in ocr_result:
|
| 37 |
+
coords, (text, confidence) = res
|
| 38 |
+
if confidence > 0.5:
|
| 39 |
+
texts.append(text)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
merged = " ".join([t for t in texts if t]).strip()
|
| 43 |
+
merged = re.sub(r"\s+", "", merged)
|
| 44 |
+
print(merged)
|
| 45 |
+
return merged
|
| 46 |
+
|
| 47 |
+
def merge_by_rows(ocr_result, y_threshold=40):
|
| 48 |
+
blocks = []
|
| 49 |
+
texts = ocr_result[0]['rec_texts']
|
| 50 |
+
scores = ocr_result[0]['rec_scores']
|
| 51 |
+
coordss = ocr_result[0]['rec_polys']
|
| 52 |
+
ocr_result = [
|
| 53 |
+
(pts, (txt, conf))
|
| 54 |
+
for pts, txt, conf in zip(coordss, texts, scores)
|
| 55 |
+
]
|
| 56 |
+
|
| 57 |
+
for pts, (txt, conf) in ocr_result:
|
| 58 |
+
if conf < 0.5 or not txt.strip():
|
| 59 |
+
continue
|
| 60 |
+
ys = [p[1] for p in pts]
|
| 61 |
+
xs = [p[0] for p in pts]
|
| 62 |
+
blocks.append({
|
| 63 |
+
"text": txt.strip(),
|
| 64 |
+
"x_min": min(xs),
|
| 65 |
+
"y_max": max(ys)
|
| 66 |
+
})
|
| 67 |
+
if not blocks:
|
| 68 |
+
return ""
|
| 69 |
+
blocks.sort(key=lambda b: b["y_max"])
|
| 70 |
+
lines, cur = [], [blocks[0]]
|
| 71 |
+
for blk in blocks[1:]:
|
| 72 |
+
if abs(blk["y_max"] - cur[0]["y_max"]) <= y_threshold:
|
| 73 |
+
cur.append(blk)
|
| 74 |
+
else:
|
| 75 |
+
lines.append(cur)
|
| 76 |
+
cur = [blk]
|
| 77 |
+
lines.append(cur)
|
| 78 |
+
merged = []
|
| 79 |
+
for line in lines:
|
| 80 |
+
line.sort(key=lambda b: b["x_min"])
|
| 81 |
+
merged.append("".join(b["text"] for b in line))
|
| 82 |
+
return " ".join(merged)
|
| 83 |
+
|
| 84 |
+
# ========== 依赖函数(全部保留,原样引入即可) ==========
|
| 85 |
+
def is_guide_button_with_yolo_image(
|
| 86 |
+
image_np: np.ndarray,
|
| 87 |
+
model: Union[str, YOLO],
|
| 88 |
+
ocr,
|
| 89 |
+
out_dir: Union[str, Path] = "yolo_outputs",
|
| 90 |
+
name: str = "predict",
|
| 91 |
+
imgsz: int = 1024,
|
| 92 |
+
device: Union[int, str] = 1,
|
| 93 |
+
iou: float = 0.45,
|
| 94 |
+
max_det: int = 300,
|
| 95 |
+
half: bool = False,
|
| 96 |
+
verbose: bool = True,
|
| 97 |
+
idx: Optional[Union[int, str]] = None # 新增:指定保存文件名
|
| 98 |
+
) -> bool:
|
| 99 |
+
"""
|
| 100 |
+
用 YOLO 对单张 numpy 图像进行检测,并保存可视化与 JSON。
|
| 101 |
+
返回:是否包含促销元素(True/False)
|
| 102 |
+
"""
|
| 103 |
+
class_thresholds = {
|
| 104 |
+
0: 1.0, # 优惠券
|
| 105 |
+
1: 0.75, # 促销贴纸
|
| 106 |
+
2: 1.0, # 元宝
|
| 107 |
+
3: 1.0, # 大礼物盒子
|
| 108 |
+
4: 0.95, # 爆炸
|
| 109 |
+
5: 0.95, # 礼物盒子
|
| 110 |
+
6: 0.95, # 福袋
|
| 111 |
+
7: 0.95, # 红包
|
| 112 |
+
8: 0.95, # 金币
|
| 113 |
+
9: 0.95, # 金币簇
|
| 114 |
+
10: 0.35, # 长条促销贴纸
|
| 115 |
+
11: 0.35, # 长条贴纸
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
# 加载模型
|
| 119 |
+
if isinstance(model, str):
|
| 120 |
+
model = YOLO(model)
|
| 121 |
+
|
| 122 |
+
out_dir = Path(out_dir)
|
| 123 |
+
out_dir.mkdir(parents=True, exist_ok=True)
|
| 124 |
+
|
| 125 |
+
results = model.predict(
|
| 126 |
+
source=[image_np],
|
| 127 |
+
imgsz=imgsz,
|
| 128 |
+
conf=0.25,
|
| 129 |
+
iou=iou,
|
| 130 |
+
device=device,
|
| 131 |
+
max_det=max_det,
|
| 132 |
+
half=half,
|
| 133 |
+
save=True,
|
| 134 |
+
project=str(out_dir),
|
| 135 |
+
name=name,
|
| 136 |
+
exist_ok=True,
|
| 137 |
+
verbose=verbose
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
promo_found = False
|
| 141 |
+
det_list = []
|
| 142 |
+
names_dict = getattr(model, "names", None)
|
| 143 |
+
|
| 144 |
+
for result in results:
|
| 145 |
+
save_dir = Path(result.save_dir)
|
| 146 |
+
if result.boxes is None:
|
| 147 |
+
continue
|
| 148 |
+
boxes = result.boxes.xyxy.cpu().numpy()
|
| 149 |
+
classes = result.boxes.cls.cpu().numpy().astype(int)
|
| 150 |
+
scores = result.boxes.conf.cpu().numpy()
|
| 151 |
+
|
| 152 |
+
if verbose:
|
| 153 |
+
print("—— detections ——")
|
| 154 |
+
|
| 155 |
+
h, w = image_np.shape[:2]
|
| 156 |
+
total_img_area = h * w # 图像总面积
|
| 157 |
+
|
| 158 |
+
for (x1, y1, x2, y2), cid, score in zip(boxes, classes, scores):
|
| 159 |
+
cname = names_dict.get(int(cid), str(int(cid))) if isinstance(names_dict, dict) else str(int(cid))
|
| 160 |
+
if verbose:
|
| 161 |
+
print(f"cls={cname}({cid}) conf={score:.3f}")
|
| 162 |
+
det_list.append([cname])
|
| 163 |
+
det_list.append([float(x1), float(y1), float(x2), float(y2), int(cid), float(score)])
|
| 164 |
+
|
| 165 |
+
th = class_thresholds.get(cid, None)
|
| 166 |
+
if th is None:
|
| 167 |
+
continue
|
| 168 |
+
|
| 169 |
+
# ================= 修改部分开始 =================
|
| 170 |
+
|
| 171 |
+
# 处理非长条类型的类别 (即 0-9)
|
| 172 |
+
if cid not in (10, 11):
|
| 173 |
+
if cid == 1:
|
| 174 |
+
# 计算当前检测框的面积
|
| 175 |
+
box_area = (x2 - x1) * (y2 - y1)
|
| 176 |
+
# 计算占比
|
| 177 |
+
ratio = box_area / total_img_area if total_img_area > 0 else 0
|
| 178 |
+
|
| 179 |
+
# 逻辑:如果是类别1,置信度达标 且 面积占比小于 0.01,判定为True
|
| 180 |
+
if score >= th and ratio < 0.01:
|
| 181 |
+
if verbose:
|
| 182 |
+
print(f" [Match] Class 1 (Area Ratio: {ratio:.4f} < 0.01)")
|
| 183 |
+
promo_found = True
|
| 184 |
+
else:
|
| 185 |
+
# 其他类别 (0, 2-9) 保持原逻辑:只看置信度
|
| 186 |
+
if score >= th:
|
| 187 |
+
promo_found = True
|
| 188 |
+
continue
|
| 189 |
+
|
| 190 |
+
# ================= 修改部分结束 =================
|
| 191 |
+
|
| 192 |
+
# 下面是处理类别 10, 11 的逻辑 (原代码保持不变)
|
| 193 |
+
if cid in (10,11) and score >= 0.875:
|
| 194 |
+
promo_found = True
|
| 195 |
+
continue
|
| 196 |
+
|
| 197 |
+
if score >= th:
|
| 198 |
+
if ocr is not None:
|
| 199 |
+
# 裁剪检测框区域
|
| 200 |
+
xi1, yi1 = max(0, int(x1)), max(0, int(y1))
|
| 201 |
+
xi2, yi2 = min(w - 1, int(x2)), min(h - 1, int(y2))
|
| 202 |
+
if xi2 > xi1 and yi2 > yi1:
|
| 203 |
+
crop = image_np[yi1:yi2, xi1:xi2]
|
| 204 |
+
try:
|
| 205 |
+
ocr_result = ocr.ocr(crop)
|
| 206 |
+
# text_merged = extract_texts(ocr_result)
|
| 207 |
+
text_merged = merge_by_rows(ocr_result)
|
| 208 |
+
print(text_merged)
|
| 209 |
+
# 正则匹配:命中任一关键词则判定为促销
|
| 210 |
+
if any(p.search(text_merged) for p in compiled_patterns):
|
| 211 |
+
if verbose:
|
| 212 |
+
print(f"[OCR-MATCH] {cname} 命中促销关键词")
|
| 213 |
+
promo_found = True
|
| 214 |
+
continue
|
| 215 |
+
except Exception as e:
|
| 216 |
+
if verbose:
|
| 217 |
+
print(f"[OCR-ERROR] {e}")
|
| 218 |
+
# OCR 失败时不改变 promo_found,保持谨慎
|
| 219 |
+
|
| 220 |
+
# —— 重命名保存的图片 ——
|
| 221 |
+
if idx is not None:
|
| 222 |
+
default_path = save_dir / "image0.jpg"
|
| 223 |
+
target_path = save_dir / f"{idx}.jpg"
|
| 224 |
+
if default_path.exists():
|
| 225 |
+
default_path.rename(target_path)
|
| 226 |
+
if verbose:
|
| 227 |
+
print(f"[保存] 可视化检测图: {target_path}")
|
| 228 |
+
|
| 229 |
+
# 保存检测结果 JSON
|
| 230 |
+
json_path = save_dir / f"detections_{idx if idx is not None else '0'}.json"
|
| 231 |
+
with open(json_path, "w", encoding="utf-8") as f:
|
| 232 |
+
json.dump(det_list, f, ensure_ascii=False, indent=2)
|
| 233 |
+
if verbose:
|
| 234 |
+
print(f"[保存] 检测数组: {json_path}")
|
| 235 |
+
|
| 236 |
+
# ==== 检测框全部没命中,再做一次全局OCR,匹配extra_keywords ====
|
| 237 |
+
if ocr is not None and not promo_found:
|
| 238 |
+
try:
|
| 239 |
+
global_ocr_result = ocr.ocr(image_np)
|
| 240 |
+
global_text = merge_by_rows(global_ocr_result)
|
| 241 |
+
print("[全局OCR]", global_text)
|
| 242 |
+
if any(p.search(global_text) for p in compiled_extra_patterns):
|
| 243 |
+
if verbose:
|
| 244 |
+
print("[全局OCR-MATCH] 命中extra_keywords促销关键词")
|
| 245 |
+
return True
|
| 246 |
+
except Exception as e:
|
| 247 |
+
if verbose:
|
| 248 |
+
print(f"[全局OCR-ERROR] {e}")
|
| 249 |
+
|
| 250 |
+
return promo_found
|