zry-research commited on
Commit
3bda1a9
·
1 Parent(s): d0338a5

feat: add weight to model/

Browse files
Files changed (2) hide show
  1. code/file_process_v2.py +153 -0
  2. 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