最新可用
This commit is contained in:
149
core/all.py
149
core/all.py
@ -1,45 +1,136 @@
|
||||
from core.ocr import load_model as ocrLoadModel, detect as ocrDetect
|
||||
from core.face import load_model as faceLoadModel, detect as faceDetect
|
||||
from core.yolo import load_model as yoloLoadModel, detect as yoloDetect
|
||||
from concurrent.futures import ThreadPoolExecutor, Future
|
||||
import threading
|
||||
import cv2
|
||||
import numpy as np
|
||||
|
||||
# 添加一个标记变量,用于监控load_model是否已被调用
|
||||
_model_loaded = False
|
||||
# -------------------------- 核心配置参数 --------------------------
|
||||
MAX_WORKERS = 6 # 线程池最大线程数
|
||||
DETECTION_ORDER = ["yolo", "face", "ocr"] # 检测优先级顺序
|
||||
TIMEOUT = 30 # 检测超时时间(秒)
|
||||
|
||||
# -------------------------- 全局状态管理 --------------------------
|
||||
_executor = None # 线程池实例
|
||||
_model_loaded = False # 模型加载状态标记
|
||||
_model_lock = threading.Lock() # 模型加载线程锁
|
||||
_executor_lock = threading.Lock() # 线程池初始化锁
|
||||
_task_counter = 0 # 任务计数器
|
||||
_task_counter_lock = threading.Lock() # 任务计数锁
|
||||
|
||||
|
||||
# -------------------------- 工具函数 --------------------------
|
||||
def _get_next_task_id():
|
||||
"""获取唯一任务ID,用于日志追踪"""
|
||||
global _task_counter
|
||||
with _task_counter_lock:
|
||||
_task_counter += 1
|
||||
return _task_counter
|
||||
|
||||
|
||||
# -------------------------- 模型加载 --------------------------
|
||||
def load_model():
|
||||
"""加载所有检测模型并初始化线程池(仅执行一次)"""
|
||||
global _model_loaded
|
||||
if not _model_loaded:
|
||||
with _model_lock:
|
||||
if not _model_loaded:
|
||||
print("=== 开始加载检测模型 ===")
|
||||
|
||||
# 如果已经调用过,直接忽略
|
||||
if _model_loaded:
|
||||
return
|
||||
# 按顺序加载模型
|
||||
print("加载YOLO模型...")
|
||||
yoloLoadModel()
|
||||
|
||||
# 首次调用时加载模型
|
||||
ocrLoadModel()
|
||||
faceLoadModel()
|
||||
yoloLoadModel()
|
||||
print("加载人脸检测模型...")
|
||||
faceLoadModel()
|
||||
|
||||
# 标记为已调用
|
||||
_model_loaded = True
|
||||
print("加载OCR模型...")
|
||||
ocrLoadModel()
|
||||
|
||||
_model_loaded = True
|
||||
print("=== 所有模型加载完成 ===")
|
||||
|
||||
# 初始化线程池
|
||||
_init_thread_pool()
|
||||
|
||||
|
||||
def detect(frame):
|
||||
# 先进行YOLO检测
|
||||
yolo_flag, yolo_result = yoloDetect(frame)
|
||||
print("YOLO检测结果:", yolo_result)
|
||||
if yolo_flag:
|
||||
return (True, yolo_result, "yolo")
|
||||
# -------------------------- 线程池管理 --------------------------
|
||||
def _init_thread_pool():
|
||||
"""初始化线程池(仅内部调用)"""
|
||||
global _executor
|
||||
with _executor_lock:
|
||||
if _executor is None:
|
||||
_executor = ThreadPoolExecutor(
|
||||
max_workers=MAX_WORKERS,
|
||||
thread_name_prefix="DetectionThread"
|
||||
)
|
||||
print(f"=== 线程池初始化完成,最大线程数: {MAX_WORKERS} ===")
|
||||
|
||||
# YOLO未检测到,进行人脸检测
|
||||
face_flag, face_result = faceDetect(frame)
|
||||
print("人脸检测结果:", face_result)
|
||||
if face_flag:
|
||||
return (True, face_result, "face")
|
||||
|
||||
# 人脸未检测到,进行OCR检测
|
||||
ocr_flag, ocr_result = ocrDetect(frame)
|
||||
print("OCR检测结果:", ocr_result)
|
||||
if ocr_flag:
|
||||
return (True, ocr_result, "ocr")
|
||||
def shutdown():
|
||||
"""关闭线程池,释放资源"""
|
||||
global _executor
|
||||
with _executor_lock:
|
||||
if _executor is not None:
|
||||
_executor.shutdown(wait=True)
|
||||
_executor = None
|
||||
print("=== 线程池已安全关闭 ===")
|
||||
|
||||
|
||||
# -------------------------- 检测逻辑实现 --------------------------
|
||||
def _detect_in_thread(frame: np.ndarray, task_id: int) -> tuple:
|
||||
"""在子线程中执行检测逻辑"""
|
||||
thread_name = threading.current_thread().name
|
||||
print(f"任务[{task_id}] 开始执行,线程: {thread_name}")
|
||||
|
||||
try:
|
||||
# 按照优先级执行检测
|
||||
for detector in DETECTION_ORDER:
|
||||
if detector == "yolo":
|
||||
flag, result = yoloDetect(frame)
|
||||
elif detector == "face":
|
||||
flag, result = faceDetect(frame)
|
||||
elif detector == "ocr":
|
||||
flag, result = ocrDetect(frame)
|
||||
else:
|
||||
flag, result = False, None
|
||||
|
||||
print(f"任务[{task_id}] {detector}检测结果: {'成功' if flag else '失败'}")
|
||||
if flag:
|
||||
print(f"任务[{task_id}] 完成检测,使用检测器: {detector}")
|
||||
return (True, result, detector, task_id)
|
||||
|
||||
# 所有检测器均未检测到结果
|
||||
print(f"任务[{task_id}] 所有检测器均未检测到内容")
|
||||
return (False, "未检测到任何内容", "none", task_id)
|
||||
|
||||
except Exception as e:
|
||||
print(f"任务[{task_id}] 检测过程发生错误: {str(e)}")
|
||||
return (False, f"检测错误: {str(e)}", "error", task_id)
|
||||
|
||||
|
||||
# -------------------------- 外部调用接口 --------------------------
|
||||
def detect(frame: np.ndarray) -> Future:
|
||||
"""
|
||||
提交检测任务到线程池
|
||||
|
||||
参数:
|
||||
frame: 待检测图像(ndarray格式,cv2.imdecode生成)
|
||||
|
||||
返回:
|
||||
Future对象,通过result()方法获取检测结果
|
||||
"""
|
||||
# 确保模型已加载
|
||||
if not _model_loaded:
|
||||
print("警告: 模型尚未加载,将自动加载")
|
||||
load_model()
|
||||
|
||||
# 生成任务ID
|
||||
task_id = _get_next_task_id()
|
||||
|
||||
# 提交任务到线程池
|
||||
future = _executor.submit(_detect_in_thread, frame, task_id)
|
||||
print(f"任务[{task_id}]: 已提交到线程池")
|
||||
return future
|
||||
|
||||
# 所有检测都未检测到
|
||||
return (False, "未检测到任何内容", "none")
|
220
core/face.py
220
core/face.py
@ -1,27 +1,183 @@
|
||||
import os
|
||||
import numpy as np
|
||||
import cv2
|
||||
from PIL import Image # 确保正确导入Image类
|
||||
import gc
|
||||
import time
|
||||
import threading
|
||||
from PIL import Image
|
||||
from insightface.app import FaceAnalysis
|
||||
# 导入获取人脸信息的服务
|
||||
from service.face_service import get_all_face_name_with_eigenvalue
|
||||
|
||||
# 用于检查GPU状态
|
||||
try:
|
||||
import pynvml
|
||||
|
||||
pynvml.nvmlInit()
|
||||
_nvml_available = True
|
||||
except ImportError:
|
||||
print("警告: pynvml库未安装,无法检测GPU状态,将默认使用0号GPU")
|
||||
_nvml_available = False
|
||||
|
||||
# 全局变量
|
||||
_face_app = None
|
||||
_known_faces_embeddings = {} # 存储姓名到特征值的映射
|
||||
_known_faces_names = [] # 存储所有已知姓名
|
||||
_using_gpu = False # 标记是否使用GPU
|
||||
_used_gpu_id = -1 # 记录当前使用的GPU ID
|
||||
|
||||
# 资源管理变量
|
||||
_ref_count = 0
|
||||
_last_used_time = 0
|
||||
_lock = threading.Lock()
|
||||
_release_timeout = 8 # 5秒无使用则释放
|
||||
_is_releasing = False # 标记是否正在释放
|
||||
|
||||
# 调试用计数器
|
||||
_debug_counter = {
|
||||
"created": 0,
|
||||
"released": 0,
|
||||
"detected": 0
|
||||
}
|
||||
|
||||
|
||||
def load_model():
|
||||
"""加载人脸识别模型及已知人脸特征库"""
|
||||
global _face_app, _known_faces_embeddings, _known_faces_names
|
||||
def check_gpu_availability(gpu_id, threshold=0.7):
|
||||
"""检查指定GPU是否可用(内存使用率低于阈值)"""
|
||||
if not _nvml_available:
|
||||
return True # 无法检测时默认认为可用
|
||||
|
||||
try:
|
||||
handle = pynvml.nvmlDeviceGetHandleByIndex(gpu_id)
|
||||
mem_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
|
||||
usage = mem_info.used / mem_info.total
|
||||
# 内存使用率低于阈值则认为可用
|
||||
return usage < threshold
|
||||
except Exception as e:
|
||||
print(f"检查GPU {gpu_id} 状态时出错: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def select_best_gpu(preferred_gpus=[0, 1]):
|
||||
"""选择最佳可用GPU,严格按照首选列表顺序检查,优先使用0号GPU"""
|
||||
# 首先检查首选GPU列表
|
||||
for gpu_id in preferred_gpus:
|
||||
try:
|
||||
# 检查GPU是否存在
|
||||
if _nvml_available:
|
||||
pynvml.nvmlDeviceGetHandleByIndex(gpu_id)
|
||||
|
||||
# 检查GPU是否可用
|
||||
if check_gpu_availability(gpu_id):
|
||||
print(f"GPU {gpu_id} 可用,将使用该GPU")
|
||||
return gpu_id
|
||||
else:
|
||||
if gpu_id == 0:
|
||||
print(f"GPU 0 内存使用率过高(繁忙),尝试切换到其他GPU")
|
||||
except Exception as e:
|
||||
print(f"GPU {gpu_id} 不存在或无法访问: {e}")
|
||||
continue
|
||||
|
||||
# 如果所有首选GPU都不可用,返回-1表示使用CPU
|
||||
print("所有指定的GPU都不可用,将使用CPU进行计算")
|
||||
return -1
|
||||
|
||||
|
||||
def _release_engine():
|
||||
"""释放人脸识别引擎资源"""
|
||||
global _face_app, _is_releasing, _known_faces_embeddings, _known_faces_names
|
||||
if not _face_app or _is_releasing:
|
||||
return
|
||||
|
||||
try:
|
||||
_is_releasing = True
|
||||
# 释放InsightFace资源
|
||||
if hasattr(_face_app, 'model'):
|
||||
# 清除模型资源
|
||||
_face_app.model = None
|
||||
_face_app = None
|
||||
|
||||
# 清空人脸数据
|
||||
_known_faces_embeddings.clear()
|
||||
_known_faces_names.clear()
|
||||
|
||||
_debug_counter["released"] += 1
|
||||
print(f"Face recognition engine released. Stats: {_debug_counter}")
|
||||
|
||||
# 清理GPU缓存
|
||||
gc.collect()
|
||||
try:
|
||||
import torch
|
||||
if torch.cuda.is_available():
|
||||
torch.cuda.empty_cache()
|
||||
torch.cuda.ipc_collect()
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
import tensorflow as tf
|
||||
tf.keras.backend.clear_session()
|
||||
except ImportError:
|
||||
pass
|
||||
finally:
|
||||
_is_releasing = False
|
||||
|
||||
|
||||
def _monitor_thread():
|
||||
"""监控线程,检查并释放超时未使用的资源"""
|
||||
global _ref_count, _last_used_time, _face_app
|
||||
while True:
|
||||
time.sleep(5) # 每5秒检查一次
|
||||
with _lock:
|
||||
# 只有当引擎存在、没有引用且超时,才释放
|
||||
if _face_app and _ref_count == 0 and not _is_releasing:
|
||||
elapsed = time.time() - _last_used_time
|
||||
if elapsed > _release_timeout:
|
||||
print(f"Idle timeout ({elapsed:.1f}s > {_release_timeout}s), releasing face engine")
|
||||
_release_engine()
|
||||
|
||||
|
||||
def load_model(prefer_gpu=True, preferred_gpus=[0, 1]):
|
||||
"""加载人脸识别模型及已知人脸特征库,默认优先使用0号GPU"""
|
||||
global _face_app, _known_faces_embeddings, _known_faces_names, _using_gpu, _used_gpu_id
|
||||
|
||||
# 确保监控线程只启动一次
|
||||
if not any(t.name == "FaceMonitor" for t in threading.enumerate()):
|
||||
threading.Thread(target=_monitor_thread, daemon=True, name="FaceMonitor").start()
|
||||
print("Face monitor thread started")
|
||||
|
||||
# 如果正在释放中,等待释放完成
|
||||
while _is_releasing:
|
||||
time.sleep(0.1)
|
||||
|
||||
# 如果已经初始化,直接返回
|
||||
if _face_app:
|
||||
return True
|
||||
|
||||
# 初始化InsightFace模型
|
||||
try:
|
||||
_face_app = FaceAnalysis(name='buffalo_l', root=os.path.expanduser('~/.insightface'))
|
||||
_face_app.prepare(ctx_id=0, det_size=(640, 640))
|
||||
# 初始化InsightFace
|
||||
print("正在初始化InsightFace人脸识别引擎...")
|
||||
_face_app = FaceAnalysis(name='buffalo_l', root='~/.insightface')
|
||||
|
||||
# 选择合适的GPU,默认优先使用0号
|
||||
ctx_id = 0
|
||||
if prefer_gpu:
|
||||
ctx_id = select_best_gpu(preferred_gpus)
|
||||
_using_gpu = ctx_id != -1
|
||||
_used_gpu_id = ctx_id if _using_gpu else -1
|
||||
|
||||
if _using_gpu:
|
||||
print(f"成功初始化,使用GPU {ctx_id} 进行计算")
|
||||
else:
|
||||
print("成功初始化,使用CPU进行计算")
|
||||
|
||||
# 准备模型
|
||||
_face_app.prepare(ctx_id=ctx_id, det_size=(640, 640))
|
||||
print("InsightFace人脸识别引擎初始化成功。")
|
||||
_debug_counter["created"] += 1
|
||||
print(f"Face engine initialized. Stats: {_debug_counter}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Face model load failed: {e}")
|
||||
print(f"初始化失败: {e}")
|
||||
return False
|
||||
|
||||
# 从服务获取所有人脸姓名和特征值
|
||||
@ -62,19 +218,52 @@ def load_model():
|
||||
|
||||
def detect(frame, threshold=0.4):
|
||||
"""检测并识别人脸,返回结果元组(是否匹配到已知人脸, 结果字符串)"""
|
||||
global _face_app, _known_faces_embeddings, _known_faces_names
|
||||
global _face_app, _known_faces_embeddings, _known_faces_names, _using_gpu, _used_gpu_id
|
||||
global _ref_count, _last_used_time
|
||||
|
||||
if not _face_app or not _known_faces_names or frame is None:
|
||||
return (False, "未初始化或无效帧")
|
||||
# 验证前置条件
|
||||
if frame is None or frame.size == 0:
|
||||
return (False, "无效帧数据")
|
||||
|
||||
# 增加引用计数并获取引擎实例
|
||||
engine = None
|
||||
with _lock:
|
||||
_ref_count += 1
|
||||
_last_used_time = time.time()
|
||||
_debug_counter["detected"] += 1
|
||||
|
||||
# 初始化引擎(如果未初始化且不在释放中)
|
||||
if not _face_app and not _is_releasing:
|
||||
if not load_model(prefer_gpu=True):
|
||||
_ref_count -= 1 # 恢复引用计数
|
||||
return (False, "引擎初始化失败")
|
||||
|
||||
# 获取当前引擎引用
|
||||
engine = _face_app
|
||||
|
||||
# 检查引擎是否可用
|
||||
if not engine or not _known_faces_names:
|
||||
with _lock:
|
||||
_ref_count = max(0, _ref_count - 1)
|
||||
return (False, "人脸识别引擎不可用或未初始化")
|
||||
|
||||
try:
|
||||
# 如果使用GPU,确保输入帧在处理前是连续的数组
|
||||
if _using_gpu and not frame.flags.contiguous:
|
||||
frame = np.ascontiguousarray(frame)
|
||||
|
||||
faces = _face_app.get(frame)
|
||||
except Exception as e:
|
||||
print(f"Face detect error: {e}")
|
||||
# 检测到错误时尝试重新选择GPU并重新初始化
|
||||
print("尝试重新选择GPU并重新初始化...")
|
||||
with _lock:
|
||||
_ref_count = max(0, _ref_count - 1)
|
||||
load_model(prefer_gpu=True) # 重新初始化时保持默认GPU优先级
|
||||
return (False, f"检测错误: {str(e)}")
|
||||
|
||||
result_parts = []
|
||||
has_matched = False # 新增标记:是否有匹配到的已知人脸
|
||||
has_matched = False # 标记是否有匹配到的已知人脸
|
||||
|
||||
for face in faces:
|
||||
# 特征归一化
|
||||
@ -109,5 +298,12 @@ def detect(frame, threshold=0.4):
|
||||
else:
|
||||
result_str = "; ".join(result_parts)
|
||||
|
||||
# 第一个返回值改为:是否匹配到已知人脸
|
||||
# 减少引用计数,确保线程安全
|
||||
with _lock:
|
||||
_ref_count = max(0, _ref_count - 1)
|
||||
# 持续使用时更新最后使用时间
|
||||
if _ref_count > 0:
|
||||
_last_used_time = time.time()
|
||||
|
||||
# 第一个返回值为:是否匹配到已知人脸
|
||||
return (has_matched, result_str)
|
196
core/ocr.py
196
core/ocr.py
@ -1,5 +1,8 @@
|
||||
import os
|
||||
import cv2
|
||||
import gc
|
||||
import time
|
||||
import threading
|
||||
from rapidocr import RapidOCR
|
||||
from service.sensitive_service import get_all_sensitive_words
|
||||
|
||||
@ -7,70 +10,177 @@ from service.sensitive_service import get_all_sensitive_words
|
||||
_ocr_engine = None
|
||||
_forbidden_words = set()
|
||||
_conf_threshold = 0.5
|
||||
|
||||
ocr_config_path = os.path.join(os.path.dirname(__file__), "config", "config.yaml")
|
||||
|
||||
# 资源管理变量
|
||||
_ref_count = 0
|
||||
_last_used_time = 0
|
||||
_lock = threading.Lock()
|
||||
_release_timeout = 5 # 30秒无使用则释放
|
||||
_is_releasing = False # 标记是否正在释放
|
||||
|
||||
# 调试用计数器
|
||||
_debug_counter = {
|
||||
"created": 0,
|
||||
"released": 0,
|
||||
"detected": 0
|
||||
}
|
||||
|
||||
|
||||
def _release_engine():
|
||||
"""释放OCR引擎资源"""
|
||||
global _ocr_engine, _is_releasing
|
||||
if not _ocr_engine or _is_releasing:
|
||||
return
|
||||
|
||||
try:
|
||||
_is_releasing = True
|
||||
# 如果有释放方法则调用
|
||||
if hasattr(_ocr_engine, 'release'):
|
||||
_ocr_engine.release()
|
||||
_ocr_engine = None
|
||||
_debug_counter["released"] += 1
|
||||
print(f"OCR engine released. Stats: {_debug_counter}")
|
||||
|
||||
# 清理GPU缓存
|
||||
gc.collect()
|
||||
try:
|
||||
import torch
|
||||
if torch.cuda.is_available():
|
||||
torch.cuda.empty_cache()
|
||||
torch.cuda.ipc_collect()
|
||||
except ImportError:
|
||||
pass
|
||||
try:
|
||||
import tensorflow as tf
|
||||
tf.keras.backend.clear_session()
|
||||
except ImportError:
|
||||
pass
|
||||
finally:
|
||||
_is_releasing = False
|
||||
|
||||
|
||||
def _monitor_thread():
|
||||
"""监控线程,优化检查逻辑"""
|
||||
global _ref_count, _last_used_time, _ocr_engine
|
||||
while True:
|
||||
time.sleep(5) # 每5秒检查一次
|
||||
with _lock:
|
||||
# 只有当引擎存在、没有引用且超时,才释放
|
||||
if _ocr_engine and _ref_count == 0 and not _is_releasing:
|
||||
elapsed = time.time() - _last_used_time
|
||||
if elapsed > _release_timeout:
|
||||
print(f"Idle timeout ({elapsed:.1f}s > {_release_timeout}s), releasing engine")
|
||||
_release_engine()
|
||||
|
||||
|
||||
def load_model():
|
||||
"""加载OCR引擎及违禁词列表"""
|
||||
global _ocr_engine, _forbidden_words, _conf_threshold
|
||||
"""加载违禁词列表和初始化监控线程"""
|
||||
global _forbidden_words
|
||||
|
||||
# 确保监控线程只启动一次
|
||||
if not any(t.name == "OCRMonitor" for t in threading.enumerate()):
|
||||
threading.Thread(target=_monitor_thread, daemon=True, name="OCRMonitor").start()
|
||||
print("OCR monitor thread started")
|
||||
|
||||
# 加载违禁词
|
||||
try:
|
||||
_forbidden_words = get_all_sensitive_words()
|
||||
print(f"Loaded {len(_forbidden_words)} forbidden words")
|
||||
except Exception as e:
|
||||
print(f"Forbidden words load error: {e}")
|
||||
return False
|
||||
|
||||
# 初始化OCR引擎
|
||||
# 验证配置文件
|
||||
if not os.path.exists(ocr_config_path):
|
||||
print(f"OCR config not found: {ocr_config_path}")
|
||||
return False
|
||||
|
||||
try:
|
||||
_ocr_engine = RapidOCR(config_path=ocr_config_path)
|
||||
except Exception as e:
|
||||
print(f"OCR model load failed: {e}")
|
||||
return False
|
||||
|
||||
return True if _ocr_engine else False
|
||||
return True
|
||||
|
||||
|
||||
def detect(frame):
|
||||
"""OCR检测并筛选违禁词,返回(是否检测到违禁词, 结果字符串)"""
|
||||
if not _ocr_engine or not _forbidden_words or frame is None or frame.size == 0:
|
||||
return (False, "未初始化或无效帧")
|
||||
"""OCR检测,优化引用计数管理"""
|
||||
global _ocr_engine, _forbidden_words, _conf_threshold, _ref_count, _last_used_time
|
||||
|
||||
# 验证前置条件
|
||||
if not _forbidden_words:
|
||||
return (False, "违禁词未初始化")
|
||||
if frame is None or frame.size == 0:
|
||||
return (False, "无效帧数据")
|
||||
if not os.path.exists(ocr_config_path):
|
||||
return (False, f"OCR配置文件不存在: {ocr_config_path}")
|
||||
|
||||
# 增加引用计数并获取引擎实例
|
||||
engine = None
|
||||
with _lock:
|
||||
_ref_count += 1
|
||||
_last_used_time = time.time()
|
||||
_debug_counter["detected"] += 1
|
||||
|
||||
# 初始化引擎(如果未初始化且不在释放中)
|
||||
if not _ocr_engine and not _is_releasing:
|
||||
try:
|
||||
_ocr_engine = RapidOCR(config_path=ocr_config_path)
|
||||
_debug_counter["created"] += 1
|
||||
print(f"OCR engine initialized. Stats: {_debug_counter}")
|
||||
except Exception as e:
|
||||
print(f"OCR model load failed: {e}")
|
||||
_ref_count -= 1 # 恢复引用计数
|
||||
return (False, f"引擎初始化失败: {str(e)}")
|
||||
|
||||
# 获取当前引擎引用
|
||||
engine = _ocr_engine
|
||||
|
||||
# 检查引擎是否可用
|
||||
if not engine:
|
||||
with _lock:
|
||||
_ref_count -= 1
|
||||
return (False, "OCR引擎不可用")
|
||||
|
||||
try:
|
||||
ocr_res = _ocr_engine(frame)
|
||||
# 执行OCR检测
|
||||
ocr_res = engine(frame)
|
||||
|
||||
# 验证OCR结果格式
|
||||
if not ocr_res or not hasattr(ocr_res, 'txts') or not hasattr(ocr_res, 'scores'):
|
||||
return (False, "无OCR结果")
|
||||
|
||||
# 处理OCR结果
|
||||
texts = [t.strip() for t in ocr_res.txts if t and isinstance(t, str)]
|
||||
confs = [c for c in ocr_res.scores if c and isinstance(c, (int, float))]
|
||||
|
||||
if len(texts) != len(confs):
|
||||
return (False, "OCR结果格式异常")
|
||||
|
||||
# 筛选违禁词
|
||||
vio_info = []
|
||||
for txt, conf in zip(texts, confs):
|
||||
if conf < _conf_threshold:
|
||||
continue
|
||||
matched = [w for w in _forbidden_words if w in txt]
|
||||
if matched:
|
||||
vio_info.append(f"文本: '{txt}' 包含违禁词: {', '.join(matched)} (置信度: {conf:.2f})")
|
||||
|
||||
# 构建结果
|
||||
has_text = len(texts) > 0
|
||||
has_violation = len(vio_info) > 0
|
||||
|
||||
if not has_text:
|
||||
return (False, "未识别到文本")
|
||||
elif has_violation:
|
||||
return (True, "; ".join(vio_info))
|
||||
else:
|
||||
return (False, "未检测到违禁词")
|
||||
|
||||
except Exception as e:
|
||||
print(f"OCR detect error: {e}")
|
||||
return (False, f"检测错误: {str(e)}")
|
||||
|
||||
if not ocr_res or not hasattr(ocr_res, 'txts') or not hasattr(ocr_res, 'scores'):
|
||||
return (False, "无OCR结果")
|
||||
|
||||
# 处理OCR结果
|
||||
texts = [t.strip() for t in ocr_res.txts if t and isinstance(t, str)]
|
||||
confs = [c for c in ocr_res.scores if c and isinstance(c, (int, float))]
|
||||
if len(texts) != len(confs):
|
||||
return (False, "OCR结果格式异常")
|
||||
|
||||
# 筛选违禁词
|
||||
vio_info = []
|
||||
for txt, conf in zip(texts, confs):
|
||||
if conf < _conf_threshold:
|
||||
continue
|
||||
matched = [w for w in _forbidden_words if w in txt]
|
||||
if matched:
|
||||
vio_info.append(f"文本: '{txt}' 包含违禁词: {', '.join(matched)} (置信度: {conf:.2f})")
|
||||
|
||||
# 构建结果字符串
|
||||
has_text = len(texts) > 0
|
||||
has_violation = len(vio_info) > 0
|
||||
|
||||
if not has_text:
|
||||
return (False, "未识别到文本")
|
||||
elif has_violation:
|
||||
return (True, "; ".join(vio_info))
|
||||
else:
|
||||
return (False, "未检测到违禁词")
|
||||
finally:
|
||||
# 减少引用计数,确保线程安全
|
||||
with _lock:
|
||||
_ref_count = max(0, _ref_count - 1)
|
||||
# 持续使用时更新最后使用时间
|
||||
if _ref_count > 0:
|
||||
_last_used_time = time.time()
|
||||
|
Reference in New Issue
Block a user