|
|
@@ -6,6 +6,8 @@ from dataclasses import dataclass
|
|
|
from typing import Any, Optional
|
|
|
|
|
|
import cv2
|
|
|
+import numpy as np
|
|
|
+import difflib
|
|
|
|
|
|
from app.core.config import settings
|
|
|
from app.core.logger import get_logger
|
|
|
@@ -87,7 +89,9 @@ class VideoService:
|
|
|
方差越大,说明边缘信息越丰富(越不模糊)。
|
|
|
"""
|
|
|
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
|
|
|
- return float(cv2.Laplacian(gray, cv2.CV_64F).var())
|
|
|
+ # 增加高斯模糊,过滤掉反光产生的噪点和高频毛刺
|
|
|
+ blurred = cv2.GaussianBlur(gray, (3, 3), 0)
|
|
|
+ return float(cv2.Laplacian(blurred, cv2.CV_64F).var())
|
|
|
|
|
|
def calculate_weight(self, current_time_ms: int, target_time_ms: int) -> float:
|
|
|
"""利用高斯函数计算时间权重。距离 target_time_ms 越近,返回值越接近 1.0"""
|
|
|
@@ -500,17 +504,17 @@ class VideoService:
|
|
|
score = 0.0
|
|
|
ocr_set = set(ocr_tokens)
|
|
|
for token in expected_tokens:
|
|
|
- if token in ocr_set:
|
|
|
- score += 1.0 # 完全命中给 1 分
|
|
|
- continue
|
|
|
-
|
|
|
- # 兼容:如果目标 token 是 OCR结果的子串,或者 OCR结果是 token的子串 (例如 "PIKACHU" 匹配出 "PIKACH")
|
|
|
- partial_match = any(
|
|
|
- len(other) >= 2 and (token in other or other in token)
|
|
|
- for other in ocr_set
|
|
|
- )
|
|
|
- if partial_match:
|
|
|
- score += 0.6 # 部分匹配给 0.6 分
|
|
|
+ best_ratio = 0.0
|
|
|
+ for other in ocr_tokens:
|
|
|
+ # 计算字符串相似度 (0 到 1)
|
|
|
+ ratio = difflib.SequenceMatcher(None, token, other).ratio()
|
|
|
+ if ratio > best_ratio:
|
|
|
+ best_ratio = ratio
|
|
|
+
|
|
|
+ if best_ratio > 0.85:
|
|
|
+ score += 1.0 # 相似度极高,视为完全命中
|
|
|
+ elif best_ratio > 0.6:
|
|
|
+ score += 0.6 # 存在一定错别字,给部分分
|
|
|
|
|
|
return min(score / len(expected_tokens), 1.0)
|
|
|
|
|
|
@@ -611,8 +615,12 @@ class VideoService:
|
|
|
if not scoring_candidates:
|
|
|
scoring_candidates = candidates
|
|
|
|
|
|
- # 找准当前窗口期的相对最大清晰度作为归一化基准
|
|
|
- max_sharpness = max(candidate.sharpness for candidate in scoring_candidates) if scoring_candidates else 0.0
|
|
|
+ # 改为使用 90 分位数,防止单帧反光噪点拉爆整个分数池
|
|
|
+ if scoring_candidates:
|
|
|
+ sharpnesses = [c.sharpness for c in scoring_candidates]
|
|
|
+ max_sharpness = float(np.percentile(sharpnesses, 90))
|
|
|
+ else:
|
|
|
+ max_sharpness = 0.0
|
|
|
segmentation_used = any(candidate.segmentation_used for candidate in candidates)
|
|
|
|
|
|
expected = self._build_expected_text(card_output)
|