mirror of
https://github.com/OLmatter/glm-coding-helper.git
synced 2026-10-07 12:57:52 +08:00
Backend: - Default OCR model PP-OCRv5_server_rec -> PP-OCRv6_tiny_rec (~14x faster, 83ms/img vs 1189ms on 379 real captchas, accuracy still 100%) - Configurable via config.json ocr_model or env CNCAPTCHA_CPU_OCR_MODEL/GLM_OCR_MODEL - GUI OCR model choices updated to v6 (tiny/medium) + v5 server fallback - worker.py: split 3 crops into independent OCR tasks for parallel recognition - ppocr_worker.py: batch forward (predict_batch_*) + single-crop path - server.py: partial results aggregation + OCR_MODEL env passthrough - requirements: paddleocr>=3.7.0 / paddlex>=3.7.0 (v6 requires) Userscript v23.3: - Fix: auto-click subscribe then wait 15s when click didn't trigger captcha. Synthetic click sometimes gets swallowed by Zhipu frontend (button DOM ready but component state machine not ready), no captcha pops up, main loop stuck in WAITING until MODAL_WAIT=15000 timeout. - Fix: use 'iframe got new prompt+bg image' as the signal. iframe increments GM counter glm_captcha_seen_seq each time it sees a new captcha (prompt or bg changed); main loop records baseline at click time, compares in WAITING - counter increased = captcha popped, wait patiently; no increase after 1.5s = click didn't trigger captcha, immediately retry subscribe. Counter is monotonic, no residue, no timestamp race.
118 lines
3.5 KiB
Python
118 lines
3.5 KiB
Python
"""
|
|
YOLO Worker - 极致流水线版
|
|
- OMP=1 独占物理核,消灭 GIL 争抢
|
|
- 裁剪切片通过 queue 直接传递,无共享内存
|
|
"""
|
|
import os
|
|
import sys
|
|
import io
|
|
import time
|
|
from pathlib import Path
|
|
|
|
import psutil
|
|
from PIL import Image
|
|
from ultralytics import YOLO
|
|
|
|
if getattr(sys, 'frozen', False):
|
|
ROOT = Path(sys._MEIPASS)
|
|
else:
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
DETECTOR = ROOT / "models" / "weights" / "yolo-captcha-detector.pt"
|
|
YOLO_IMGSZ = 448
|
|
|
|
# YOLO 单线程!多进程并行靠进程数
|
|
for _key in ("OMP_NUM_THREADS", "MKL_NUM_THREADS", "OPENBLAS_NUM_THREADS", "NUMEXPR_NUM_THREADS"):
|
|
os.environ[_key] = "1"
|
|
|
|
from backend.evaluate import select_fixed3
|
|
|
|
|
|
def run_yolo_worker(core_id: int, req_queue, ocr_queue, ready_queue):
|
|
# 绑定物理核
|
|
try:
|
|
p = psutil.Process()
|
|
p.cpu_affinity([core_id])
|
|
except Exception:
|
|
pass
|
|
|
|
detector = YOLO(str(DETECTOR))
|
|
_ = detector.predict(
|
|
Image.new("RGB", (YOLO_IMGSZ, YOLO_IMGSZ)),
|
|
imgsz=YOLO_IMGSZ,
|
|
conf=0.15,
|
|
iou=0.5,
|
|
max_det=1,
|
|
verbose=False,
|
|
)
|
|
print(f"[yolo] Core {core_id} ready")
|
|
ready_queue.put("yolo_ready")
|
|
|
|
while True:
|
|
payload = req_queue.get()
|
|
if payload is None:
|
|
break
|
|
|
|
req_id = payload["req_id"]
|
|
chars = payload["chars"]
|
|
|
|
try:
|
|
image = Image.open(io.BytesIO(payload["img_bytes"])).convert("RGB")
|
|
|
|
t0 = time.perf_counter()
|
|
result = detector.predict(
|
|
source=image,
|
|
imgsz=YOLO_IMGSZ,
|
|
conf=0.15,
|
|
iou=0.5,
|
|
max_det=10,
|
|
verbose=False,
|
|
)[0]
|
|
yolo_ms = (time.perf_counter() - t0) * 1000
|
|
|
|
raw_boxes, raw_confs = [], []
|
|
if result.boxes is not None:
|
|
for b in result.boxes:
|
|
raw_boxes.append(tuple(float(x) for x in b.xyxy[0].tolist()))
|
|
raw_confs.append(float(b.conf[0].item()))
|
|
|
|
boxes, confs, reason = select_fixed3(raw_boxes, raw_confs, image.size)
|
|
selected = sorted(zip(boxes, confs), key=lambda x: x[0][0])
|
|
boxes = [x[0] for x in selected]
|
|
|
|
# 纯内存裁剪
|
|
crop_bytes_list = []
|
|
for box in boxes:
|
|
x1, y1, x2, y2 = [int(round(v)) for v in box]
|
|
crop = image.crop(
|
|
(
|
|
max(0, x1),
|
|
max(0, y1),
|
|
min(image.width, x2),
|
|
min(image.height, y2),
|
|
)
|
|
)
|
|
buf = io.BytesIO()
|
|
crop.save(buf, format="PNG")
|
|
crop_bytes_list.append(buf.getvalue())
|
|
|
|
# 每个 crop 独立投递,让多个 OCR worker 并行识别同一张验证码。
|
|
total = len(crop_bytes_list)
|
|
for crop_index, crop_bytes in enumerate(crop_bytes_list):
|
|
ocr_queue.put({
|
|
"req_id": req_id,
|
|
"crop_index": crop_index,
|
|
"crop_total": total,
|
|
"yolo_ms": yolo_ms,
|
|
"boxes": boxes,
|
|
"chars": chars,
|
|
"image_size": list(image.size),
|
|
"reason": reason,
|
|
"crop_bytes": crop_bytes,
|
|
})
|
|
|
|
except Exception as e:
|
|
import traceback
|
|
|
|
traceback.print_exc()
|
|
ocr_queue.put({"req_id": req_id, "error": str(e)})
|