commit 8506c3af7950a38ed28bfa5bc1f1a2c77673402d
Author: 琉璃月光 <15630071+llyg777@user.noreply.gitee.com>
Date: Tue Dec 16 15:12:02 2025 +0800
first commit
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..10b731c
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
diff --git a/.idea/ML_xiantiao.iml b/.idea/ML_xiantiao.iml
new file mode 100644
index 0000000..8770519
--- /dev/null
+++ b/.idea/ML_xiantiao.iml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..48c5af0
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..def46bb
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..94a25f7
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/camera_basler_capture_img/camera_basler_capture_test1img.py b/camera_basler_capture_img/camera_basler_capture_test1img.py
new file mode 100644
index 0000000..233a211
--- /dev/null
+++ b/camera_basler_capture_img/camera_basler_capture_test1img.py
@@ -0,0 +1,63 @@
+# camera_capture.py
+import os
+from pypylon import pylon
+import cv2
+from datetime import datetime
+
+
+def search_get_device():
+ """搜索并返回 Basler GigE 相机对象"""
+ tl_factory = pylon.TlFactory.GetInstance()
+ for dev_info in tl_factory.EnumerateDevices():
+ if dev_info.GetDeviceClass() == 'BaslerGigE':
+ print(f"Found Basler GigE Camera: {dev_info.GetModelName()} IP: {dev_info.GetIpAddress()}")
+ return pylon.InstantCamera(tl_factory.CreateDevice(dev_info))
+ raise EnvironmentError("没有找到 Basler GigE 相机")
+
+
+def grab_image(save=True):
+ """
+ 采集一张图像并返回 numpy ndarray(BGR)
+ save=True 时自动保存 PNG 文件到 camera/ 文件夹
+ """
+ # 创建保存目录
+ save_dir = "camera"
+ os.makedirs(save_dir, exist_ok=True)
+
+ cam = search_get_device()
+ cam.Open()
+
+ # 设置图像转换器(BGR8 → OpenCV格式)
+ converter = pylon.ImageFormatConverter()
+ converter.OutputPixelFormat = pylon.PixelType_BGR8packed
+ converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned
+
+ cam.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
+
+ grabResult = cam.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
+
+ if grabResult.GrabSucceeded():
+ image = converter.Convert(grabResult)
+ img = image.GetArray() # OpenCV 可处理的 ndarray
+ print("成功采集图像:", img.shape)
+
+ # 保存 PNG
+ if save:
+ filename = os.path.join(save_dir, f"Image_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png")
+ cv2.imwrite(filename, img)
+ print("已保存:", filename)
+
+ else:
+ print("采集失败:", grabResult.ErrorCode, grabResult.ErrorDescription)
+ img = None
+
+ grabResult.Release()
+ cam.Close()
+ return img
+
+
+if __name__ == "__main__":
+ img = grab_image(save=True)
+ if img is not None:
+ print("图像类型:", type(img))
+ print("图像尺寸:", img.shape)
diff --git a/camera_basler_capture_img/camera_main.py b/camera_basler_capture_img/camera_main.py
new file mode 100644
index 0000000..cb2affb
--- /dev/null
+++ b/camera_basler_capture_img/camera_main.py
@@ -0,0 +1,108 @@
+import threading
+import queue
+import cv2
+from pypylon import pylon
+from datetime import datetime
+import os
+
+# =================== 配置 ===================
+SAVE_DIR = "camera/continuous"
+SAVE_EVERY_N_FRAMES = 1 # 每N帧保存一张,None表示不保存
+SHOW = True
+QUEUE_MAXSIZE = 50
+
+# =================== 相机搜索 ===================
+def search_get_device():
+ """搜索并返回 Basler GigE 相机对象"""
+ tl_factory = pylon.TlFactory.GetInstance()
+ for dev_info in tl_factory.EnumerateDevices():
+ if dev_info.GetDeviceClass() == 'BaslerGigE':
+ print(f"Found Basler GigE Camera: {dev_info.GetModelName()} IP: {dev_info.GetIpAddress()}")
+ return pylon.InstantCamera(tl_factory.CreateDevice(dev_info))
+ raise EnvironmentError("没有找到 Basler GigE 相机")
+
+# =================== 异步处理线程 ===================
+frame_queue = queue.Queue(maxsize=QUEUE_MAXSIZE)
+exit_event = threading.Event()
+
+def worker(save_dir, save_every_n_frames, show):
+ frame_count = 0
+ while not exit_event.is_set():
+ try:
+ item = frame_queue.get(timeout=0.1) # 队列空时短暂等待
+ except queue.Empty:
+ continue
+
+ if item is None:
+ break
+
+ img, frame_num = item
+ frame_count += 1
+
+ # 显示图像
+ if show:
+ cv2.imshow("Basler Live Feed", img)
+ if cv2.waitKey(1) & 0xFF in (ord('q'), 27):
+ exit_event.set()
+ break
+
+ # 保存图像
+ if save_every_n_frames and frame_num % save_every_n_frames == 0:
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S_%f')[:-3]
+ filename = os.path.join(save_dir, f"frame_{frame_num:06d}_{timestamp}.png")
+ cv2.imwrite(filename, img)
+ print(f"[Saved] {filename}")
+
+ if show:
+ cv2.destroyAllWindows()
+
+# =================== 主采集函数 ===================
+def continuous_grab(save_dir=SAVE_DIR, show=SHOW, save_every_n_frames=SAVE_EVERY_N_FRAMES):
+ os.makedirs(save_dir, exist_ok=True)
+ cam = search_get_device()
+ cam.Open()
+ # Load the User Set 1 user set
+ cam.UserSetSelector.Value = "UserSet1"
+ cam.UserSetLoad.Execute()
+ cam.MaxNumBuffer = 30
+
+ converter = pylon.ImageFormatConverter()
+ converter.OutputPixelFormat = pylon.PixelType_BGR8packed
+ converter.OutputBitAlignment = pylon.OutputBitAlignment_MsbAligned
+
+ # 启动异步处理线程
+ t = threading.Thread(target=worker, args=(save_dir, save_every_n_frames, show), daemon=True)
+ t.start()
+
+ cam.StartGrabbing(pylon.GrabStrategy_LatestImageOnly)
+ frame_count = 0
+ try:
+ while cam.IsGrabbing() and not exit_event.is_set():
+ grab_result = cam.RetrieveResult(5000, pylon.TimeoutHandling_ThrowException)
+ if grab_result.GrabSucceeded():
+ image = converter.Convert(grab_result)
+ img = image.GetArray()
+ frame_count += 1
+
+ # 拷贝一份到队列
+ if not frame_queue.full():
+ frame_queue.put((img.copy(), frame_count))
+ grab_result.Release()
+
+ except KeyboardInterrupt:
+ print("被 Ctrl+C 中断")
+ cam.StopGrabbing()
+ cam.Close()
+ exit_event.set()
+
+ finally:
+ cam.StopGrabbing()
+ cam.Close()
+ # 通知线程退出
+ frame_queue.put(None)
+ t.join()
+ print(f"共采集 {frame_count} 帧")
+
+# =================== 主入口 ===================
+if __name__ == "__main__":
+ continuous_grab()
diff --git a/class_xiantiao_pc/1.png b/class_xiantiao_pc/1.png
new file mode 100644
index 0000000..7395075
Binary files /dev/null and b/class_xiantiao_pc/1.png differ
diff --git a/class_xiantiao_pc/2.png b/class_xiantiao_pc/2.png
new file mode 100644
index 0000000..b79b9bc
Binary files /dev/null and b/class_xiantiao_pc/2.png differ
diff --git a/class_xiantiao_pc/chose_ROI.py b/class_xiantiao_pc/chose_ROI.py
new file mode 100644
index 0000000..47e3e65
--- /dev/null
+++ b/class_xiantiao_pc/chose_ROI.py
@@ -0,0 +1,93 @@
+import cv2
+import numpy as np
+import os
+
+# 全局变量
+drawing = False # 是否正在绘制
+ix, iy = -1, -1 # 起始点
+roi_list = [] # 存储多个 ROI 坐标 [(x, y, w, h), ...]
+image_path = "1.png" # <<< 修改为你自己的图像路径
+save_dir = "./roi_1/1.txt" # 保存坐标的目录
+
+# 创建保存目录
+os.makedirs(save_dir, exist_ok=True)
+
+def draw_rectangle(event, x, y, flags, param):
+ global ix, iy, drawing, img_copy, roi_list
+
+ if event == cv2.EVENT_LBUTTONDOWN:
+ drawing = True
+ ix, iy = x, y
+
+ elif event == cv2.EVENT_MOUSEMOVE:
+ if drawing:
+ # 每次移动都恢复原始图像,重新画矩形
+ img_copy = img.copy()
+ cv2.rectangle(img_copy, (ix, iy), (x, y), (0, 255, 0), 2)
+ cv2.imshow("Select ROI", img_copy)
+
+ elif event == cv2.EVENT_LBUTTONUP:
+ drawing = False
+ w = x - ix
+ h = y - iy
+ if w != 0 and h != 0:
+ # 确保宽高为正
+ x_start = min(ix, x)
+ y_start = min(iy, y)
+ w = abs(w)
+ h = abs(h)
+ cv2.rectangle(img_copy, (x_start, y_start), (x_start + w, y_start + h), (0, 255, 0), 2)
+ cv2.imshow("Select ROI", img_copy)
+ # 添加到列表
+ roi_list.append((x_start, y_start, w, h))
+ print(f"已选择 ROI: (x={x_start}, y={y_start}, w={w}, h={h})")
+
+# 保存坐标到 .txt 文件的函数
+def save_rois_to_txt(rois, filepath):
+ with open(filepath, 'w') as file:
+ for roi in rois:
+ # 将每个 ROI 转换为字符串并写入文件,每行一个 ROI
+ line = ','.join(map(str, roi)) + '\n'
+ file.write(line)
+ print(f"💾 ROI 坐标已保存至: {filepath}")
+
+def select_roi(image_path):
+ global img, img_copy
+
+ img = cv2.imread(image_path)
+ if img is None:
+ print(f"❌ 无法读取图像: {image_path}")
+ return
+
+ img_copy = img.copy()
+ cv2.namedWindow("Select ROI")
+ cv2.setMouseCallback("Select ROI", draw_rectangle)
+
+ print("📌 使用鼠标左键拖拽选择 ROI")
+ print("✅ 选择完成后按 's' 键保存坐标")
+ print("⏭️ 按 'n' 键跳过/下一步(可自定义)")
+ print("🚪 按 'q' 键退出")
+
+ while True:
+ cv2.imshow("Select ROI", img_copy)
+ key = cv2.waitKey(1) & 0xFF
+
+ if key == ord('s'):
+ # 保存坐标
+ base_name = os.path.splitext(os.path.basename(image_path))[0]
+ save_path = os.path.join(save_dir, f"{base_name}_rois1.txt") # 修改了扩展名为 .txt
+ save_rois_to_txt(roi_list, save_path) # 使用新的保存函数
+
+ elif key == ord('n'):
+ print("⏭️ 跳到下一张图片(此处可扩展)")
+ break
+
+ elif key == ord('q'):
+ print("👋 退出程序")
+ cv2.destroyAllWindows()
+ return
+
+ cv2.destroyAllWindows()
+
+if __name__ == "__main__":
+ select_roi(image_path)
\ No newline at end of file
diff --git a/class_xiantiao_pc/class_xiantiao.py b/class_xiantiao_pc/class_xiantiao.py
new file mode 100644
index 0000000..205404b
--- /dev/null
+++ b/class_xiantiao_pc/class_xiantiao.py
@@ -0,0 +1,85 @@
+import os
+import cv2
+from ultralytics import YOLO
+
+
+class ROIClassifier:
+ """ 封装 YOLO 分类模型 + ROI txt 推理 """
+
+ def __init__(self, model_path):
+ if not os.path.exists(model_path):
+ raise FileNotFoundError(f"模型文件不存在: {model_path}")
+
+ self.model = YOLO(model_path)
+ print(f"[INFO] 成功加载 YOLO 分类模型: {model_path}")
+
+ def load_roi(self, roi_txt_path):
+ """读取 ROI txt,可自动支持逗号/空格分隔"""
+ if not os.path.exists(roi_txt_path):
+ raise FileNotFoundError(f"ROI 文件不存在: {roi_txt_path}")
+ with open(roi_txt_path, "r") as f:
+ text = f.read().strip()
+ # 把逗号替换成空格,再 split
+ for ch in [",", ";"]:
+ text = text.replace(ch, " ")
+ parts = text.split()
+ if len(parts) != 4:
+ raise ValueError(f"ROI txt 格式错误,应为4个数字,解析得到: {parts}\n文件: {roi_txt_path}")
+ x1, y1, x2, y2 = map(int, parts)
+ return x1, y1, x2, y2
+
+ def classify(self, img_np, roi_txt_path):
+ """对 ROI 区域做分类,返回 0/1"""
+ h, w = img_np.shape[:2]
+ x1, y1, x2, y2 = self.load_roi(roi_txt_path)
+
+ # -------- ROI 边界安全裁剪 --------
+ x1 = max(0, min(x1, w - 1))
+ x2 = max(0, min(x2, w - 1))
+ y1 = max(0, min(y1, h - 1))
+ y2 = max(0, min(y2, h - 1))
+
+ if x2 <= x1 or y2 <= y1:
+ raise ValueError(f"ROI坐标无效: {x1, y1, x2, y2}")
+
+ # -------- 1. 裁剪 ROI --------
+ roi_img = img_np[y1:y2, x1:x2]
+
+ # -------- 2. resize 到 640×640(强制送给模型)--------
+ roi_img = cv2.resize(roi_img, (640, 640))
+
+ # -------- 3. YOLO 分类推理 --------
+ results = self.model.predict(roi_img, verbose=False)
+
+ cls = int(results[0].probs.top1) # 0 或 1
+ return cls
+
+
+def class_xiantiao():
+ # ================== 配置 ==================
+ model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls/exp_xiantiao_cls/weights/best.pt"
+ img_path = "1.png"
+ roi_txt_path = "/home/hx/开发/ML_xiantiao/class_xiantiao/roi_1/1/1_rois1.txt"
+
+ # ================== 1. 加载模型 ==================
+ classifier = ROIClassifier(model_path)
+
+ # ================== 2. 加载图像 ==================
+ if not os.path.exists(img_path):
+ raise FileNotFoundError(f"图片不存在: {img_path}")
+
+ img_np = cv2.imread(img_path)
+ if img_np is None:
+ raise ValueError("图像加载失败,可能路径错误或图像文件损坏。")
+
+ # ================== 3. 推理 ==================
+ result = classifier.classify(img_np, roi_txt_path)
+
+ # ================== 4. 输出 ==================
+ print(f"\n===== 推理结果 =====")
+ print(f"ROI 文件:{roi_txt_path}")
+ print(f"分类结果:{result} (0=异常 / 1=正常)\n")
+
+
+if __name__ == "__main__":
+ class_xiantiao()
diff --git a/class_xiantiao_pc/cls_quexian.py b/class_xiantiao_pc/cls_quexian.py
new file mode 100644
index 0000000..36d0eb0
--- /dev/null
+++ b/class_xiantiao_pc/cls_quexian.py
@@ -0,0 +1,63 @@
+import os
+import cv2
+from ultralytics import YOLO
+
+
+class FullImageClassifier:
+ """封装 YOLO 分类模型,对整张图像进行二分类(无缺陷/有缺陷)"""
+
+ def __init__(self, model_path):
+ if not os.path.exists(model_path):
+ raise FileNotFoundError(f"模型文件不存在: {model_path}")
+
+ self.model = YOLO(model_path)
+ print(f"[INFO] 成功加载 YOLO 分类模型: {model_path}")
+
+ def classify(self, img_np):
+ """
+ 对整张图像进行分类,返回类别 ID(0 或 1)
+
+ Args:
+ img_np (np.ndarray): BGR 格式的 OpenCV 图像 (H, W, C)
+
+ Returns:
+ int: 分类结果,{0: "有缺陷", 1: "无缺陷"}
+ """
+ # 直接 resize 整图到模型输入尺寸(YOLO 默认为 224x224 或 640x640,由训练决定)
+ # Ultralytics YOLO 会自动处理 resize,但显式指定更可控
+ resized_img = cv2.resize(img_np, (640, 640))
+
+ # 推理(verbose=False 关闭进度条)
+ results = self.model.predict(resized_img, verbose=False)
+
+ cls = int(results[0].probs.top1) # 获取 top-1 类别索引
+ return cls
+
+
+def cls_quexian():
+ # ================== 配置 ==================
+ model_path = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls/exp_xiantiao_cls/weights/best.pt"
+ img_path = "1.png"
+
+ # ================== 1. 加载模型 ==================
+ classifier = FullImageClassifier(model_path)
+
+ # ================== 2. 加载图像 ==================
+ if not os.path.exists(img_path):
+ raise FileNotFoundError(f"图片不存在: {img_path}")
+
+ img_np = cv2.imread(img_path)
+ if img_np is None:
+ raise ValueError("图像加载失败,可能路径错误或图像文件损坏。")
+
+ # ================== 3. 推理(整图)==================
+ result = classifier.classify(img_np)
+
+ # ================== 4. 输出 ==================
+ label_map = {0: "有缺陷", 1: "无缺陷"}
+ print(f"\n===== 推理结果 =====")
+ print(f"分类结果:{result} → {label_map.get(result, '未知')}\n")
+
+
+if __name__ == "__main__":
+ cls_quexian()
\ No newline at end of file
diff --git a/class_xiantiao_pc/detect.py b/class_xiantiao_pc/detect.py
new file mode 100644
index 0000000..6a2f0e2
--- /dev/null
+++ b/class_xiantiao_pc/detect.py
@@ -0,0 +1,115 @@
+import os
+import cv2
+from ultralytics import YOLO
+
+
+class ObjectDetector:
+ """封装 YOLO 目标检测模型,检测图像中的缺陷"""
+
+ def __init__(self, model_path):
+ if not os.path.exists(model_path):
+ raise FileNotFoundError(f"模型文件不存在: {model_path}")
+ self.model = YOLO(model_path)
+ print(f"[INFO] 成功加载 YOLO 目标检测模型: {model_path}")
+
+ def detect(self, img_np, conf_threshold=0.5):
+ # 注意:这里先不设 conf 阈值,以便后续按类别筛选最高分
+ results = self.model.predict(img_np, conf=0.0, verbose=False) # 获取所有预测
+ detections = []
+ for result in results:
+ boxes = result.boxes.cpu().numpy()
+ for box in boxes:
+ if box.conf.item() >= conf_threshold: # 在 Python 层过滤
+ detection_info = {
+ 'bbox': box.xyxy[0],
+ 'confidence': box.conf.item(),
+ 'class_id': int(box.cls.item())
+ }
+ detections.append(detection_info)
+ return detections
+
+
+def detect_quexian(img_path="1.png", model_path="/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls/exp_xiantiao_cls/weights/best.pt",
+ conf_threshold=0.5, debug=False):
+ """
+ 检测木条图像中的孔洞/裂缝缺陷,并返回是否为良品。
+ 每个类别仅保留置信度最高的一个框。
+
+ Args:
+ img_path (str): 输入图像路径
+ model_path (str): YOLO 检测模型路径(必须是 detect 任务训练的)
+ conf_threshold (float): 置信度阈值
+ debug (bool): 是否启用调试模式(打印详细信息)
+
+ Returns:
+ bool: True 表示无缺陷(良品),False 表示有缺陷(不良品)
+ """
+ # 1. 加载图像
+ if not os.path.exists(img_path):
+ raise FileNotFoundError(f"图片不存在: {img_path}")
+ img_np = cv2.imread(img_path)
+ if img_np is None:
+ raise ValueError("图像加载失败,可能路径错误或图像文件损坏。")
+
+ # 2. 加载模型并检测(获取所有 ≥ conf_threshold 的框)
+ detector = ObjectDetector(model_path)
+ all_detections = detector.detect(img_np, conf_threshold=conf_threshold)
+
+ # 3. 按类别分组,取每个类别中置信度最高的框
+ best_per_class = {}
+ for det in all_detections:
+ cls_id = det['class_id']
+ if cls_id not in best_per_class or det['confidence'] > best_per_class[cls_id]['confidence']:
+ best_per_class[cls_id] = det
+
+ # 转为列表(用于后续处理)
+ top_detections = list(best_per_class.values())
+
+ # 4. 判定是否有缺陷:只要有一个类别有框,就算有缺陷
+ has_defect = len(top_detections) > 0
+ is_good = not has_defect
+
+ # 5. 可视化:只绘制每个类别的最高置信度框
+ label_map = {0: "hole", 1: "crack"}
+ vis_img = img_np.copy()
+ for det in top_detections:
+ x1, y1, x2, y2 = map(int, det['bbox'])
+ conf = det['confidence']
+ cls_id = det['class_id']
+ label = f"{label_map.get(cls_id, '未知')} {conf:.2f}"
+ cv2.rectangle(vis_img, (x1, y1), (x2, y2), (0, 0, 255), 2)
+ cv2.putText(vis_img, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
+
+ # 6. 调试信息(仅在 debug=True 时输出)
+ if debug:
+ print(f"\n===== 缺陷检测结果 (DEBUG 模式) =====")
+ print(f"置信度阈值: {conf_threshold}")
+ print(f"有效类别数量: {len(top_detections)}")
+ for i, det in enumerate(top_detections):
+ cls_name = label_map.get(det['class_id'], '未知')
+ bbox_int = det['bbox'].astype(int).tolist()
+ print(f" - 类别 '{cls_name}' 最高置信度框: 置信度={det['confidence']:.3f}, bbox={bbox_int}")
+
+ # 7. 显示结果图像
+ cv2.imshow('Detection Results', vis_img)
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+ return is_good
+
+
+if __name__ == "__main__":
+ # 示例:启用 debug 模式
+ is_good_product = detect_quexian(
+ #img_path="/home/hx/开发/ML_xiantiao/class_xiantiao_pc/test_image/val/1.jpg",
+ img_path="/home/hx/开发/ML_xiantiao/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227.jpg",
+ model_path="/home/hx/yolo/ultralytics_yolo11-main/runs/train/exp_detect/weights/best.pt",
+ conf_threshold=0.5,
+ debug=True # 改为 False 即静默模式
+ )
+
+ # 主程序最终输出(简洁版)
+ if is_good_product:
+ print("产品合格")
+ else:
+ print("产品存在缺陷")
\ No newline at end of file
diff --git a/class_xiantiao_pc/divid_val.py b/class_xiantiao_pc/divid_val.py
new file mode 100644
index 0000000..a8106fa
--- /dev/null
+++ b/class_xiantiao_pc/divid_val.py
@@ -0,0 +1,108 @@
+import os
+import shutil
+import random
+from pathlib import Path
+
+
+def split_train_to_val(train_dir, val_dir, ratio=0.1, seed=42):
+ """
+ 从 train_dir 随机抽取 ratio 比例的数据到 val_dir。
+ 自动判断是分类结构(有子文件夹)还是平铺结构(无子文件夹)。
+
+ Args:
+ train_dir (str): 训练集路径
+ val_dir (str): 验证集路径(会自动创建)
+ ratio (float): 抽取比例,如 0.1 表示 10%
+ seed (int): 随机种子,保证可复现
+ """
+ train_path = Path(train_dir)
+ val_path = Path(val_dir)
+
+ if not train_path.exists():
+ raise FileNotFoundError(f"训练目录不存在: {train_path}")
+
+ # 设置随机种子
+ random.seed(seed)
+
+ # 获取所有一级子项
+ items = [p for p in train_path.iterdir()]
+
+ # 判断是否为分类结构:所有子项都是目录
+ is_classification = all(p.is_dir() for p in items) and len(items) > 0
+
+ if is_classification:
+ print("📁 检测到分类结构(含类别子文件夹)")
+ for class_dir in items:
+ class_name = class_dir.name
+ src_class_dir = train_path / class_name
+ dst_class_dir = val_path / class_name
+ dst_class_dir.mkdir(parents=True, exist_ok=True)
+
+ # 获取该类下所有文件(只取图像,但会连带移动同名标签)
+ files = [f for f in src_class_dir.iterdir() if f.is_file()]
+ if not files:
+ print(f" ⚠️ 类别 '{class_name}' 为空,跳过")
+ continue
+
+ # 随机抽取
+ num_val = max(1, int(len(files) * ratio)) # 至少抽1个
+ val_files = random.sample(files, num_val)
+
+ # 移动文件(包括可能的同名标签)
+ for f in val_files:
+ # 移动主文件
+ shutil.move(str(f), str(dst_class_dir / f.name))
+ # 尝试移动同名不同扩展名的标签(如 .txt)
+ for ext in ['.txt', '.xml', '.json']:
+ label_file = f.with_suffix(ext)
+ if label_file.exists():
+ shutil.move(str(label_file), str(dst_class_dir / label_file.name))
+
+ print(f" ✅ 类别 '{class_name}': {len(val_files)} / {len(files)} 已移至 val")
+
+ else:
+ print("📄 检测到平铺结构(无类别子文件夹)")
+ val_path.mkdir(parents=True, exist_ok=True)
+
+ # 获取所有文件,按“主文件”分组(如 img.jpg 和 img.txt 视为一组)
+ all_files = [f for f in train_path.iterdir() if f.is_file()]
+ # 提取所有不带扩展名的 stem(去重)
+ stems = set(f.stem for f in all_files)
+ file_groups = []
+ for stem in stems:
+ group = [f for f in all_files if f.stem == stem]
+ file_groups.append(group)
+
+ if not file_groups:
+ print("⚠️ 训练目录为空")
+ return
+
+ # 随机抽取组
+ num_val = max(1, int(len(file_groups) * ratio))
+ val_groups = random.sample(file_groups, num_val)
+
+ # 移动每组所有文件
+ for group in val_groups:
+ for f in group:
+ shutil.move(str(f), str(val_path / f.name))
+
+ print(f"✅ 平铺结构: {len(val_groups)} 组 / {len(file_groups)} 组 已移至 val")
+
+ print(f"\n🎉 分割完成!验证集已保存至: {val_path}")
+
+
+# ======================
+# 使用示例
+# ======================
+if __name__ == "__main__":
+ # 修改为你自己的路径
+ #TRAIN_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-new/19cc/train"
+ #VAL_DIR = "/media/hx/04e879fa-d697-4b02-ac7e-a4148876ebb0/dataset/cls-new/19cc/val"
+ TRAIN_DIR = "/home/hx/开发/ML_xiantiao/image/datasetr1/train"
+ VAL_DIR = "/home/hx/开发/ML_xiantiao/image/datasetr1/val"
+ split_train_to_val(
+ train_dir=TRAIN_DIR,
+ val_dir=VAL_DIR,
+ ratio=0.1, # 抽取 10%
+ seed=25 # 随机种子
+ )
\ No newline at end of file
diff --git a/class_xiantiao_pc/image_test.py b/class_xiantiao_pc/image_test.py
new file mode 100644
index 0000000..e8d476e
--- /dev/null
+++ b/class_xiantiao_pc/image_test.py
@@ -0,0 +1,71 @@
+import cv2
+import numpy as np
+import os
+
+
+def detect_cracks_in_wood(image_path, output_dir="output"):
+ """
+ 检测木条图像中的裂缝缺陷(线状或不规则形状)
+
+ Args:
+ image_path (str): 输入图像路径
+ output_dir (str): 结果保存目录
+ """
+ os.makedirs(output_dir, exist_ok=True)
+
+ # 1. 读取图像
+ img = cv2.imread(image_path)
+ if img is None:
+ raise FileNotFoundError(f"无法读取图像: {image_path}")
+
+ original = img.copy()
+ gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
+
+ # 2. 使用高斯模糊去除噪声,并增强裂缝对比度
+ blurred = cv2.GaussianBlur(gray, (5, 5), 0)
+
+ # 3. 使用Canny边缘检测算法查找边缘
+ edges = cv2.Canny(blurred, 50, 150)
+
+ # 4. 形态学操作:使用闭运算填充裂缝间的空隙
+ kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
+ closed_edges = cv2.morphologyEx(edges, cv2.MORPH_CLOSE, kernel, iterations=2)
+
+ # 5. 查找轮廓
+ contours, _ = cv2.findContours(closed_edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
+
+ # 6. 筛选疑似裂缝的轮廓
+ min_length = 200 # 最小长度(像素),根据实际情况调整
+ max_gap = 10 # 连接断开点的最大距离(像素)
+
+ crack_contours = []
+ for cnt in contours:
+ length = cv2.arcLength(cnt, False) # 计算曲线长度
+ if length > min_length:
+ crack_contours.append(cnt)
+
+ # 7. 在原图上绘制检测到的裂缝
+ result_img = original.copy()
+ cv2.drawContours(result_img, crack_contours, -1, (0, 0, 255), 2) # 红色框
+
+ # 8. 保存结果
+ base_name = os.path.splitext(os.path.basename(image_path))[0]
+ cv2.imwrite(os.path.join(output_dir, f"{base_name}_edges.png"), edges)
+ cv2.imwrite(os.path.join(output_dir, f"{base_name}_closed_edges.png"), closed_edges)
+ cv2.imwrite(os.path.join(output_dir, f"{base_name}_result.png"), result_img)
+
+ print(f"✅ 检测完成!共找到 {len(crack_contours)} 个疑似裂缝")
+ print(f"📁 结果已保存至: {output_dir}")
+
+ # (可选)显示
+ cv2.imshow("Original", cv2.resize(original, (640, 480)))
+ cv2.imshow("Edges", cv2.resize(edges, (640, 480)))
+ cv2.imshow("Closed Edges", cv2.resize(closed_edges, (640, 480)))
+ cv2.imshow("Result", cv2.resize(result_img, (640, 480)))
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+
+if __name__ == "__main__":
+ image_path = "1.jpg" # ← 替换为你的木条图像路径
+ detect_cracks_in_wood(image_path)
\ No newline at end of file
diff --git a/class_xiantiao_pc/resize_dataset_image.py b/class_xiantiao_pc/resize_dataset_image.py
new file mode 100644
index 0000000..822f222
--- /dev/null
+++ b/class_xiantiao_pc/resize_dataset_image.py
@@ -0,0 +1,57 @@
+import os
+import cv2
+
+# ----------------------------
+# 配置
+# ----------------------------
+SOURCE_ROOT_DIR = "/home/hx/开发/ML_xiantiao/image/dataset1" # 原始图片根目录
+TARGET_ROOT_DIR = "/home/hx/开发/ML_xiantiao/image/datasetr1" # 输出根目录
+CLASSES = ["class0", "class1"] # 类别列表
+TARGET_SIZE = 640 # resize 尺寸
+SUBSETS = ["train", "val", "test"]
+
+# ----------------------------
+# 全局 ROI (x, y, w, h)
+# ----------------------------
+GLOBAL_ROI = [3,0,694,182]
+# ----------------------------
+# 主处理函数
+# ----------------------------
+def process_images():
+ x, y, w, h = GLOBAL_ROI
+ for subset in SUBSETS:
+ for class_dir in CLASSES:
+ src_dir = os.path.join(SOURCE_ROOT_DIR, subset, class_dir)
+ tgt_dir = os.path.join(TARGET_ROOT_DIR, subset, class_dir)
+ os.makedirs(tgt_dir, exist_ok=True)
+
+ if not os.path.exists(src_dir):
+ print(f"警告: 源目录 {src_dir} 不存在,跳过")
+ continue
+
+ for file in os.listdir(src_dir):
+ if not (file.endswith(".jpg") or file.endswith(".png")):
+ continue
+
+ img_path = os.path.join(src_dir, file)
+ img = cv2.imread(img_path)
+ if img is None:
+ print(f"❌ 无法读取图片: {img_path}")
+ continue
+
+ h_img, w_img = img.shape[:2]
+ x1, y1 = max(0, x), max(0, y)
+ x2, y2 = min(w_img, x + w), min(h_img, y + h)
+
+ cropped = img[y1:y2, x1:x2]
+ if cropped.size == 0:
+ print(f"❌ 裁剪结果为空: {file}")
+ continue
+
+ resized = cv2.resize(cropped, (TARGET_SIZE, TARGET_SIZE))
+ tgt_path = os.path.join(tgt_dir, file)
+ cv2.imwrite(tgt_path, resized)
+ print(f"✅ 图片处理完成: {subset}/{class_dir}/{file}")
+
+if __name__ == "__main__":
+ process_images()
\ No newline at end of file
diff --git a/class_xiantiao_pc/roi_1/1/1_rois1.txt b/class_xiantiao_pc/roi_1/1/1_rois1.txt
new file mode 100644
index 0000000..c2cbae8
--- /dev/null
+++ b/class_xiantiao_pc/roi_1/1/1_rois1.txt
@@ -0,0 +1 @@
+3,0,694,182
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train.cache b/class_xiantiao_pc/test_image/train.cache
new file mode 100644
index 0000000..a0617e5
Binary files /dev/null and b/class_xiantiao_pc/test_image/train.cache differ
diff --git a/class_xiantiao_pc/test_image/train/1.jpg b/class_xiantiao_pc/test_image/train/1.jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/1.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/1.txt b/class_xiantiao_pc/test_image/train/1.txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/1.txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/1(另一个复件).jpg b/class_xiantiao_pc/test_image/train/1(另一个复件).jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/1(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/1(另一个复件).txt b/class_xiantiao_pc/test_image/train/1(另一个复件).txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/1(另一个复件).txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/1(复件).jpg b/class_xiantiao_pc/test_image/train/1(复件).jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/1(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/1(复件).txt b/class_xiantiao_pc/test_image/train/1(复件).txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/1(复件).txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/1(第 3 个复件).jpg b/class_xiantiao_pc/test_image/train/1(第 3 个复件).jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/1(第 3 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/1(第 3 个复件).txt b/class_xiantiao_pc/test_image/train/1(第 3 个复件).txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/1(第 3 个复件).txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/1(第 4 个复件).jpg b/class_xiantiao_pc/test_image/train/1(第 4 个复件).jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/1(第 4 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/1(第 4 个复件).txt b/class_xiantiao_pc/test_image/train/1(第 4 个复件).txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/1(第 4 个复件).txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/1(第 5 个复件).jpg b/class_xiantiao_pc/test_image/train/1(第 5 个复件).jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/1(第 5 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/1(第 5 个复件).txt b/class_xiantiao_pc/test_image/train/1(第 5 个复件).txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/1(第 5 个复件).txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/2.jpg b/class_xiantiao_pc/test_image/train/2.jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/2.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/2.txt b/class_xiantiao_pc/test_image/train/2.txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/2.txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/2(另一个复件).jpg b/class_xiantiao_pc/test_image/train/2(另一个复件).jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/2(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/2(另一个复件).txt b/class_xiantiao_pc/test_image/train/2(另一个复件).txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/2(另一个复件).txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/2(复件).jpg b/class_xiantiao_pc/test_image/train/2(复件).jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/2(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/2(复件).txt b/class_xiantiao_pc/test_image/train/2(复件).txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/2(复件).txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/2(第 3 个复件).jpg b/class_xiantiao_pc/test_image/train/2(第 3 个复件).jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/2(第 3 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/2(第 3 个复件).txt b/class_xiantiao_pc/test_image/train/2(第 3 个复件).txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/2(第 3 个复件).txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/2(第 4 个复件).jpg b/class_xiantiao_pc/test_image/train/2(第 4 个复件).jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/2(第 4 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/2(第 4 个复件).txt b/class_xiantiao_pc/test_image/train/2(第 4 个复件).txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/2(第 4 个复件).txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/2(第 5 个复件).jpg b/class_xiantiao_pc/test_image/train/2(第 5 个复件).jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/2(第 5 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/2(第 5 个复件).txt b/class_xiantiao_pc/test_image/train/2(第 5 个复件).txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/2(第 5 个复件).txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/3.jpg b/class_xiantiao_pc/test_image/train/3.jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/3.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/3.txt b/class_xiantiao_pc/test_image/train/3.txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/3.txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/3(另一个复件).jpg b/class_xiantiao_pc/test_image/train/3(另一个复件).jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/3(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/3(另一个复件).txt b/class_xiantiao_pc/test_image/train/3(另一个复件).txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/3(另一个复件).txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/3(复件).jpg b/class_xiantiao_pc/test_image/train/3(复件).jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/3(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/3(复件).txt b/class_xiantiao_pc/test_image/train/3(复件).txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/3(复件).txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/3(第 3 个复件).jpg b/class_xiantiao_pc/test_image/train/3(第 3 个复件).jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/3(第 3 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/3(第 3 个复件).txt b/class_xiantiao_pc/test_image/train/3(第 3 个复件).txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/3(第 3 个复件).txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/3(第 4 个复件).jpg b/class_xiantiao_pc/test_image/train/3(第 4 个复件).jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/3(第 4 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/3(第 4 个复件).txt b/class_xiantiao_pc/test_image/train/3(第 4 个复件).txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/3(第 4 个复件).txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/3(第 5 个复件).jpg b/class_xiantiao_pc/test_image/train/3(第 5 个复件).jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/3(第 5 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/3(第 5 个复件).txt b/class_xiantiao_pc/test_image/train/3(第 5 个复件).txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/3(第 5 个复件).txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/4.jpg b/class_xiantiao_pc/test_image/train/4.jpg
new file mode 100644
index 0000000..db96106
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/4.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/4.txt b/class_xiantiao_pc/test_image/train/4.txt
new file mode 100644
index 0000000..13b84c3
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/4.txt
@@ -0,0 +1 @@
+0 0.535815 0.542963 0.570926 0.593823
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/4(另一个复件).jpg b/class_xiantiao_pc/test_image/train/4(另一个复件).jpg
new file mode 100644
index 0000000..db96106
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/4(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/4(另一个复件).txt b/class_xiantiao_pc/test_image/train/4(另一个复件).txt
new file mode 100644
index 0000000..13b84c3
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/4(另一个复件).txt
@@ -0,0 +1 @@
+0 0.535815 0.542963 0.570926 0.593823
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/4(复件).jpg b/class_xiantiao_pc/test_image/train/4(复件).jpg
new file mode 100644
index 0000000..db96106
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/4(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/4(复件).txt b/class_xiantiao_pc/test_image/train/4(复件).txt
new file mode 100644
index 0000000..13b84c3
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/4(复件).txt
@@ -0,0 +1 @@
+0 0.535815 0.542963 0.570926 0.593823
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/4(第 3 个复件).jpg b/class_xiantiao_pc/test_image/train/4(第 3 个复件).jpg
new file mode 100644
index 0000000..db96106
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/4(第 3 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/4(第 3 个复件).txt b/class_xiantiao_pc/test_image/train/4(第 3 个复件).txt
new file mode 100644
index 0000000..13b84c3
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/4(第 3 个复件).txt
@@ -0,0 +1 @@
+0 0.535815 0.542963 0.570926 0.593823
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_205.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_205.jpg
new file mode 100644
index 0000000..227d88e
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_205.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_205.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_205.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_206.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_206.jpg
new file mode 100644
index 0000000..24a96b3
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_206.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_206.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_206.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_207.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_207.jpg
new file mode 100644
index 0000000..97821f4
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_207.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_207.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_207.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_208.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_208.jpg
new file mode 100644
index 0000000..3f497c5
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_208.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_208.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_208.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_209.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_209.jpg
new file mode 100644
index 0000000..2ac231c
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_209.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_209.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_209.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_210.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_210.jpg
new file mode 100644
index 0000000..1f69882
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_210.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_210.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_210.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_211.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_211.jpg
new file mode 100644
index 0000000..dd02474
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_211.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_211.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_211.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_212.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_212.jpg
new file mode 100644
index 0000000..7a18096
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_212.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_212.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_212.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_213.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_213.jpg
new file mode 100644
index 0000000..95ed53a
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_213.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_213.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_213.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214.jpg
new file mode 100644
index 0000000..e6eb0fe
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214.txt
new file mode 100644
index 0000000..57b7efc
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214.txt
@@ -0,0 +1 @@
+0 0.493643 0.417761 0.436794 0.658617
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(另一个复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(另一个复件).jpg
new file mode 100644
index 0000000..e6eb0fe
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(另一个复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(另一个复件).txt
new file mode 100644
index 0000000..57b7efc
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(另一个复件).txt
@@ -0,0 +1 @@
+0 0.493643 0.417761 0.436794 0.658617
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(复件).jpg
new file mode 100644
index 0000000..e6eb0fe
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(复件).txt
new file mode 100644
index 0000000..57b7efc
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_214(复件).txt
@@ -0,0 +1 @@
+0 0.493643 0.417761 0.436794 0.658617
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_215.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_215.jpg
new file mode 100644
index 0000000..f8e2d38
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_215.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_215.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_215.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_216.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_216.jpg
new file mode 100644
index 0000000..a9482dc
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_216.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_216.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_216.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_217.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_217.jpg
new file mode 100644
index 0000000..d5cad2e
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_217.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_217.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_217.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_218.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_218.jpg
new file mode 100644
index 0000000..3a0c622
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_218.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_218.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_218.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219.jpg
new file mode 100644
index 0000000..3f07c31
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219.txt
new file mode 100644
index 0000000..4a746cf
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219.txt
@@ -0,0 +1 @@
+1 0.682482 0.234558 0.050639 0.296622
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(另一个复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(另一个复件).jpg
new file mode 100644
index 0000000..3f07c31
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(另一个复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(另一个复件).txt
new file mode 100644
index 0000000..4a746cf
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(另一个复件).txt
@@ -0,0 +1 @@
+1 0.682482 0.234558 0.050639 0.296622
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(复件).jpg
new file mode 100644
index 0000000..3f07c31
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(复件).txt
new file mode 100644
index 0000000..4a746cf
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(复件).txt
@@ -0,0 +1 @@
+1 0.682482 0.234558 0.050639 0.296622
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(第 3 个复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(第 3 个复件).jpg
new file mode 100644
index 0000000..3f07c31
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(第 3 个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(第 3 个复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(第 3 个复件).txt
new file mode 100644
index 0000000..4a746cf
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_219(第 3 个复件).txt
@@ -0,0 +1 @@
+1 0.682482 0.234558 0.050639 0.296622
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_220.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_220.jpg
new file mode 100644
index 0000000..2c437e9
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_220.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_220.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_220.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_221.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_221.jpg
new file mode 100644
index 0000000..07772c3
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_221.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_221.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_221.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_222.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_222.jpg
new file mode 100644
index 0000000..b61322a
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_222.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_222.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_222.txt
new file mode 100644
index 0000000..4d75971
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_222.txt
@@ -0,0 +1 @@
+1 0.789386 0.558403 0.128296 0.055070
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_223.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_223.jpg
new file mode 100644
index 0000000..f42f7c1
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_223.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_223.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_223.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_224.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_224.jpg
new file mode 100644
index 0000000..ee7bdcd
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_224.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_224.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_224.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_225.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_225.jpg
new file mode 100644
index 0000000..fff65aa
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_225.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_225.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_225.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_226.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_226.jpg
new file mode 100644
index 0000000..3f86467
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_226.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_226.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_226.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227.jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227.jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227.jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227.txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227.txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227.txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(另一个复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(另一个复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(另一个复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(另一个复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(另一个复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(另一个复件)(复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(另一个复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(另一个复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(另一个复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(另一个复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(另一个复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(复件).jpg b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(复件).txt b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/train/微信图片_20251216095823_227(复件)(复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val.cache b/class_xiantiao_pc/test_image/val.cache
new file mode 100644
index 0000000..40d4a10
Binary files /dev/null and b/class_xiantiao_pc/test_image/val.cache differ
diff --git a/class_xiantiao_pc/test_image/val/1.jpg b/class_xiantiao_pc/test_image/val/1.jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/1.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/1.txt b/class_xiantiao_pc/test_image/val/1.txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/1.txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/1(另一个复件).jpg b/class_xiantiao_pc/test_image/val/1(另一个复件).jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/1(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/1(另一个复件).txt b/class_xiantiao_pc/test_image/val/1(另一个复件).txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/1(另一个复件).txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/1(复件).jpg b/class_xiantiao_pc/test_image/val/1(复件).jpg
new file mode 100644
index 0000000..3e8a46b
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/1(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/1(复件).txt b/class_xiantiao_pc/test_image/val/1(复件).txt
new file mode 100644
index 0000000..48a6713
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/1(复件).txt
@@ -0,0 +1 @@
+1 0.614113 0.455275 0.664973 0.245135
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/2.jpg b/class_xiantiao_pc/test_image/val/2.jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/2.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/2.txt b/class_xiantiao_pc/test_image/val/2.txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/2.txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/2(另一个复件).jpg b/class_xiantiao_pc/test_image/val/2(另一个复件).jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/2(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/2(另一个复件).txt b/class_xiantiao_pc/test_image/val/2(另一个复件).txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/2(另一个复件).txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/2(复件).jpg b/class_xiantiao_pc/test_image/val/2(复件).jpg
new file mode 100644
index 0000000..5bc7e8f
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/2(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/2(复件).txt b/class_xiantiao_pc/test_image/val/2(复件).txt
new file mode 100644
index 0000000..9eab983
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/2(复件).txt
@@ -0,0 +1,2 @@
+0 0.325184 0.190563 0.102830 0.112333
+0 0.532322 0.519080 0.408382 0.188687
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/3.jpg b/class_xiantiao_pc/test_image/val/3.jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/3.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/3.txt b/class_xiantiao_pc/test_image/val/3.txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/3.txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/3(另一个复件).jpg b/class_xiantiao_pc/test_image/val/3(另一个复件).jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/3(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/3(另一个复件).txt b/class_xiantiao_pc/test_image/val/3(另一个复件).txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/3(另一个复件).txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/3(复件).jpg b/class_xiantiao_pc/test_image/val/3(复件).jpg
new file mode 100644
index 0000000..c896240
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/3(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/3(复件).txt b/class_xiantiao_pc/test_image/val/3(复件).txt
new file mode 100644
index 0000000..243b11e
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/3(复件).txt
@@ -0,0 +1 @@
+0 0.670369 0.354613 0.214830 0.096716
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/4.jpg b/class_xiantiao_pc/test_image/val/4.jpg
new file mode 100644
index 0000000..db96106
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/4.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/4.txt b/class_xiantiao_pc/test_image/val/4.txt
new file mode 100644
index 0000000..13b84c3
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/4.txt
@@ -0,0 +1 @@
+0 0.535815 0.542963 0.570926 0.593823
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/4(另一个复件).jpg b/class_xiantiao_pc/test_image/val/4(另一个复件).jpg
new file mode 100644
index 0000000..db96106
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/4(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/4(另一个复件).txt b/class_xiantiao_pc/test_image/val/4(另一个复件).txt
new file mode 100644
index 0000000..13b84c3
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/4(另一个复件).txt
@@ -0,0 +1 @@
+0 0.535815 0.542963 0.570926 0.593823
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/4(复件).jpg b/class_xiantiao_pc/test_image/val/4(复件).jpg
new file mode 100644
index 0000000..db96106
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/4(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/4(复件).txt b/class_xiantiao_pc/test_image/val/4(复件).txt
new file mode 100644
index 0000000..13b84c3
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/4(复件).txt
@@ -0,0 +1 @@
+0 0.535815 0.542963 0.570926 0.593823
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_208.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_208.jpg
new file mode 100644
index 0000000..3f497c5
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_208.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_208.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_208.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_209.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_209.jpg
new file mode 100644
index 0000000..2ac231c
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_209.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_209.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_209.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_210.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_210.jpg
new file mode 100644
index 0000000..1f69882
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_210.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_210.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_210.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214.jpg
new file mode 100644
index 0000000..e6eb0fe
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214.txt
new file mode 100644
index 0000000..57b7efc
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214.txt
@@ -0,0 +1 @@
+0 0.493643 0.417761 0.436794 0.658617
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(另一个复件).jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(另一个复件).jpg
new file mode 100644
index 0000000..e6eb0fe
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(另一个复件).txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(另一个复件).txt
new file mode 100644
index 0000000..57b7efc
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(另一个复件).txt
@@ -0,0 +1 @@
+0 0.493643 0.417761 0.436794 0.658617
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(复件).jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(复件).jpg
new file mode 100644
index 0000000..e6eb0fe
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(复件).txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(复件).txt
new file mode 100644
index 0000000..57b7efc
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_214(复件).txt
@@ -0,0 +1 @@
+0 0.493643 0.417761 0.436794 0.658617
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_215.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_215.jpg
new file mode 100644
index 0000000..f8e2d38
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_215.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_215.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_215.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_216.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_216.jpg
new file mode 100644
index 0000000..a9482dc
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_216.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_216.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_216.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_217.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_217.jpg
new file mode 100644
index 0000000..d5cad2e
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_217.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_217.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_217.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_218.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_218.jpg
new file mode 100644
index 0000000..3a0c622
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_218.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_218.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_218.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219.jpg
new file mode 100644
index 0000000..3f07c31
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219.txt
new file mode 100644
index 0000000..4a746cf
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219.txt
@@ -0,0 +1 @@
+1 0.682482 0.234558 0.050639 0.296622
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(另一个复件).jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(另一个复件).jpg
new file mode 100644
index 0000000..3f07c31
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(另一个复件).txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(另一个复件).txt
new file mode 100644
index 0000000..4a746cf
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(另一个复件).txt
@@ -0,0 +1 @@
+1 0.682482 0.234558 0.050639 0.296622
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(复件).jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(复件).jpg
new file mode 100644
index 0000000..3f07c31
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(复件).txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(复件).txt
new file mode 100644
index 0000000..4a746cf
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_219(复件).txt
@@ -0,0 +1 @@
+1 0.682482 0.234558 0.050639 0.296622
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_220.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_220.jpg
new file mode 100644
index 0000000..2c437e9
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_220.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_220.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_220.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_221.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_221.jpg
new file mode 100644
index 0000000..07772c3
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_221.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_221.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_221.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_224.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_224.jpg
new file mode 100644
index 0000000..ee7bdcd
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_224.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_224.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_224.txt
new file mode 100644
index 0000000..e69de29
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227.jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227.jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227.jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227.txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227.txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227.txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件).jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件).txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(另一个复件).jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(另一个复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(另一个复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(另一个复件).txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(另一个复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(另一个复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(复件).jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(复件).txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(另一个复件)(复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(复件).jpg b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(复件).jpg
new file mode 100644
index 0000000..2dbdfc8
Binary files /dev/null and b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(复件).jpg differ
diff --git a/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(复件).txt b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(复件).txt
new file mode 100644
index 0000000..10a75f1
--- /dev/null
+++ b/class_xiantiao_pc/test_image/val/微信图片_20251216095823_227(复件).txt
@@ -0,0 +1 @@
+0 0.527443 0.369660 0.108679 0.055803
\ No newline at end of file
diff --git a/class_xiantiao_pc/tuili_f.py b/class_xiantiao_pc/tuili_f.py
new file mode 100644
index 0000000..10ec9e0
--- /dev/null
+++ b/class_xiantiao_pc/tuili_f.py
@@ -0,0 +1,69 @@
+import os
+from pathlib import Path
+import cv2
+from ultralytics import YOLO
+
+# ---------------------------
+# 配置路径(请按需修改)
+# ---------------------------
+MODEL_PATH = "/home/hx/yolo/ultralytics_yolo11-main/runs/train/cls/exp_xiantiao_cls/weights/best.pt" # 你的二分类模型
+INPUT_FOLDER = "/home/hx/开发/ML_xiantiao/image/test" # 输入图像文件夹
+OUTPUT_ROOT = "/home/hx/开发/ML_xiantiao/image/test_result" # 输出根目录(会生成 合格/不合格 子文件夹)
+
+# 类别映射(必须与训练时的 data.yaml 顺序一致)
+CLASS_NAMES = {0: "不合格", 1: "合格"}
+
+# ---------------------------
+# 批量推理函数
+# ---------------------------
+def batch_classify(model_path, input_folder, output_root):
+ # 加载模型
+ model = YOLO(model_path)
+ print(f"✅ 模型加载成功: {model_path}")
+
+ # 创建输出目录
+ output_root = Path(output_root)
+ for cls_name in CLASS_NAMES.values():
+ (output_root / cls_name).mkdir(parents=True, exist_ok=True)
+
+ # 支持的图像格式
+ IMG_EXTS = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff'}
+
+ input_dir = Path(input_folder)
+ processed = 0
+
+ for img_path in input_dir.iterdir():
+ if img_path.suffix.lower() not in IMG_EXTS:
+ continue
+
+ # 读取图像
+ img = cv2.imread(str(img_path))
+ if img is None:
+ print(f"❌ 无法读取: {img_path}")
+ continue
+
+ # 推理(整图)
+ results = model(img)
+ probs = results[0].probs.data.cpu().numpy()
+ pred_class_id = int(probs.argmax())
+ pred_label = CLASS_NAMES[pred_class_id]
+ confidence = float(probs[pred_class_id])
+
+ # 保存原图到对应文件夹
+ dst = output_root / pred_label / img_path.name
+ cv2.imwrite(str(dst), img)
+
+ print(f"✅ {img_path.name} → {pred_label} ({confidence:.2f})")
+ processed += 1
+
+ print(f"\n🎉 共处理 {processed} 张图像,结果已保存至: {output_root}")
+
+# ---------------------------
+# 运行入口
+# ---------------------------
+if __name__ == "__main__":
+ batch_classify(
+ model_path=MODEL_PATH,
+ input_folder=INPUT_FOLDER,
+ output_root=OUTPUT_ROOT
+ )
\ No newline at end of file
diff --git a/class_xiantiao_rknn/1.png b/class_xiantiao_rknn/1.png
new file mode 100644
index 0000000..7395075
Binary files /dev/null and b/class_xiantiao_rknn/1.png differ
diff --git a/class_xiantiao_rknn/2.png b/class_xiantiao_rknn/2.png
new file mode 100644
index 0000000..b79b9bc
Binary files /dev/null and b/class_xiantiao_rknn/2.png differ
diff --git a/class_xiantiao_rknn/main_cls.py b/class_xiantiao_rknn/main_cls.py
new file mode 100644
index 0000000..791fdad
--- /dev/null
+++ b/class_xiantiao_rknn/main_cls.py
@@ -0,0 +1,99 @@
+import cv2
+import numpy as np
+import platform
+from rknnlite.api import RKNNLite
+
+# ------------------- 全局变量 -------------------
+_global_rknn_instance = None
+labels = {0: '无线条', 1: '有线条'}
+
+# ROI: x, y, w, h
+ROI = (3,0,694,182)
+
+DEVICE_COMPATIBLE_NODE = '/proc/device-tree/compatible'
+
+
+# ------------------- 主机信息 -------------------
+def get_host():
+ system = platform.system()
+ machine = platform.machine()
+ os_machine = system + '-' + machine
+ if os_machine == 'Linux-aarch64':
+ try:
+ with open(DEVICE_COMPATIBLE_NODE) as f:
+ device_compatible_str = f.read()
+ if 'rk3562' in device_compatible_str:
+ host = 'RK3562'
+ elif 'rk3576' in device_compatible_str:
+ host = 'RK3576'
+ elif 'rk3588' in device_compatible_str:
+ host = 'RK3588'
+ else:
+ host = 'RK3566_RK3568'
+ except IOError:
+ print('Read device node {} failed.'.format(DEVICE_COMPATIBLE_NODE))
+ exit(-1)
+ else:
+ host = os_machine
+ return host
+
+
+# ------------------- RKNN 模型初始化(只加载一次) -------------------
+def init_rknn_model(model_path):
+ global _global_rknn_instance
+ if _global_rknn_instance is None:
+ rknn_lite = RKNNLite(verbose=False)
+ ret = rknn_lite.load_rknn(model_path)
+ if ret != 0:
+ raise RuntimeError(f'Load model failed: {ret}')
+ ret = rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0)
+ if ret != 0:
+ raise RuntimeError(f'Init runtime failed: {ret}')
+ _global_rknn_instance = rknn_lite
+ print(f'[INFO] RKNN model loaded: {model_path}')
+ return _global_rknn_instance
+
+
+# ------------------- 图像预处理 + ROI 裁剪 -------------------
+def preprocess(raw_image, target_size=(640, 640)):
+ """
+ ROI 裁剪 + resize + batch 维度
+ """
+ global ROI
+ x, y, w, h = ROI
+ roi_img = raw_image[y:y+h, x:x+w]
+ img_resized = cv2.resize(roi_img, target_size)
+ img_batch = np.expand_dims(img_resized, 0) # 添加 batch 维度
+ return img_batch
+
+
+# ------------------- 推理函数 -------------------
+def xiantiao_cls_inference_once(rknn, raw_image, target_size=(640, 640)):
+ """
+ 使用已加载的 rknn 实例进行推理
+ 返回: (class_id, boolean)
+ """
+ img = preprocess(raw_image, target_size)
+ outputs = rknn.inference([img])
+ output = outputs[0].reshape(-1)
+ class_id = int(np.argmax(output))
+ bool_value = class_id == 1
+ return class_id, bool_value
+
+
+# ------------------- 测试 -------------------
+if __name__ == '__main__':
+ image_path = "./test_image/class1/2.jpg"
+ model_path = "xiantiao_rk3588.rknn"
+
+ bgr_image = cv2.imread(image_path)
+ if bgr_image is None:
+ raise RuntimeError(f"Failed to read image: {image_path}")
+ rgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)
+
+ # 只初始化一次模型
+ rknn_model = init_rknn_model(model_path)
+
+ # 多次调用都用同一个 rknn_model
+ class_id, bool_value = xiantiao_cls_inference_once(rknn_model, rgb_image)
+ print(f"类别ID: {class_id}, 布尔值: {bool_value}")
diff --git a/class_xiantiao_rknn/main_detect.py b/class_xiantiao_rknn/main_detect.py
new file mode 100644
index 0000000..7389250
--- /dev/null
+++ b/class_xiantiao_rknn/main_detect.py
@@ -0,0 +1,216 @@
+import cv2
+import numpy as np
+import platform
+from rknnlite.api import RKNNLite
+
+# ------------------- 全局变量 -------------------
+_global_rknn_detector = None
+labels = {0: 'hole', 1: 'crack'} # 请确保与训练时类别顺序一致
+
+DEVICE_COMPATIBLE_NODE = '/proc/device-tree/compatible'
+
+
+# ------------------- 主机信息 -------------------
+def get_host():
+ system = platform.system()
+ machine = platform.machine()
+ os_machine = system + '-' + machine
+ if os_machine == 'Linux-aarch64':
+ try:
+ with open(DEVICE_COMPATIBLE_NODE) as f:
+ device_compatible_str = f.read()
+ if 'rk3562' in device_compatible_str:
+ host = 'RK3562'
+ elif 'rk3576' in device_compatible_str:
+ host = 'RK3576'
+ elif 'rk3588' in device_compatible_str:
+ host = 'RK3588'
+ else:
+ host = 'RK3566_RK3568'
+ except IOError:
+ print('Read device node {} failed.'.format(DEVICE_COMPATIBLE_NODE))
+ exit(-1)
+ else:
+ host = os_machine
+ return host
+
+
+# ------------------- RKNN 检测模型初始化(单例) -------------------
+def init_rknn_detector(model_path):
+ global _global_rknn_detector
+ if _global_rknn_detector is None:
+ rknn = RKNNLite(verbose=False)
+ ret = rknn.load_rknn(model_path)
+ if ret != 0:
+ raise RuntimeError(f'Load RKNN detection model failed: {ret}')
+ ret = rknn.init_runtime(core_mask=RKNNLite.NPU_CORE_0)
+ if ret != 0:
+ raise RuntimeError(f'Init RKNN runtime failed: {ret}')
+ _global_rknn_detector = rknn
+ print(f'[INFO] RKNN detection model loaded: {model_path}')
+ return _global_rknn_detector
+
+
+# ------------------- 预处理:接收 RGB np.ndarray,返回模型输入 -------------------
+def preprocess_rgb_image(rgb_image, target_size=(640, 640)):
+ """
+ 输入: rgb_image (H, W, C) uint8, RGB 格式
+ 输出: (1, C, H, W) 的 np.ndarray,用于 RKNN 推理
+ """
+ resized = cv2.resize(rgb_image, target_size)
+ img_input = np.expand_dims(resized, 0) # (1, H, W, C)
+ img_input = np.transpose(img_input, (0, 3, 1, 2)) # (1, C, H, W)
+ return img_input
+
+
+# ------------------- 后处理 -------------------
+def postprocess_yolov8(outputs, conf_threshold=0.5, input_size=(640, 640)):
+ pred = outputs[0]
+ if pred.ndim == 3:
+ pred = np.squeeze(pred, axis=0)
+ pred = pred.T
+
+ boxes = pred[:, :4] # cx, cy, w, h
+ scores = pred[:, 4:]
+
+ class_ids = np.argmax(scores, axis=1)
+ confidences = np.max(scores, axis=1)
+
+ mask = confidences >= conf_threshold
+ boxes = boxes[mask]
+ confidences = confidences[mask]
+ class_ids = class_ids[mask]
+
+ x_center, y_center, w, h = boxes[:, 0], boxes[:, 1], boxes[:, 2], boxes[:, 3]
+ x1 = x_center - w / 2
+ y1 = y_center - h / 2
+ x2 = x_center + w / 2
+ y2 = y_center + h / 2
+ boxes_xyxy = np.stack((x1, y1, x2, y2), axis=1)
+
+ detections = []
+ for i in range(len(boxes_xyxy)):
+ detections.append({
+ 'bbox': boxes_xyxy[i].tolist(),
+ 'confidence': float(confidences[i]),
+ 'class_id': int(class_ids[i])
+ })
+ return detections
+
+
+# ------------------- 坐标映射回原始图像 -------------------
+def scale_detections_to_original(detections, original_shape, input_size=(640, 640)):
+ if not detections:
+ return detections
+
+ orig_h, orig_w = original_shape[:2]
+ input_w, input_h = input_size
+
+ scaled = []
+ for det in detections:
+ x1, y1, x2, y2 = det['bbox']
+ x1_orig = x1 * (orig_w / input_w)
+ y1_orig = y1 * (orig_h / input_h)
+ x2_orig = x2 * (orig_w / input_w)
+ y2_orig = y2 * (orig_h / input_h)
+ scaled.append({
+ 'bbox': [x1_orig, y1_orig, x2_orig, y2_orig],
+ 'confidence': det['confidence'],
+ 'class_id': det['class_id']
+ })
+ return scaled
+
+
+# ------------------- 主推理函数:接收 RGB np.ndarray -------------------
+def detect_quexian_from_rgb_array(
+ rgb_image: np.ndarray,
+ model_path: str = "xiantiao_detect.rknn",
+ conf_threshold: float = 0.5,
+ debug: bool = False
+) -> bool:
+ """
+ 输入:
+ rgb_image: np.ndarray, shape (H, W, 3), dtype uint8, RGB 格式
+ model_path: RKNN 模型路径
+ conf_threshold: 置信度阈值
+ debug: 是否显示结果
+
+ 返回:
+ bool: True 表示良品(无缺陷),False 表示有缺陷
+ """
+ if rgb_image is None or rgb_image.size == 0:
+ raise ValueError("输入图像为空")
+
+ # 初始化模型(单例)
+ rknn = init_rknn_detector(model_path)
+
+ # 预处理
+ img_input = preprocess_rgb_image(rgb_image, target_size=(640, 640))
+
+ # 推理
+ outputs = rknn.inference(inputs=[img_input])
+
+ # 后处理(在 640x640 坐标系)
+ detections = postprocess_yolov8(outputs, conf_threshold=conf_threshold)
+
+ # 映射回原始图像坐标
+ detections = scale_detections_to_original(detections, rgb_image.shape, input_size=(640, 640))
+
+ # 每类只保留最高置信度框
+ best_per_class = {}
+ for det in detections:
+ cls_id = det['class_id']
+ if cls_id not in best_per_class or det['confidence'] > best_per_class[cls_id]['confidence']:
+ best_per_class[cls_id] = det
+ top_detections = list(best_per_class.values())
+
+ is_good = len(top_detections) == 0
+
+ # 调试可视化(需转回 BGR 给 cv2 显示)
+ if debug:
+ bgr_vis = cv2.cvtColor(rgb_image, cv2.COLOR_RGB2BGR)
+ for det in top_detections:
+ x1, y1, x2, y2 = map(int, det['bbox'])
+ label = f"{labels.get(det['class_id'], 'unknown')} {det['confidence']:.2f}"
+ cv2.rectangle(bgr_vis, (x1, y1), (x2, y2), (0, 0, 255), 2)
+ cv2.putText(bgr_vis, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2)
+
+ print(f"\n===== RKNN Detection Result (DEBUG) =====")
+ print(f"Defect classes detected: {len(top_detections)}")
+ for det in top_detections:
+ cls_name = labels.get(det['class_id'], 'unknown')
+ print(f" - {cls_name}: conf={det['confidence']:.3f}, bbox={det['bbox']}")
+
+ cv2.imshow('Detection', bgr_vis)
+ cv2.waitKey(0)
+ cv2.destroyAllWindows()
+
+ return is_good
+
+
+# ------------------- 主函数:读图并调用 -------------------
+if __name__ == '__main__':
+ image_path = "./test_image/detect/defect1.jpg"
+ model_path = "xiantiao_detect.rknn"
+
+ # 1. 读取图像(BGR)
+ bgr_image = cv2.imread(image_path)
+ if bgr_image is None:
+ raise RuntimeError(f"Failed to read image: {image_path}")
+
+ # 2. 转为 RGB(供推理函数使用)
+ rgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)
+
+ # 3. 调用推理函数(传入 np.ndarray)
+ is_good_product = detect_quexian_from_rgb_array(
+ rgb_image=rgb_image,
+ model_path=model_path,
+ conf_threshold=0.5,
+ debug=True
+ )
+
+ # 4. 输出结果
+ if is_good_product:
+ print("✅ 产品合格(无缺陷)")
+ else:
+ print("❌ 产品存在缺陷")
\ No newline at end of file
diff --git a/class_xiantiao_rknn/main_quexian_cls.py b/class_xiantiao_rknn/main_quexian_cls.py
new file mode 100644
index 0000000..af6fd67
--- /dev/null
+++ b/class_xiantiao_rknn/main_quexian_cls.py
@@ -0,0 +1,94 @@
+import cv2
+import numpy as np
+import platform
+from rknnlite.api import RKNNLite
+
+# ------------------- 全局变量 -------------------
+_global_rknn_instance = None
+labels = {0: '无缺陷', 1: '有缺陷'}
+
+DEVICE_COMPATIBLE_NODE = '/proc/device-tree/compatible'
+
+
+# ------------------- 主机信息 -------------------
+def get_host():
+ system = platform.system()
+ machine = platform.machine()
+ os_machine = system + '-' + machine
+ if os_machine == 'Linux-aarch64':
+ try:
+ with open(DEVICE_COMPATIBLE_NODE) as f:
+ device_compatible_str = f.read()
+ if 'rk3562' in device_compatible_str:
+ host = 'RK3562'
+ elif 'rk3576' in device_compatible_str:
+ host = 'RK3576'
+ elif 'rk3588' in device_compatible_str:
+ host = 'RK3588'
+ else:
+ host = 'RK3566_RK3568'
+ except IOError:
+ print('Read device node {} failed.'.format(DEVICE_COMPATIBLE_NODE))
+ exit(-1)
+ else:
+ host = os_machine
+ return host
+
+
+# ------------------- RKNN 模型初始化(只加载一次) -------------------
+def init_rknn_model(model_path):
+ global _global_rknn_instance
+ if _global_rknn_instance is None:
+ rknn_lite = RKNNLite(verbose=False)
+ ret = rknn_lite.load_rknn(model_path)
+ if ret != 0:
+ raise RuntimeError(f'Load model failed: {ret}')
+ ret = rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0)
+ if ret != 0:
+ raise RuntimeError(f'Init runtime failed: {ret}')
+ _global_rknn_instance = rknn_lite
+ print(f'[INFO] RKNN model loaded: {model_path}')
+ return _global_rknn_instance
+
+
+# ------------------- 图像预处理(无 ROI,直接 resize 整图) -------------------
+def preprocess(raw_image, target_size=(640, 640)):
+ """
+ 直接对整张图像 resize 到模型输入尺寸,并添加 batch 维度
+ """
+ img_resized = cv2.resize(raw_image, target_size)
+ img_batch = np.expand_dims(img_resized, 0) # (H, W, C) -> (1, H, W, C)
+ return img_batch
+
+
+# ------------------- 推理函数 -------------------
+def quexian_cls_inference_once(rknn, raw_image, target_size=(640, 640)):
+ """
+ 使用已加载的 rknn 实例进行推理
+ 返回: (class_id, boolean)
+ """
+ img = preprocess(raw_image, target_size)
+ outputs = rknn.inference([img])
+ output = outputs[0].reshape(-1)
+ class_id = int(np.argmax(output))
+ bool_value = class_id == 1 # 1 表示“有线条”
+ return class_id, bool_value
+
+
+# ------------------- 测试 -------------------
+if __name__ == '__main__':
+ image_path = "./test_image/class1/2.jpg"
+ model_path = "xiantiao_rk3588.rknn"
+
+ bgr_image = cv2.imread(image_path)
+ if bgr_image is None:
+ raise RuntimeError(f"Failed to read image: {image_path}")
+ rgb_image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)
+
+ # 只初始化一次模型
+ rknn_model = init_rknn_model(model_path)
+
+ # 推理
+ class_id, bool_value = quexian_cls_inference_once(rknn_model, rgb_image)
+ print(f"类别ID: {class_id}, 布尔值: {bool_value}")
+ print(f"预测结果: {labels[class_id]}")
\ No newline at end of file
diff --git a/class_xiantiao_rknn/xiantiao_rk3588.rknn b/class_xiantiao_rknn/xiantiao_rk3588.rknn
new file mode 100644
index 0000000..df3f916
Binary files /dev/null and b/class_xiantiao_rknn/xiantiao_rk3588.rknn differ
diff --git a/hikvision/camera_capture.py b/hikvision/camera_capture.py
new file mode 100644
index 0000000..dfb4415
--- /dev/null
+++ b/hikvision/camera_capture.py
@@ -0,0 +1,625 @@
+import os
+import sys
+import numpy as np
+from os import getcwd
+import cv2
+#import msvcrt
+from ctypes import *
+
+# ================== 关键修复:设置 MVCAM_COMMON_RUNENV ==================
+# 先尝试从系统获取,如果没有则使用默认路径
+import sys
+import ctypes
+
+# 临时修复:让 WINFUNCTYPE 在 Linux 上指向 CFUNCTYPE
+if not sys.platform.startswith('win'):
+ if not hasattr(ctypes, 'WINFUNCTYPE'):
+ ctypes.WINFUNCTYPE = ctypes.CFUNCTYPE
+
+if os.getenv("MVCAM_COMMON_RUNENV") is None:
+ # 根据你的 MVS 安装位置调整这个路径!
+ default_mvs_lib_path = "/opt/MVS/lib" # 常见路径,也可能是 /opt/MVS/bin
+ os.environ["MVCAM_COMMON_RUNENV"] = default_mvs_lib_path
+ print(f"[INFO] Set MVCAM_COMMON_RUNENV to: {default_mvs_lib_path}")
+
+
+# 确保 libMvCameraControl.so 存在
+dll_path = os.path.join(os.environ["MVCAM_COMMON_RUNENV"], "64", "libMvCameraControl.so")
+if not os.path.exists(dll_path):
+ raise FileNotFoundError(f"Required library not found: {dll_path}. "
+ f"Please check your MVS installation and MVCAM_COMMON_RUNENV path.")
+sys.path.append("/opt/MVS/Samples/64/Python/MvImport")
+from MvCameraControl_class import *
+
+# 枚举设备
+def enum_devices(device = 0 , device_way = False):
+ """
+ device = 0 枚举网口、USB口、未知设备、cameralink 设备
+ device = 1 枚举GenTL设备
+ """
+ if device_way == False:
+ if device == 0:
+ tlayerType = MV_GIGE_DEVICE | MV_USB_DEVICE | MV_UNKNOW_DEVICE | MV_1394_DEVICE | MV_CAMERALINK_DEVICE
+ deviceList = MV_CC_DEVICE_INFO_LIST()
+ # 枚举设备
+ ret = MvCamera.MV_CC_EnumDevices(tlayerType, deviceList)
+ if ret != 0:
+ print("enum devices fail! ret[0x%x]" % ret)
+ sys.exit()
+ if deviceList.nDeviceNum == 0:
+ print("find no device!")
+ sys.exit()
+ print("Find %d devices!" % deviceList.nDeviceNum)
+ return deviceList
+ else:
+ pass
+ elif device_way == True:
+ pass
+
+# 判断不同类型设备
+def identify_different_devices(deviceList):
+ # 判断不同类型设备,并输出相关信息
+ for i in range(0, deviceList.nDeviceNum):
+ mvcc_dev_info = cast(deviceList.pDeviceInfo[i], POINTER(MV_CC_DEVICE_INFO)).contents
+ # 判断是否为网口相机
+ if mvcc_dev_info.nTLayerType == MV_GIGE_DEVICE:
+ print ("\n网口设备序号: [%d]" % i)
+ # 获取设备名
+ strModeName = ""
+ for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chModelName:
+ strModeName = strModeName + chr(per)
+ print ("当前设备型号名: %s" % strModeName)
+ # 获取当前设备 IP 地址
+ nip1_1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0xff000000) >> 24)
+ nip1_2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x00ff0000) >> 16)
+ nip1_3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x0000ff00) >> 8)
+ nip1_4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentIp & 0x000000ff)
+ print ("当前 ip 地址: %d.%d.%d.%d" % (nip1_1, nip1_2, nip1_3, nip1_4))
+ # 获取当前子网掩码
+ nip2_1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentSubNetMask & 0xff000000) >> 24)
+ nip2_2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentSubNetMask & 0x00ff0000) >> 16)
+ nip2_3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentSubNetMask & 0x0000ff00) >> 8)
+ nip2_4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nCurrentSubNetMask & 0x000000ff)
+ print ("当前子网掩码 : %d.%d.%d.%d" % (nip2_1, nip2_2, nip2_3, nip2_4))
+ # 获取当前网关
+ nip3_1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nDefultGateWay & 0xff000000) >> 24)
+ nip3_2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nDefultGateWay & 0x00ff0000) >> 16)
+ nip3_3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nDefultGateWay & 0x0000ff00) >> 8)
+ nip3_4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nDefultGateWay & 0x000000ff)
+ print("当前网关 : %d.%d.%d.%d" % (nip3_1, nip3_2, nip3_3, nip3_4))
+ # 获取网口 IP 地址
+ nip4_1 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nNetExport & 0xff000000) >> 24)
+ nip4_2 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nNetExport & 0x00ff0000) >> 16)
+ nip4_3 = ((mvcc_dev_info.SpecialInfo.stGigEInfo.nNetExport & 0x0000ff00) >> 8)
+ nip4_4 = (mvcc_dev_info.SpecialInfo.stGigEInfo.nNetExport & 0x000000ff)
+ print("当前连接的网口 IP 地址 : %d.%d.%d.%d" % (nip4_1, nip4_2, nip4_3, nip4_4))
+ # 获取制造商名称
+ strmanufacturerName = ""
+ for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chManufacturerName:
+ strmanufacturerName = strmanufacturerName + chr(per)
+ print("制造商名称 : %s" % strmanufacturerName)
+ # 获取设备版本
+ stdeviceversion = ""
+ for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chDeviceVersion:
+ stdeviceversion = stdeviceversion + chr(per)
+ print("设备当前使用固件版本 : %s" % stdeviceversion)
+ # 获取制造商的具体信息
+ stManufacturerSpecificInfo = ""
+ for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chManufacturerSpecificInfo:
+ stManufacturerSpecificInfo = stManufacturerSpecificInfo + chr(per)
+ print("设备制造商的具体信息 : %s" % stManufacturerSpecificInfo)
+ # 获取设备序列号
+ stSerialNumber = ""
+ for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chSerialNumber:
+ stSerialNumber = stSerialNumber + chr(per)
+ print("设备序列号 : %s" % stSerialNumber)
+ # 获取用户自定义名称
+ stUserDefinedName = ""
+ for per in mvcc_dev_info.SpecialInfo.stGigEInfo.chUserDefinedName:
+ stUserDefinedName = stUserDefinedName + chr(per)
+ print("用户自定义名称 : %s" % stUserDefinedName)
+
+ # 判断是否为 USB 接口相机
+ elif mvcc_dev_info.nTLayerType == MV_USB_DEVICE:
+ print ("\nU3V 设备序号e: [%d]" % i)
+ strModeName = ""
+ for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chModelName:
+ if per == 0:
+ break
+ strModeName = strModeName + chr(per)
+ print ("当前设备型号名 : %s" % strModeName)
+ strSerialNumber = ""
+ for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
+ if per == 0:
+ break
+ strSerialNumber = strSerialNumber + chr(per)
+ print ("当前设备序列号 : %s" % strSerialNumber)
+ # 获取制造商名称
+ strmanufacturerName = ""
+ for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chVendorName:
+ strmanufacturerName = strmanufacturerName + chr(per)
+ print("制造商名称 : %s" % strmanufacturerName)
+ # 获取设备版本
+ stdeviceversion = ""
+ for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chDeviceVersion:
+ stdeviceversion = stdeviceversion + chr(per)
+ print("设备当前使用固件版本 : %s" % stdeviceversion)
+ # 获取设备序列号
+ stSerialNumber = ""
+ for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chSerialNumber:
+ stSerialNumber = stSerialNumber + chr(per)
+ print("设备序列号 : %s" % stSerialNumber)
+ # 获取用户自定义名称
+ stUserDefinedName = ""
+ for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chUserDefinedName:
+ stUserDefinedName = stUserDefinedName + chr(per)
+ print("用户自定义名称 : %s" % stUserDefinedName)
+ # 获取设备 GUID
+ stDeviceGUID = ""
+ for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chDeviceGUID:
+ stDeviceGUID = stDeviceGUID + chr(per)
+ print("设备GUID号 : %s" % stDeviceGUID)
+ # 获取设备的家族名称
+ stFamilyName = ""
+ for per in mvcc_dev_info.SpecialInfo.stUsb3VInfo.chFamilyName:
+ stFamilyName = stFamilyName + chr(per)
+ print("设备的家族名称 : %s" % stFamilyName)
+
+ # 判断是否为 1394-a/b 设备
+ elif mvcc_dev_info.nTLayerType == MV_1394_DEVICE:
+ print("\n1394-a/b device: [%d]" % i)
+
+ # 判断是否为 cameralink 设备
+ elif mvcc_dev_info.nTLayerType == MV_CAMERALINK_DEVICE:
+ print("\ncameralink device: [%d]" % i)
+ # 获取当前设备名
+ strModeName = ""
+ for per in mvcc_dev_info.SpecialInfo.stCamLInfo.chModelName:
+ if per == 0:
+ break
+ strModeName = strModeName + chr(per)
+ print("当前设备型号名 : %s" % strModeName)
+ # 获取当前设备序列号
+ strSerialNumber = ""
+ for per in mvcc_dev_info.SpecialInfo.stCamLInfo.chSerialNumber:
+ if per == 0:
+ break
+ strSerialNumber = strSerialNumber + chr(per)
+ print("当前设备序列号 : %s" % strSerialNumber)
+ # 获取制造商名称
+ strmanufacturerName = ""
+ for per in mvcc_dev_info.SpecialInfo.stCamLInfo.chVendorName:
+ strmanufacturerName = strmanufacturerName + chr(per)
+ print("制造商名称 : %s" % strmanufacturerName)
+ # 获取设备版本
+ stdeviceversion = ""
+ for per in mvcc_dev_info.SpecialInfo.stCamLInfo.chDeviceVersion:
+ stdeviceversion = stdeviceversion + chr(per)
+ print("设备当前使用固件版本 : %s" % stdeviceversion)
+
+# 输入需要连接的相机的序号
+def input_num_camera(deviceList):
+ nConnectionNum = input("please input the number of the device to connect:")
+ if int(nConnectionNum) >= deviceList.nDeviceNum:
+ print("intput error!")
+ sys.exit()
+ return nConnectionNum
+
+# 创建相机实例并创建句柄,(设置日志路径)
+def creat_camera(deviceList , nConnectionNum ,log = True , log_path = getcwd()):
+ """
+
+ :param deviceList: 设备列表
+ :param nConnectionNum: 需要连接的设备序号
+ :param log: 是否创建日志
+ :param log_path: 日志保存路径
+ :return: 相机实例和设备列表
+ """
+ # 创建相机实例
+ cam = MvCamera()
+ # 选择设备并创建句柄
+ stDeviceList = cast(deviceList.pDeviceInfo[int(nConnectionNum)], POINTER(MV_CC_DEVICE_INFO)).contents
+ if log == True:
+ ret = cam.MV_CC_SetSDKLogPath(log_path)
+ print(log_path)
+ if ret != 0:
+ print("set Log path fail! ret[0x%x]" % ret)
+ sys.exit()
+ # 创建句柄,生成日志
+ ret = cam.MV_CC_CreateHandle(stDeviceList)
+ if ret != 0:
+ print("create handle fail! ret[0x%x]" % ret)
+ sys.exit()
+ elif log == False:
+ # 创建句柄,不生成日志
+ ret = cam.MV_CC_CreateHandleWithoutLog(stDeviceList)
+ print(1111)
+ if ret != 0:
+ print("create handle fail! ret[0x%x]" % ret)
+ sys.exit()
+ return cam , stDeviceList
+
+# 打开设备
+def open_device(cam):
+ # ch:打开设备 | en:Open device
+ ret = cam.MV_CC_OpenDevice(MV_ACCESS_Exclusive, 0)
+ if ret != 0:
+ print("open device fail! ret[0x%x]" % ret)
+ sys.exit()
+
+# 获取各种类型节点参数
+def get_Value(cam , param_type = "int_value" , node_name = "PayloadSize"):
+ """
+ :param cam: 相机实例
+ :param_type: 获取节点值得类型
+ :param node_name: 节点名 可选 int 、float 、enum 、bool 、string 型节点
+ :return: 节点值
+ """
+ if param_type == "int_value":
+ stParam = MVCC_INTVALUE_EX()
+ memset(byref(stParam), 0, sizeof(MVCC_INTVALUE_EX))
+ ret = cam.MV_CC_GetIntValueEx(node_name, stParam)
+ if ret != 0:
+ print("获取 int 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ int_value = stParam.nCurValue
+ return int_value
+
+ elif param_type == "float_value":
+ stFloatValue = MVCC_FLOATVALUE()
+ memset(byref(stFloatValue), 0, sizeof(MVCC_FLOATVALUE))
+ ret = cam.MV_CC_GetFloatValue( node_name , stFloatValue)
+ if ret != 0:
+ print("获取 float 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ float_value = stFloatValue.fCurValue
+ return float_value
+
+ elif param_type == "enum_value":
+ stEnumValue = MVCC_ENUMVALUE()
+ memset(byref(stEnumValue), 0, sizeof(MVCC_ENUMVALUE))
+ ret = cam.MV_CC_GetEnumValue(node_name, stEnumValue)
+ if ret != 0:
+ print("获取 enum 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ enum_value = stEnumValue.nCurValue
+ return enum_value
+
+ elif param_type == "bool_value":
+ stBool = c_bool(False)
+ ret = cam.MV_CC_GetBoolValue(node_name, stBool)
+ if ret != 0:
+ print("获取 bool 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ return stBool.value
+
+ elif param_type == "string_value":
+ stStringValue = MVCC_STRINGVALUE()
+ memset(byref(stStringValue), 0, sizeof( MVCC_STRINGVALUE))
+ ret = cam.MV_CC_GetStringValue(node_name, stStringValue)
+ if ret != 0:
+ print("获取 string 型数据 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ string_value = stStringValue.chCurValue
+ return string_value
+
+# 设置各种类型节点参数
+def set_Value(cam , param_type = "int_value" , node_name = "PayloadSize" , node_value = None):
+ """
+ :param cam: 相机实例
+ :param param_type: 需要设置的节点值得类型
+ int:
+ float:
+ enum: 参考于客户端中该选项的 Enum Entry Value 值即可
+ bool: 对应 0 为关,1 为开
+ string: 输入值为数字或者英文字符,不能为汉字
+ :param node_name: 需要设置的节点名
+ :param node_value: 设置给节点的值
+ :return:
+ """
+ if param_type == "int_value":
+ stParam = int(node_value)
+ ret = cam.MV_CC_SetIntValueEx(node_name, stParam)
+ if ret != 0:
+ print("设置 int 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ print("设置 int 型数据节点 %s 成功 !设置值为 %s !"%(node_name , node_value))
+
+ elif param_type == "float_value":
+ stFloatValue = float(node_value)
+ ret = cam.MV_CC_SetFloatValue( node_name , stFloatValue)
+ if ret != 0:
+ print("设置 float 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ print("设置 float 型数据节点 %s 成功 !设置值为 %s !" % (node_name, node_value))
+
+ elif param_type == "enum_value":
+ stEnumValue = node_value
+ ret = cam.MV_CC_SetEnumValue(node_name, stEnumValue)
+ if ret != 0:
+ print("设置 enum 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ print("设置 enum 型数据节点 %s 成功 !设置值为 %s !" % (node_name, node_value))
+
+ elif param_type == "bool_value":
+ ret = cam.MV_CC_SetBoolValue(node_name, node_value)
+ if ret != 0:
+ print("设置 bool 型数据节点 %s 失败 ! 报错码 ret[0x%x]" %(node_name,ret))
+ sys.exit()
+ print("设置 bool 型数据节点 %s 成功 !设置值为 %s !" % (node_name, node_value))
+
+ elif param_type == "string_value":
+ stStringValue = str(node_value)
+ ret = cam.MV_CC_SetStringValue(node_name, stStringValue)
+ if ret != 0:
+ print("设置 string 型数据节点 %s 失败 ! 报错码 ret[0x%x]" % (node_name , ret))
+ sys.exit()
+ print("设置 string 型数据节点 %s 成功 !设置值为 %s !" % (node_name, node_value))
+
+# 寄存器读写
+def read_or_write_memory(cam , way = "read"):
+ if way == "read":
+ pass
+ cam.MV_CC_ReadMemory()
+ elif way == "write":
+ pass
+ cam.MV_CC_WriteMemory()
+
+# 判断相机是否处于连接状态(返回值如何获取)=================================
+def decide_divice_on_line(cam):
+ value = cam.MV_CC_IsDeviceConnected()
+ if value == True:
+ print("该设备在线 !")
+ else:
+ print("该设备已掉线 !", value)
+
+# 设置 SDK 内部图像缓存节点个数
+def set_image_Node_num(cam , Num = 1):
+ ret = cam.MV_CC_SetImageNodeNum(nNum = Num)
+ if ret != 0:
+ print("设置 SDK 内部图像缓存节点个数失败 ,报错码 ret[0x%x]" % ret)
+ else:
+ print("设置 SDK 内部图像缓存节点个数为 %d ,设置成功!" % Num)
+
+# 设置取流策略
+def set_grab_strategy(cam , grabstrategy = 0 , outputqueuesize = 1):
+ """
+ • OneByOne: 从旧到新一帧一帧的从输出缓存列表中获取图像,打开设备后默认为该策略
+ • LatestImagesOnly: 仅从输出缓存列表中获取最新的一帧图像,同时清空输出缓存列表
+ • LatestImages: 从输出缓存列表中获取最新的OutputQueueSize帧图像,其中OutputQueueSize范围为1 - ImageNodeNum,可用MV_CC_SetOutputQueueSize()接口设置,ImageNodeNum默认为1,可用MV_CC_SetImageNodeNum()接口设置OutputQueueSize设置成1等同于LatestImagesOnly策略,OutputQueueSize设置成ImageNodeNum等同于OneByOne策略
+ • UpcomingImage: 在调用取流接口时忽略输出缓存列表中所有图像,并等待设备即将生成的一帧图像。该策略只支持GigE设备,不支持U3V设备
+ """
+ if grabstrategy != 2:
+ ret = cam.MV_CC_SetGrabStrategy(enGrabStrategy = grabstrategy)
+ if ret != 0:
+ print("设置取流策略失败 ,报错码 ret[0x%x]" % ret)
+ else:
+ print("设置 取流策略为 %d ,设置成功!" % grabstrategy)
+ else:
+ ret = cam.MV_CC_SetGrabStrategy(enGrabStrategy=grabstrategy)
+ if ret != 0:
+ print("设置取流策略失败 ,报错码 ret[0x%x]" % ret)
+ else:
+ print("设置 取流策略为 %d ,设置成功!" % grabstrategy)
+
+ ret = cam.MV_CC_SetOutputQueueSize(nOutputQueueSize = outputqueuesize)
+ if ret != 0:
+ print("设置使出缓存个数失败 ,报错码 ret[0x%x]" % ret)
+ else:
+ print("设置 输出缓存个数为 %d ,设置成功!" % outputqueuesize)
+
+# 显示图像
+def image_show(image , name):
+ image = cv2.resize(image, (600, 400), interpolation=cv2.INTER_AREA)
+ name = str(name)
+ cv2.imshow(name, image)
+ cv2.imwrite("name.bmp", image)
+ k = cv2.waitKey(1) & 0xff
+
+# 需要显示的图像数据转换
+def image_control(data , stFrameInfo):
+ if stFrameInfo.enPixelType == 17301505:
+ image = data.reshape((stFrameInfo.nHeight, stFrameInfo.nWidth))
+ image_show(image=image , name = stFrameInfo.nHeight)
+ elif stFrameInfo.enPixelType == 17301514:
+ data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
+ image = cv2.cvtColor(data, cv2.COLOR_BAYER_GB2RGB)
+ image_show(image=image, name = stFrameInfo.nHeight)
+ elif stFrameInfo.enPixelType == 35127316:
+ data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
+ image = cv2.cvtColor(data, cv2.COLOR_RGB2BGR)
+ image_show(image=image, name = stFrameInfo.nHeight)
+ elif stFrameInfo.enPixelType == 34603039:
+ data = data.reshape(stFrameInfo.nHeight, stFrameInfo.nWidth, -1)
+ image = cv2.cvtColor(data, cv2.COLOR_YUV2BGR_Y422)
+ image_show(image = image, name = stFrameInfo.nHeight)
+
+# 主动图像采集
+def access_get_image(cam , active_way = "getImagebuffer"):
+ """
+ :param cam: 相机实例
+ :active_way:主动取流方式的不同方法 分别是(getImagebuffer)(getoneframetimeout)
+ :return:
+ """
+ if active_way == "getImagebuffer":
+ stOutFrame = MV_FRAME_OUT()
+ memset(byref(stOutFrame), 0, sizeof(stOutFrame))
+ while True:
+ ret = cam.MV_CC_GetImageBuffer(stOutFrame, 1000)
+ if None != stOutFrame.pBufAddr and 0 == ret and stOutFrame.stFrameInfo.enPixelType == 17301505:
+ print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
+ pData = (c_ubyte * stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight)()
+ cdll.msvcrt.memcpy(byref(pData), stOutFrame.pBufAddr,stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight)
+ data = np.frombuffer(pData, count=int(stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight),dtype=np.uint8)
+ image_control(data=data, stFrameInfo=stOutFrame.stFrameInfo)
+ elif None != stOutFrame.pBufAddr and 0 == ret and stOutFrame.stFrameInfo.enPixelType == 17301514:
+ print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
+ pData = (c_ubyte * stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight)()
+ cdll.msvcrt.memcpy(byref(pData), stOutFrame.pBufAddr,stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight)
+ data = np.frombuffer(pData, count=int(stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight),dtype=np.uint8)
+ image_control(data=data, stFrameInfo=stOutFrame.stFrameInfo)
+ elif None != stOutFrame.pBufAddr and 0 == ret and stOutFrame.stFrameInfo.enPixelType == 35127316:
+ print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
+ pData = (c_ubyte * stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight*3)()
+ cdll.msvcrt.memcpy(byref(pData), stOutFrame.pBufAddr,stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight*3)
+ data = np.frombuffer(pData, count=int(stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight*3),dtype=np.uint8)
+ image_control(data=data, stFrameInfo=stOutFrame.stFrameInfo)
+ elif None != stOutFrame.pBufAddr and 0 == ret and stOutFrame.stFrameInfo.enPixelType == 34603039:
+ print("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stOutFrame.stFrameInfo.nWidth, stOutFrame.stFrameInfo.nHeight, stOutFrame.stFrameInfo.nFrameNum))
+ pData = (c_ubyte * stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 2)()
+ cdll.msvcrt.memcpy(byref(pData), stOutFrame.pBufAddr,stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 2)
+ data = np.frombuffer(pData, count=int(stOutFrame.stFrameInfo.nWidth * stOutFrame.stFrameInfo.nHeight * 2),dtype=np.uint8)
+ image_control(data=data, stFrameInfo=stOutFrame.stFrameInfo)
+ else:
+ print("no data[0x%x]" % ret)
+ nRet = cam.MV_CC_FreeImageBuffer(stOutFrame)
+
+ elif active_way == "getoneframetimeout":
+ stParam = MVCC_INTVALUE_EX()
+ memset(byref(stParam), 0, sizeof(MVCC_INTVALUE_EX))
+ ret = cam.MV_CC_GetIntValueEx("PayloadSize", stParam)
+ if ret != 0:
+ print("get payload size fail! ret[0x%x]" % ret)
+ sys.exit()
+ nDataSize = stParam.nCurValue
+ pData = (c_ubyte * nDataSize)()
+ stFrameInfo = MV_FRAME_OUT_INFO_EX()
+ memset(byref(stFrameInfo), 0, sizeof(stFrameInfo))
+ while True:
+ ret = cam.MV_CC_GetOneFrameTimeout(pData, nDataSize, stFrameInfo, 1000)
+ if ret == 0:
+ print("get one frame: Width[%d], Height[%d], nFrameNum[%d] " % (stFrameInfo.nWidth, stFrameInfo.nHeight, stFrameInfo.nFrameNum))
+ image = np.asarray(pData)
+ image_control(data=image, stFrameInfo=stFrameInfo)
+ else:
+ print("no data[0x%x]" % ret)
+
+# 回调取图采集
+winfun_ctype = WINFUNCTYPE
+stFrameInfo = POINTER(MV_FRAME_OUT_INFO_EX)
+pData = POINTER(c_ubyte)
+FrameInfoCallBack = winfun_ctype(None, pData, stFrameInfo, c_void_p)
+def image_callback(pData, pFrameInfo, pUser):
+ global img_buff
+ img_buff = None
+ stFrameInfo = cast(pFrameInfo, POINTER(MV_FRAME_OUT_INFO_EX)).contents
+ if stFrameInfo:
+ print ("get one frame: Width[%d], Height[%d], nFrameNum[%d]" % (stFrameInfo.nWidth, stFrameInfo.nHeight, stFrameInfo.nFrameNum))
+ if img_buff is None and stFrameInfo.enPixelType == 17301505:
+ img_buff = (c_ubyte * stFrameInfo.nWidth*stFrameInfo.nHeight)()
+ cdll.msvcrt.memcpy(byref(img_buff) , pData , stFrameInfo.nWidth*stFrameInfo.nHeight)
+ data = np.frombuffer(img_buff , count = int(stFrameInfo.nWidth*stFrameInfo.nHeight) , dtype = np.uint8)
+ image_control(data=data, stFrameInfo=stFrameInfo)
+ del img_buff
+ elif img_buff is None and stFrameInfo.enPixelType == 17301514:
+ img_buff = (c_ubyte * stFrameInfo.nWidth*stFrameInfo.nHeight)()
+ cdll.msvcrt.memcpy(byref(img_buff) , pData , stFrameInfo.nWidth*stFrameInfo.nHeight)
+ data = np.frombuffer(img_buff , count = int(stFrameInfo.nWidth*stFrameInfo.nHeight) , dtype = np.uint8)
+ image_control(data=data, stFrameInfo=stFrameInfo)
+ del img_buff
+ elif img_buff is None and stFrameInfo.enPixelType == 35127316:
+ img_buff = (c_ubyte * stFrameInfo.nWidth * stFrameInfo.nHeight*3)()
+ cdll.msvcrt.memcpy(byref(img_buff), pData, stFrameInfo.nWidth * stFrameInfo.nHeight*3)
+ data = np.frombuffer(img_buff, count=int(stFrameInfo.nWidth * stFrameInfo.nHeight*3), dtype=np.uint8)
+ image_control(data=data, stFrameInfo=stFrameInfo)
+ del img_buff
+ elif img_buff is None and stFrameInfo.enPixelType == 34603039:
+ img_buff = (c_ubyte * stFrameInfo.nWidth * stFrameInfo.nHeight * 2)()
+ cdll.msvcrt.memcpy(byref(img_buff), pData, stFrameInfo.nWidth * stFrameInfo.nHeight * 2)
+ data = np.frombuffer(img_buff, count=int(stFrameInfo.nWidth * stFrameInfo.nHeight * 2), dtype=np.uint8)
+ image_control(data=data, stFrameInfo=stFrameInfo)
+ del img_buff
+CALL_BACK_FUN = FrameInfoCallBack(image_callback)
+
+# 事件回调
+stEventInfo = POINTER(MV_EVENT_OUT_INFO)
+pData = POINTER(c_ubyte)
+EventInfoCallBack = winfun_ctype(None, stEventInfo, c_void_p)
+def event_callback(pEventInfo, pUser):
+ stPEventInfo = cast(pEventInfo, POINTER(MV_EVENT_OUT_INFO)).contents
+ nBlockId = stPEventInfo.nBlockIdHigh
+ nBlockId = (nBlockId << 32) + stPEventInfo.nBlockIdLow
+ nTimestamp = stPEventInfo.nTimestampHigh
+ nTimestamp = (nTimestamp << 32) + stPEventInfo.nTimestampLow
+ if stPEventInfo:
+ print ("EventName[%s], EventId[%u], BlockId[%d], Timestamp[%d]" % (stPEventInfo.EventName, stPEventInfo.nEventID, nBlockId, nTimestamp))
+CALL_BACK_FUN_2 = EventInfoCallBack(event_callback)
+
+# 注册回调取图
+def call_back_get_image(cam):
+ # ch:注册抓图回调 | en:Register image callback
+ ret = cam.MV_CC_RegisterImageCallBackEx(CALL_BACK_FUN, None)
+ if ret != 0:
+ print("register image callback fail! ret[0x%x]" % ret)
+ sys.exit()
+
+# 关闭设备与销毁句柄
+def close_and_destroy_device(cam , data_buf=None):
+ # 停止取流
+ ret = cam.MV_CC_StopGrabbing()
+ if ret != 0:
+ print("stop grabbing fail! ret[0x%x]" % ret)
+ sys.exit()
+ # 关闭设备
+ ret = cam.MV_CC_CloseDevice()
+ if ret != 0:
+ print("close deivce fail! ret[0x%x]" % ret)
+ del data_buf
+ sys.exit()
+ # 销毁句柄
+ ret = cam.MV_CC_DestroyHandle()
+ if ret != 0:
+ print("destroy handle fail! ret[0x%x]" % ret)
+ del data_buf
+ sys.exit()
+ del data_buf
+
+# 开启取流并获取数据包大小
+def start_grab_and_get_data_size(cam):
+ ret = cam.MV_CC_StartGrabbing()
+ if ret != 0:
+ print("开始取流失败! ret[0x%x]" % ret)
+ sys.exit()
+
+def main():
+ # 枚举设备
+ deviceList = enum_devices(device=0, device_way=False)
+ # 判断不同类型设备
+ identify_different_devices(deviceList)
+ # 输入需要被连接的设备
+ nConnectionNum = input_num_camera(deviceList)
+ # 创建相机实例并创建句柄,(设置日志路径)
+ cam, stDeviceList = creat_camera(deviceList, nConnectionNum, log=False)
+ # decide_divice_on_line(cam) ==============
+ # 打开设备
+ open_device(cam)
+ # # 设置缓存节点个数
+ # set_image_Node_num(cam, Num=10)
+ # # 设置取流策略
+ # set_grab_strategy(cam, grabstrategy=2, outputqueuesize=10)
+ # 设置设备的一些参数
+ # set_Value(cam, param_type="bool_value", node_name="TriggerCacheEnable", node_value=1)
+ # 获取设备的一些参数
+ # get_value = get_Value(cam , param_type = "int_value" , node_name = "PayloadSize")
+
+ stdcall = input("回调方式取流显示请输入 0 主动取流方式显示请输入 1:")
+ if int(stdcall) == 0:
+ # 回调方式抓取图像
+ call_back_get_image(cam)
+ # 开启设备取流
+ start_grab_and_get_data_size(cam)
+ # 当使用 回调取流时,需要在此处添加
+ print ("press a key to stop grabbing.")
+ msvcrt.getch()
+ # 关闭设备与销毁句柄
+ close_and_destroy_device(cam)
+ elif int(stdcall) == 1:
+ # 开启设备取流
+ start_grab_and_get_data_size(cam)
+ # 主动取流方式抓取图像
+ access_get_image(cam, active_way="getImagebuffer")
+ # 关闭设备与销毁句柄
+ close_and_destroy_device(cam)
+
+if __name__=="__main__":
+ main()
\ No newline at end of file
diff --git a/rknn-multi-threaded-nosigmoid/.gitignore b/rknn-multi-threaded-nosigmoid/.gitignore
new file mode 100644
index 0000000..deea617
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/.gitignore
@@ -0,0 +1,162 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+#Pipfile.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+#poetry.lock
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+#pdm.lock
+# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
+# in version control.
+# https://pdm.fming.dev/#use-with-ide
+.pdm.toml
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+*.mp4
\ No newline at end of file
diff --git a/rknn-multi-threaded-nosigmoid/LICENSE b/rknn-multi-threaded-nosigmoid/LICENSE
new file mode 100644
index 0000000..261eeb9
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/rknn-multi-threaded-nosigmoid/README.md b/rknn-multi-threaded-nosigmoid/README.md
new file mode 100644
index 0000000..43d012f
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/README.md
@@ -0,0 +1,36 @@
+# 简介
+* 使用多线程异步操作rknn模型, 提高rk3588/rk3588s的NPU使用率, 进而提高推理帧数(rk3568之类修改后应该也能使用, 但是作者本人并没有rk3568开发板......)
+* 此分支使用模型[yolov5s_relu_tk2_RK3588_i8.rknn](https://github.com/airockchip/rknn_model_zoo), 将yolov5s模型的激活函数silu修改为为relu,在损失一点精度的情况下获得较大性能提升,详情见于[rknn_model_zoo](https://github.com/airockchip/rknn_model_zoo/tree/main/models/CV/object_detection/yolo)
+* 此项目的[c++](https://github.com/leafqycc/rknn-cpp-Multithreading)实现
+
+# 更新说明
+* 无
+
+
+# 使用说明
+### 演示
+ * 将仓库拉取至本地, 并将Releases中的演示视频放于项目根目录下, 运行main.py查看演示示例
+ * 切换至root用户运行performance.sh可以进行定频操作(约等于开启性能模式)
+ * 运行rkcat.sh可以查看当前温度与NPU占用
+### 部署应用
+ * 修改main.py下的modelPath为你自己的模型所在路径
+ * 修改main.py下的cap为你想要运行的视频/摄像头
+ * 修改main.py下的TPEs为你想要的线程数, 具体可参考下表
+ * 修改func.py为你自己需要的推理函数, 具体可查看myFunc函数
+
+# 多线程模型帧率测试
+* 使用performance.sh进行CPU/NPU定频尽量减少误差
+* 测试模型为[yolov5s_relu_tk2_RK3588_i8.rknn](https://github.com/airockchip/rknn_model_zoo)
+* 测试视频见于Releases
+
+| 模型\线程数 | 1 | 2 | 3 | 4 | 5 | 6 |
+| ---- | ---- | ---- | ---- | ---- | ---- | ---- |
+| yolov5s | 27.4491 | 49.0747 | 65.3673 | 63.3204 | 71.8407 | 72.0590 |
+
+# 补充
+* 多线程下CPU, NPU占用较高, **核心温度相应增高**, 请做好散热。推荐开1, 2, 3线程, 实测小铜片散热下运行三分钟温度约为56°, 64°, 69°
+
+# Acknowledgements
+* https://github.com/ultralytics/yolov5
+* https://github.com/rockchip-linux/rknn-toolkit2
+* https://github.com/airockchip/rknn_model_zoo
\ No newline at end of file
diff --git a/rknn-multi-threaded-nosigmoid/func.py b/rknn-multi-threaded-nosigmoid/func.py
new file mode 100644
index 0000000..99aca71
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/func.py
@@ -0,0 +1,246 @@
+# 下面代码基于你给出的 yolov5 示例做最小修改的适配版
+import cv2
+import numpy as np
+
+OBJ_THRESH, NMS_THRESH, IMG_SIZE = 0.25, 0.45, 640
+
+CLASSES = ("person", "bicycle", "car", "motorbike ", "aeroplane ", "bus ", "train", "truck ", "boat", "traffic light",
+ "fire hydrant", "stop sign ", "parking meter", "bench", "bird", "cat", "dog ", "horse ", "sheep", "cow", "elephant",
+ "bear", "zebra ", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite",
+ "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife ",
+ "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza ", "donut", "cake", "chair", "sofa",
+ "pottedplant", "bed", "diningtable", "toilet ", "tvmonitor", "laptop\t", "mouse\t", "remote ", "keyboard ", "cell phone", "microwave ",
+ "oven ", "toaster", "sink", "refrigerator ", "book", "clock", "vase", "scissors ", "teddy bear ", "hair drier", "toothbrush ")
+
+# ---------- 保留你原来的辅助函数(process/filter/nms/xywh2xyxy) ----------
+def xywh2xyxy(x):
+ y = np.copy(x)
+ y[:, 0] = x[:, 0] - x[:, 2] / 2
+ y[:, 1] = x[:, 1] - x[:, 3] / 2
+ y[:, 2] = x[:, 0] + x[:, 2] / 2
+ y[:, 3] = x[:, 1] + x[:, 3] / 2
+ return y
+
+
+def process(input, mask, anchors):
+ # input: (grid_h, grid_w, 3, attrs) attrs >=5+num_classes
+ # anchors: list of (w,h) pairs for the 3 anchors
+ anchors = [anchors[i] for i in mask]
+ grid_h, grid_w = map(int, input.shape[0:2])
+
+ box_confidence = input[..., 4]
+ box_confidence = np.expand_dims(box_confidence, axis=-1)
+
+ box_class_probs = input[..., 5:]
+
+ # YOLO11 style decode used originally in your code:
+ box_xy = input[..., :2] * 2 - 0.5
+
+ # build grid
+ col = np.tile(np.arange(0, grid_w), grid_w).reshape(-1, grid_w)
+ row = np.tile(np.arange(0, grid_h).reshape(-1, 1), grid_h)
+ col = col.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
+ row = row.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
+ grid = np.concatenate((col, row), axis=-1)
+ box_xy += grid
+ box_xy *= int(IMG_SIZE / grid_h)
+
+ box_wh = pow(input[..., 2:4] * 2, 2)
+ # multiply by provided anchors (we will use unit anchors if we want to neutralize)
+ box_wh = box_wh * anchors
+
+ return np.concatenate((box_xy, box_wh), axis=-1), box_confidence, box_class_probs
+
+
+def filter_boxes(boxes, box_confidences, box_class_probs):
+ boxes = boxes.reshape(-1, 4)
+ box_confidences = box_confidences.reshape(-1)
+ box_class_probs = box_class_probs.reshape(-1, box_class_probs.shape[-1])
+
+ _box_pos = np.where(box_confidences >= OBJ_THRESH)
+ boxes = boxes[_box_pos]
+ box_confidences = box_confidences[_box_pos]
+ box_class_probs = box_class_probs[_box_pos]
+
+ class_max_score = np.max(box_class_probs, axis=-1)
+ classes = np.argmax(box_class_probs, axis=-1)
+ _class_pos = np.where(class_max_score >= OBJ_THRESH)
+
+ return boxes[_class_pos], classes[_class_pos], (class_max_score * box_confidences)[_class_pos]
+
+
+def nms_boxes(boxes, scores):
+ x = boxes[:, 0]
+ y = boxes[:, 1]
+ w = boxes[:, 2] - boxes[:, 0]
+ h = boxes[:, 3] - boxes[:, 1]
+
+ areas = w * h
+ order = scores.argsort()[::-1]
+
+ keep = []
+ eps = 1e-7
+ while order.size > 0:
+ i = order[0]
+ keep.append(i)
+
+ xx1 = np.maximum(x[i], x[order[1:]])
+ yy1 = np.maximum(y[i], y[order[1:]])
+ xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]])
+ yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]])
+
+ w1 = np.maximum(0.0, xx2 - xx1 + 1e-5)
+ h1 = np.maximum(0.0, yy2 - yy1 + 1e-5)
+ inter = w1 * h1
+
+ denom = (areas[i] + areas[order[1:]] - inter)
+ denom = np.maximum(denom, eps)
+ ovr = inter / denom
+ inds = np.where(ovr <= NMS_THRESH)[0]
+ order = order[inds + 1]
+ return np.array(keep)
+
+
+# ---------- 关键:把你的 9 输出拼成原来 yolov5_post_process 需要的 input_data 格式 ----------
+def yolov11_to_yolov5_style_input(outputs):
+ """
+ outputs: list of 9 tensors (1, C, H, W) in this order per your print:
+ [reg80, cls80, obj80, reg40, cls40, obj40, reg20, cls20, obj20]
+ We will convert each scale to a (H, W, 3, 5+num_classes) array and repeat
+ the same per-anchor slice so that your existing yolov5_post_process can be reused.
+ To avoid anchor scaling changing box_wh, we set anchors to 1x1 in later call.
+ """
+ input_data = []
+ # scales: (indices and corresponding H,W from tensors)
+ for i in range(0, 9, 3):
+ reg = outputs[i][0] # (64, H, W)
+ cls = outputs[i+1][0] # (80, H, W)
+ obj = outputs[i+2][0] # (1, H, W)
+
+ # find H,W from reg
+ H = reg.shape[1]
+ W = reg.shape[2]
+
+ # xywh: assume first 4 channels of reg are x,y,w,h per cell
+ xywh = reg[0:4, :, :] # (4, H, W)
+ xywh = np.transpose(xywh, (1, 2, 0)) # (H, W, 4)
+
+ # obj and cls to H,W,?
+ obj_hw = np.transpose(obj[0, :, :], (0, 1)) # (H, W)
+ cls_hw = np.transpose(cls, (1, 2, 0)) # (H, W, 80)
+
+ # build one anchor slice: [x,y,w,h,obj, cls80] -> shape (H, W, 5+80)
+ slice_hw = np.concatenate([xywh, obj_hw[..., None], cls_hw], axis=-1) # (H, W, 85)
+
+ # repeat to make 3 anchors per cell (so shape becomes H,W,3,85)
+ slice_3 = np.repeat(slice_hw[:, :, None, :], 3, axis=2) # (H, W, 3, 85)
+
+ input_data.append(slice_3)
+
+ return input_data
+
+
+def yolov5_post_process_adapted(input_data):
+ """
+ 复用你原来的 yolov5_post_process,但使用 unit anchors so that
+ box_wh 不会被不正确放缩。
+ """
+ masks = [[0, 1, 2], [3, 4, 5], [6, 7, 8]] # unused in anchors here, but kept for compatibility
+ # use neutral anchors (1,1) to avoid scaling change
+ anchors = [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1],
+ [1, 1], [1, 1], [1, 1], [1, 1]]
+
+ boxes, classes, scores = [], [], []
+ # input_data already is list of 3 arrays shaped (H,W,3,85)
+ for input in input_data:
+ # process() expects shape (grid_h, grid_w, 3, attrs)
+ b, c, s = process(input, [0,1,2], anchors) # mask and anchors values used inside process
+ b, c, s = filter_boxes(b, c, s)
+ boxes.append(b)
+ classes.append(c)
+ scores.append(s)
+
+ if len(boxes) == 0:
+ return None, None, None
+
+ boxes = np.concatenate(boxes)
+ boxes = xywh2xyxy(boxes)
+ classes = np.concatenate(classes)
+ scores = np.concatenate(scores)
+
+ # nms per class
+ nboxes, nclasses, nscores = [], [], []
+ for cls_id in set(classes):
+ inds = np.where(classes == cls_id)
+ b = boxes[inds]
+ c = classes[inds]
+ s = scores[inds]
+
+ keep = nms_boxes(b, s)
+
+ nboxes.append(b[keep])
+ nclasses.append(c[keep])
+ nscores.append(s[keep])
+
+ if not nclasses and not nscores:
+ return None, None, None
+
+ return np.concatenate(nboxes), np.concatenate(nclasses), np.concatenate(nscores)
+
+
+# ---------- draw 保持原样 ----------
+def draw(image, boxes, scores, classes):
+ for box, score, cl in zip(boxes, scores, classes):
+ top, left, right, bottom = box
+ top = int(top)
+ left = int(left)
+ cv2.rectangle(image, (top, left), (int(right), int(bottom)), (255, 0, 0), 2)
+ cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score),
+ (top, left - 6),
+ cv2.FONT_HERSHEY_SIMPLEX,
+ 0.6, (0, 0, 255), 2)
+
+
+# ---------- 最终 myFunc(替换你原来的 myFunc) ----------
+def myFunc(rknn_lite, IMG):
+ # 1. BGR -> RGB
+ IMG = cv2.cvtColor(IMG, cv2.COLOR_BGR2RGB)
+
+ # 2. Resize to model input size (640x640)
+ IMG = cv2.resize(IMG, (IMG_SIZE, IMG_SIZE)) # (640, 640, 3)
+
+ # 3. HWC -> CHW
+ IMG = np.transpose(IMG, (2, 0, 1)) # (3, 640, 640)
+
+ # 4. Add batch dimension: (1, 3, 640, 640)
+ IMG_in = np.expand_dims(IMG, axis=0).astype(np.uint8)
+
+ # 5. Inference
+ outputs = rknn_lite.inference(inputs=[IMG_in])
+
+ if outputs is None:
+ print("⚠️ Inference failed, skipping frame.")
+ return cv2.cvtColor(IMG_in.squeeze().transpose(1, 2, 0), cv2.COLOR_RGB2BGR)
+
+ # 6. Convert 9-output -> yolov5 style input_data
+ input_data = yolov11_to_yolov5_style_input(outputs)
+
+ # 7. Run adapted yolov5_post_process
+ boxes, classes, scores = yolov5_post_process_adapted(input_data)
+
+ # 8. 如果你只想保留 car(COCO id=2),在这里再过滤一次
+ if boxes is not None:
+ keep_car = np.where(classes == 2)[0]
+ if keep_car.size == 0:
+ boxes, classes, scores = None, None, None
+ else:
+ boxes = boxes[keep_car]
+ classes = classes[keep_car]
+ scores = scores[keep_car]
+
+ # Convert back to BGR for OpenCV display
+ IMG_vis = cv2.cvtColor(IMG_in.squeeze().transpose(1, 2, 0), cv2.COLOR_RGB2BGR)
+ if boxes is not None:
+ draw(IMG_vis, boxes, scores, classes)
+ return IMG_vis
+
diff --git a/rknn-multi-threaded-nosigmoid/func_cls.py b/rknn-multi-threaded-nosigmoid/func_cls.py
new file mode 100644
index 0000000..eb4e413
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/func_cls.py
@@ -0,0 +1,70 @@
+import cv2
+import numpy as np
+
+# ------------------ 手动定义分类标签 ------------------
+labels = ["noready", "ready"] # 0: 盖板不到位, 1: 盖板到位
+
+# ------------------ 配置 ------------------
+IMG_SIZE = (640, 640)
+
+
+def preprocess_cls(bgr_image_corrected, target_size=IMG_SIZE):
+ """
+ 对已经校正方向(翻转后)的 BGR 图像进行预处理:
+ - 转 RGB
+ - Resize
+ - 添加 batch 维度
+ 注意:此函数不再负责翻转!翻转应在调用前完成。
+ """
+ rgb = cv2.cvtColor(bgr_image_corrected, cv2.COLOR_BGR2RGB)
+ resized = cv2.resize(rgb, target_size)
+ input_tensor = np.expand_dims(resized, axis=0).astype(np.uint8)
+ return input_tensor
+
+
+def get_top1_class_str_from_output(outputs):
+ if outputs is None or len(outputs) == 0:
+ return "Error: No output"
+ logits = outputs[0].flatten()
+ top1_idx = int(np.argmax(logits))
+ if 0 <= top1_idx < len(labels):
+ return labels[top1_idx]
+ else:
+ return "Unknown"
+
+
+def myFunc(rknn_lite, IMG):
+ """
+ 统一推理接口(分类任务)
+ - 输入原始 BGR 图像(可能倒置)
+ - 内部先上下翻转(校正方向)
+ - 模型在翻转后的图像上推理
+ - 返回带结果的翻转后图像(即“正”的图像)
+ """
+ # 1. 上下翻转原始图像(校正摄像头倒装)
+ corrected_img = cv2.flip(IMG, 0) # 现在这是“正”的图像
+
+ # 2. 预处理(使用校正后的图像)
+ input_data = preprocess_cls(corrected_img, target_size=IMG_SIZE)
+
+ # 3. 推理
+ outputs = rknn_lite.inference(inputs=[input_data])
+
+ # 4. 解析结果
+ class_name = get_top1_class_str_from_output(outputs)
+
+ # 5. 在校正后的图像上绘制结果
+ vis_img = corrected_img.copy()
+ text = f"Class: {class_name}"
+ cv2.putText(
+ vis_img,
+ text,
+ (10, 30),
+ cv2.FONT_HERSHEY_SIMPLEX,
+ 1.0,
+ (0, 255, 0),
+ 2,
+ cv2.LINE_AA
+ )
+
+ return vis_img # 返回的是上下翻转后的 BGR 图像(即视觉上“正”的图)
diff --git a/rknn-multi-threaded-nosigmoid/func_cls5.py b/rknn-multi-threaded-nosigmoid/func_cls5.py
new file mode 100644
index 0000000..c55ae63
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/func_cls5.py
@@ -0,0 +1,442 @@
+import cv2
+import numpy as np
+
+# ---------------------------
+# 固化配置
+# ---------------------------
+CLASS_NAMES = {
+ 0: "0",
+ 1: "1",
+ 2: "2",
+ 3: "3",
+ 4: "4"
+}
+
+# FIXED_ROI 是基于原始分辨率图像(翻转后)的坐标
+# 比如基于 1920x1080
+FIXED_ROI = (445, 540, 931, 319)
+
+# 原始分辨率(ROI 对应的标注基准)
+ORIG_W = 1920
+ORIG_H = 1080
+
+# 当前输入图像尺寸(你说已经固定成 640x640)
+CUR_W = 640
+CUR_H = 640
+
+IMG_SIZE = 640
+RESIZE_MODE = "stretch"
+TO_RGB = True
+NORMALIZE = False
+LAYOUT = "NHWC"
+WEIGHT_THRESHOLD = 0.4
+W1, W2 = 0.3, 0.7
+
+
+# ---------------------------
+# 基本工具函数
+# ---------------------------
+def resize_stretch(image, size=IMG_SIZE):
+ return cv2.resize(image, (size, size))
+
+def preprocess_image_for_rknn(img, size, resize_mode, to_rgb, normalize, layout):
+ if resize_mode == "letterbox":
+ raise NotImplementedError
+ else:
+ img_box = resize_stretch(img, size=size)
+
+ if to_rgb:
+ img_box = cv2.cvtColor(img_box, cv2.COLOR_BGR2RGB)
+
+ img_f = img_box.astype(np.float32)
+ if normalize:
+ img_f /= 255.0
+
+ if layout == "NHWC":
+ out = np.expand_dims(img_f, axis=0)
+ else:
+ out = np.expand_dims(np.transpose(img_f, (2, 0, 1)), axis=0)
+
+ return out.astype(np.float32)
+
+def weighted_small_large(pred, threshold=WEIGHT_THRESHOLD, w1=W1, w2=W2):
+ p1, p2 = float(pred[1]), float(pred[2])
+ total = p1 + p2
+ score = (w1*p1 + w2*p2)/total if total > 0 else 0.0
+ return ("大堆料" if score >= threshold else "小堆料"), score, p1, p2
+
+
+# ---------------------------
+# ROI 缩放函数(核心)
+# ---------------------------
+def scale_roi_to_current(img, orig_roi, orig_w, orig_h):
+ """
+ 将基于原始分辨率的 ROI 坐标,缩放到当前尺寸的图像。
+ """
+ x, y, w, h = orig_roi
+
+ # 当前图像尺寸
+ cur_h, cur_w = img.shape[:2]
+
+ scale_x = cur_w / orig_w
+ scale_y = cur_h / orig_h
+
+ sx = int(x * scale_x)
+ sy = int(y * scale_y)
+ sw = int(w * scale_x)
+ sh = int(h * scale_y)
+
+ return sx, sy, sw, sh
+
+
+# ---------------------------
+# 主推理接口
+# ---------------------------
+def myFunc(rknn_lite, IMG):
+ if IMG is None or IMG.size == 0:
+ raise ValueError("输入图像无效")
+
+ # 1) 输入已经是 640x640,但 ROI 是 1920x1080 标注的
+ # 所以必须先缩放 ROI
+ scaled_roi = scale_roi_to_current(IMG, FIXED_ROI, ORIG_W, ORIG_H)
+
+ # 2) 翻转图像(你说 ROI 是基于“翻转后的原图”)
+ flipped_IMG = cv2.flip(IMG, 0)
+
+ # 3) 在翻转后的图像上裁剪缩放好的 ROI
+ x, y, w, h = scaled_roi
+ roi_img = flipped_IMG[y:y+h, x:x+w]
+
+ # 4) RKNN 输入预处理
+ input_tensor = preprocess_image_for_rknn(
+ roi_img,
+ size=IMG_SIZE,
+ resize_mode=RESIZE_MODE,
+ to_rgb=TO_RGB,
+ normalize=NORMALIZE,
+ layout=LAYOUT
+ )
+
+ input_tensor = np.ascontiguousarray(input_tensor.astype(np.float32))
+
+ # 5) 推理
+ outputs = rknn_lite.inference([input_tensor])
+ pred = outputs[0].reshape(-1).astype(float)
+ class_id = int(np.argmax(pred))
+
+ # 分类名称处理
+ raw_class_name = CLASS_NAMES.get(class_id, f"未知({class_id})")
+ if class_id in [1, 2]:
+ final_class, _, _, _ = weighted_small_large(pred)
+ else:
+ final_class = raw_class_name
+
+ # 6) 可视化(画在 flipped_IMG)
+ vis_img = flipped_IMG.copy()
+
+ cv2.rectangle(vis_img, (x, y), (x+w, y+h), (255, 0, 0), 2)
+
+ ty = y - 10 if y >= 20 else y + h + 20
+ cv2.putText(
+ vis_img, f"Class: {final_class}",
+ (x, ty),
+ cv2.FONT_HERSHEY_SIMPLEX,
+ 0.8,(0,255,0), 2
+ )
+
+ return vis_img
+import cv2
+import numpy as np
+
+# ---------------------------
+# 固化配置
+# ---------------------------
+CLASS_NAMES = {
+ 0: "0",
+ 1: "1",
+ 2: "2",
+ 3: "3",
+ 4: "4"
+}
+
+# FIXED_ROI 是基于原始分辨率图像(翻转后)的坐标
+# 比如基于 1920x1080
+FIXED_ROI = (445, 540, 931, 319)
+
+# 原始分辨率(ROI 对应的标注基准)
+ORIG_W = 1920
+ORIG_H = 1080
+
+# 当前输入图像尺寸(你说已经固定成 640x640)
+CUR_W = 640
+CUR_H = 640
+
+IMG_SIZE = 640
+RESIZE_MODE = "stretch"
+TO_RGB = True
+NORMALIZE = False
+LAYOUT = "NHWC"
+WEIGHT_THRESHOLD = 0.4
+W1, W2 = 0.3, 0.7
+
+
+# ---------------------------
+# 基本工具函数
+# ---------------------------
+def resize_stretch(image, size=IMG_SIZE):
+ return cv2.resize(image, (size, size))
+
+def preprocess_image_for_rknn(img, size, resize_mode, to_rgb, normalize, layout):
+ if resize_mode == "letterbox":
+ raise NotImplementedError
+ else:
+ img_box = resize_stretch(img, size=size)
+
+ if to_rgb:
+ img_box = cv2.cvtColor(img_box, cv2.COLOR_BGR2RGB)
+
+ img_f = img_box.astype(np.float32)
+ if normalize:
+ img_f /= 255.0
+
+ if layout == "NHWC":
+ out = np.expand_dims(img_f, axis=0)
+ else:
+ out = np.expand_dims(np.transpose(img_f, (2, 0, 1)), axis=0)
+
+ return out.astype(np.float32)
+
+def weighted_small_large(pred, threshold=WEIGHT_THRESHOLD, w1=W1, w2=W2):
+ p1, p2 = float(pred[1]), float(pred[2])
+ total = p1 + p2
+ score = (w1*p1 + w2*p2)/total if total > 0 else 0.0
+ return ("大堆料" if score >= threshold else "小堆料"), score, p1, p2
+
+
+# ---------------------------
+# ROI 缩放函数(核心)
+# ---------------------------
+def scale_roi_to_current(img, orig_roi, orig_w, orig_h):
+ """
+ 将基于原始分辨率的 ROI 坐标,缩放到当前尺寸的图像。
+ """
+ x, y, w, h = orig_roi
+
+ # 当前图像尺寸
+ cur_h, cur_w = img.shape[:2]
+
+ scale_x = cur_w / orig_w
+ scale_y = cur_h / orig_h
+
+ sx = int(x * scale_x)
+ sy = int(y * scale_y)
+ sw = int(w * scale_x)
+ sh = int(h * scale_y)
+
+ return sx, sy, sw, sh
+
+
+# ---------------------------
+# 主推理接口
+# ---------------------------
+def myFunc(rknn_lite, IMG):
+ if IMG is None or IMG.size == 0:
+ raise ValueError("输入图像无效")
+
+ # 1) 输入已经是 640x640,但 ROI 是 1920x1080 标注的
+ # 所以必须先缩放 ROI
+ scaled_roi = scale_roi_to_current(IMG, FIXED_ROI, ORIG_W, ORIG_H)
+
+ # 2) 翻转图像(你说 ROI 是基于“翻转后的原图”)
+ flipped_IMG = cv2.flip(IMG, 0)
+
+ # 3) 在翻转后的图像上裁剪缩放好的 ROI
+ x, y, w, h = scaled_roi
+ roi_img = flipped_IMG[y:y+h, x:x+w]
+
+ # 4) RKNN 输入预处理
+ input_tensor = preprocess_image_for_rknn(
+ roi_img,
+ size=IMG_SIZE,
+ resize_mode=RESIZE_MODE,
+ to_rgb=TO_RGB,
+ normalize=NORMALIZE,
+ layout=LAYOUT
+ )
+
+ input_tensor = np.ascontiguousarray(input_tensor.astype(np.float32))
+
+ # 5) 推理
+ outputs = rknn_lite.inference([input_tensor])
+ pred = outputs[0].reshape(-1).astype(float)
+ class_id = int(np.argmax(pred))
+
+ # 分类名称处理
+ raw_class_name = CLASS_NAMES.get(class_id, f"未知({class_id})")
+ if class_id in [1, 2]:
+ final_class, _, _, _ = weighted_small_large(pred)
+ else:
+ final_class = raw_class_name
+
+ # 6) 可视化(画在 flipped_IMG)
+ vis_img = flipped_IMG.copy()
+
+ cv2.rectangle(vis_img, (x, y), (x+w, y+h), (255, 0, 0), 2)
+
+ ty = y - 10 if y >= 20 else y + h + 20
+ cv2.putText(
+ vis_img, f"Class: {final_class}",
+ (x, ty),
+ cv2.FONT_HERSHEY_SIMPLEX,
+ 0.8,(0,255,0), 2
+ )
+
+ return vis_img
+import cv2
+import numpy as np
+
+# ---------------------------
+# 固化配置
+# ---------------------------
+CLASS_NAMES = {
+ 0: "0",
+ 1: "1",
+ 2: "2",
+ 3: "3",
+ 4: "4"
+}
+
+# FIXED_ROI 是基于原始分辨率图像(翻转后)的坐标
+# 比如基于 1920x1080
+FIXED_ROI = (445, 540, 931, 319)
+
+# 原始分辨率(ROI 对应的标注基准)
+ORIG_W = 1920
+ORIG_H = 1080
+
+# 当前输入图像尺寸(你说已经固定成 640x640)
+CUR_W = 640
+CUR_H = 640
+
+IMG_SIZE = 640
+RESIZE_MODE = "stretch"
+TO_RGB = True
+NORMALIZE = False
+LAYOUT = "NHWC"
+WEIGHT_THRESHOLD = 0.4
+W1, W2 = 0.3, 0.7
+
+
+# ---------------------------
+# 基本工具函数
+# ---------------------------
+def resize_stretch(image, size=IMG_SIZE):
+ return cv2.resize(image, (size, size))
+
+def preprocess_image_for_rknn(img, size, resize_mode, to_rgb, normalize, layout):
+ if resize_mode == "letterbox":
+ raise NotImplementedError
+ else:
+ img_box = resize_stretch(img, size=size)
+
+ if to_rgb:
+ img_box = cv2.cvtColor(img_box, cv2.COLOR_BGR2RGB)
+
+ img_f = img_box.astype(np.float32)
+ if normalize:
+ img_f /= 255.0
+
+ if layout == "NHWC":
+ out = np.expand_dims(img_f, axis=0)
+ else:
+ out = np.expand_dims(np.transpose(img_f, (2, 0, 1)), axis=0)
+
+ return out.astype(np.float32)
+
+def weighted_small_large(pred, threshold=WEIGHT_THRESHOLD, w1=W1, w2=W2):
+ p1, p2 = float(pred[1]), float(pred[2])
+ total = p1 + p2
+ score = (w1*p1 + w2*p2)/total if total > 0 else 0.0
+ return ("大堆料" if score >= threshold else "小堆料"), score, p1, p2
+
+
+# ---------------------------
+# ROI 缩放函数(核心)
+# ---------------------------
+def scale_roi_to_current(img, orig_roi, orig_w, orig_h):
+ """
+ 将基于原始分辨率的 ROI 坐标,缩放到当前尺寸的图像。
+ """
+ x, y, w, h = orig_roi
+
+ # 当前图像尺寸
+ cur_h, cur_w = img.shape[:2]
+
+ scale_x = cur_w / orig_w
+ scale_y = cur_h / orig_h
+
+ sx = int(x * scale_x)
+ sy = int(y * scale_y)
+ sw = int(w * scale_x)
+ sh = int(h * scale_y)
+
+ return sx, sy, sw, sh
+
+
+# ---------------------------
+# 主推理接口
+# ---------------------------
+def myFunc(rknn_lite, IMG):
+ if IMG is None or IMG.size == 0:
+ raise ValueError("输入图像无效")
+
+ # 1) 输入已经是 640x640,但 ROI 是 1920x1080 标注的
+ # 所以必须先缩放 ROI
+ scaled_roi = scale_roi_to_current(IMG, FIXED_ROI, ORIG_W, ORIG_H)
+
+ # 2) 翻转图像(你说 ROI 是基于“翻转后的原图”)
+ flipped_IMG = cv2.flip(IMG, 0)
+
+ # 3) 在翻转后的图像上裁剪缩放好的 ROI
+ x, y, w, h = scaled_roi
+ roi_img = flipped_IMG[y:y+h, x:x+w]
+
+ # 4) RKNN 输入预处理
+ input_tensor = preprocess_image_for_rknn(
+ roi_img,
+ size=IMG_SIZE,
+ resize_mode=RESIZE_MODE,
+ to_rgb=TO_RGB,
+ normalize=NORMALIZE,
+ layout=LAYOUT
+ )
+
+ input_tensor = np.ascontiguousarray(input_tensor.astype(np.float32))
+
+ # 5) 推理
+ outputs = rknn_lite.inference([input_tensor])
+ pred = outputs[0].reshape(-1).astype(float)
+ class_id = int(np.argmax(pred))
+
+ # 分类名称处理
+ raw_class_name = CLASS_NAMES.get(class_id, f"未知({class_id})")
+ if class_id in [1, 2]:
+ final_class, _, _, _ = weighted_small_large(pred)
+ else:
+ final_class = raw_class_name
+
+ # 6) 可视化(画在 flipped_IMG)
+ vis_img = flipped_IMG.copy()
+
+ cv2.rectangle(vis_img, (x, y), (x+w, y+h), (255, 0, 0), 2)
+
+ ty = y - 10 if y >= 20 else y + h + 20
+ cv2.putText(
+ vis_img, f"Class: {final_class}",
+ (x, ty),
+ cv2.FONT_HERSHEY_SIMPLEX,
+ 0.8,(0,255,0), 2
+ )
+
+ return vis_img
+
diff --git a/rknn-multi-threaded-nosigmoid/func_obb (copy 1).py b/rknn-multi-threaded-nosigmoid/func_obb (copy 1).py
new file mode 100644
index 0000000..7aed9a6
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/func_obb (copy 1).py
@@ -0,0 +1,198 @@
+import cv2
+import numpy as np
+import math
+
+# ---------- 配置 ----------
+CLASSES = ['clamp']
+nmsThresh = 0.4
+objectThresh = 0.5
+INPUT_DTYPE = np.uint8 # 根据模型类型修改
+
+# ---------- 可选绘制 ----------
+DRAW_BOX = True # 是否绘制检测框
+DRAW_SCORE = False # 是否绘制置信度
+
+# ---------------- 工具函数 ----------------
+class DetectBox:
+ def __init__(self, classId, score, xmin, ymin, xmax, ymax, angle):
+ self.classId = classId
+ self.score = score
+ self.xmin = xmin
+ self.ymin = ymin
+ self.xmax = xmax
+ self.ymax = ymax
+ self.angle = angle
+
+def letterbox_resize(image, size, bg_color=114):
+ tw, th = size
+ h, w = image.shape[:2]
+ scale = min(tw / w, th / h)
+ nw, nh = int(w * scale), int(h * scale)
+ img_resized = cv2.resize(image, (nw, nh), interpolation=cv2.INTER_AREA)
+ canvas = np.full((th, tw, 3), bg_color, dtype=np.uint8)
+ dx, dy = (tw - nw) // 2, (th - nh) // 2
+ canvas[dy:dy + nh, dx:dx + nw] = img_resized
+ return canvas, scale, dx, dy
+
+def rotate_rectangle(x1, y1, x2, y2, a):
+ cx, cy = (x1 + x2) / 2, (y1 + y2) / 2
+ cos_a, sin_a = math.cos(a), math.sin(a)
+ pts = [[x1, y1], [x1, y2], [x2, y2], [x2, y1]]
+ return [
+ [
+ int(cx + (xx - cx) * cos_a - (yy - cy) * sin_a),
+ int(cy + (xx - cx) * sin_a + (yy - cy) * cos_a)
+ ]
+ for xx, yy in pts
+ ]
+
+def polygon_area(pts):
+ x, y = zip(*pts)
+ return 0.5 * abs(sum(x[i] * y[(i + 1) % len(pts)] - x[(i + 1) % len(pts)] * y[i] for i in range(len(pts))))
+
+def polygon_intersection_area(p1, p2):
+ try:
+ from shapely.geometry import Polygon
+ poly1, poly2 = Polygon(p1), Polygon(p2)
+ if not poly1.is_valid:
+ poly1 = poly1.buffer(0)
+ if not poly2.is_valid:
+ poly2 = poly2.buffer(0)
+ inter = poly1.intersection(poly2)
+ return inter.area if not inter.is_empty else 0.0
+ except Exception:
+ return 0.0
+
+def IoU(box1, box2):
+ p1 = rotate_rectangle(box1.xmin, box1.ymin, box1.xmax, box1.ymax, box1.angle)
+ p2 = rotate_rectangle(box2.xmin, box2.ymin, box2.xmax, box2.ymax, box2.angle)
+ inter = polygon_intersection_area(p1, p2)
+ area1 = polygon_area(p1)
+ area2 = polygon_area(p2)
+ union = area1 + area2 - inter
+ return inter / union if union > 1e-6 else 0.0
+
+def NMS(detectResult):
+ if not detectResult:
+ return []
+ # 先按分数排序
+ boxes = sorted(detectResult, key=lambda x: x.score, reverse=True)
+ keep = []
+ for i, b1 in enumerate(boxes):
+ if b1.classId == -1:
+ continue
+ keep.append(b1)
+ for j in range(i + 1, len(boxes)):
+ b2 = boxes[j]
+ if b2.classId == b1.classId and IoU(b1, b2) > nmsThresh:
+ b2.classId = -1
+ return keep
+
+from scipy.special import expit
+
+def sigmoid(x):
+ return expit(x)
+
+def softmax(x, axis=-1):
+ x = np.asarray(x)
+ x_max = np.max(x, axis=axis, keepdims=True)
+ e = np.exp(x - x_max)
+ return e / (e.sum(axis=axis, keepdims=True) + 1e-9)
+
+ARANGE16 = np.arange(16).reshape(1, 1, 16, 1)
+
+def process(out, model_w, model_h, stride, angle_feature, index, scale_w=1, scale_h=1):
+ angle_feature = angle_feature.reshape(-1)
+ xywh = out[:, :64, :] # [1, 64, H*W]
+ conf = sigmoid(out[:, 64:, :]).reshape(-1) # [H*W]
+ boxes = []
+ class_num = len(CLASSES)
+ total = model_h * model_w * class_num
+ for ik in range(total):
+ if conf[ik] <= objectThresh:
+ continue
+ w = ik % model_w
+ h = (ik // model_w) % model_h
+ c = ik // (model_w * model_h)
+ xywh_ = xywh[0, :, h * model_w + w].reshape(1, 4, 16, 1)
+ xywh_ = softmax(xywh_, axis=2)
+ xywh_ = np.sum(xywh_ * ARANGE16, axis=2).reshape(-1) # [4]
+ xy_add = xywh_[:2] + xywh_[2:]
+ xy_sub = (xywh_[2:] - xywh_[:2]) / 2
+ angle = (angle_feature[index + h * model_w + w] - 0.25) * math.pi
+ cos_a, sin_a = math.cos(angle), math.sin(angle)
+ xy_rot = np.array([
+ xy_sub[0] * cos_a - xy_sub[1] * sin_a,
+ xy_sub[0] * sin_a + xy_sub[1] * cos_a
+ ])
+ cx = (xy_rot[0] + w + 0.5) * stride
+ cy = (xy_rot[1] + h + 0.5) * stride
+ w_box = xy_add[0] * stride
+ h_box = xy_add[1] * stride
+ xmin = (cx - w_box / 2) * scale_w
+ ymin = (cy - h_box / 2) * scale_h
+ xmax = (cx + w_box / 2) * scale_w
+ ymax = (cy + h_box / 2) * scale_h
+ boxes.append(DetectBox(c, float(conf[ik]), float(xmin), float(ymin), float(xmax), float(ymax), float(angle)))
+ return boxes
+
+# ---------------- RKNN 推理接口 ----------------
+def myFunc(rknn, frame, lock=None):
+ try:
+ img_resized, scale, offset_x, offset_y = letterbox_resize(frame, (640, 640))
+ infer_img = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB)
+ infer_input = np.expand_dims(infer_img.astype(INPUT_DTYPE), 0)
+
+ results = rknn.inference([infer_input])
+ if not results or len(results) < 1:
+ return frame
+
+ outputs = []
+ for x in results[:-1]:
+ if x is None:
+ continue
+ stride, index = 0, 0
+ if x.shape[2] == 20:
+ stride, index = 32, 20 * 4 * 20 * 4 + 20 * 2 * 20 * 2
+ elif x.shape[2] == 40:
+ stride, index = 16, 20 * 4 * 20 * 4
+ elif x.shape[2] == 80:
+ stride, index = 8, 0
+ else:
+ continue
+ feature = x.reshape(1, 65, -1)
+ outputs += process(feature, x.shape[3], x.shape[2], stride, results[-1], index, 1.0 / scale, 1.0 / scale)
+
+ if not outputs:
+ return frame
+
+ predbox = NMS(outputs)
+ if len(predbox) < 2:
+ return frame
+
+ box1, box2 = sorted(predbox, key=lambda x: x.score, reverse=True)[:2]
+
+ out_frame = frame.copy()
+ for box in [box1, box2]:
+ xmin = int((box.xmin - offset_x) / scale)
+ ymin = int((box.ymin - offset_y) / scale)
+ xmax = int((box.xmax - offset_x) / scale)
+ ymax = int((box.ymax - offset_y) / scale)
+ if DRAW_BOX:
+ pts = rotate_rectangle(xmin, ymin, xmax, ymax, box.angle)
+ cv2.polylines(out_frame, [np.array(pts, np.int32)], True, (0, 255, 0), 2)
+ if DRAW_SCORE:
+ cv2.putText(
+ out_frame,
+ f"{box.score:.2f}",
+ (xmin, max(10, ymin - 6)),
+ cv2.FONT_HERSHEY_SIMPLEX,
+ 0.6,
+ (0, 255, 0),
+ 2,
+ )
+ return out_frame
+
+ except Exception as e:
+ print(f"[func ❌] 推理异常: {e}")
+ return frame # 出错时返回原图,保证是 np.ndarray
diff --git a/rknn-multi-threaded-nosigmoid/func_obb.py b/rknn-multi-threaded-nosigmoid/func_obb.py
new file mode 100644
index 0000000..ac94f71
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/func_obb.py
@@ -0,0 +1,214 @@
+import cv2
+import numpy as np
+import math
+
+# ---------- 配置 ----------
+CLASSES = ['clamp']
+nmsThresh = 0.4
+objectThresh = 0.5
+INPUT_DTYPE = np.uint8
+
+DRAW_BOX = True
+DRAW_SCORE = False
+
+# ---------------- 工具函数 ----------------
+class DetectBox:
+ def __init__(self, classId, score, xmin, ymin, xmax, ymax, angle):
+ self.classId = classId
+ self.score = score
+ self.xmin = xmin
+ self.ymin = ymin
+ self.xmax = xmax
+ self.ymax = ymax
+ self.angle = angle
+
+def letterbox_resize(image, size, bg_color=114):
+ tw, th = size
+ h, w = image.shape[:2]
+ scale = min(tw / w, th / h)
+ nw, nh = int(w * scale), int(h * scale)
+ img_resized = cv2.resize(image, (nw, nh), interpolation=cv2.INTER_AREA)
+ canvas = np.full((th, tw, 3), bg_color, dtype=np.uint8)
+ dx, dy = (tw - nw) // 2, (th - nh) // 2
+ canvas[dy:dy + nh, dx:dx + nw] = img_resized
+ return canvas, scale, dx, dy
+
+def rotate_rectangle(x1, y1, x2, y2, a):
+ cx, cy = (x1 + x2) / 2, (y1 + y2) / 2
+ cos_a, sin_a = math.cos(a), math.sin(a)
+ pts = [[x1, y1], [x1, y2], [x2, y2], [x2, y1]]
+ return [
+ [
+ int(cx + (xx - cx) * cos_a - (yy - cy) * sin_a),
+ int(cy + (xx - cx) * sin_a + (yy - cy) * cos_a)
+ ]
+ for xx, yy in pts
+ ]
+
+def polygon_area(pts):
+ x, y = zip(*pts)
+ return 0.5 * abs(sum(x[i] * y[(i+1)%4] - x[(i+1)%4]*y[i] for i in range(4)))
+
+def polygon_intersection_area(p1, p2):
+ try:
+ from shapely.geometry import Polygon
+ poly1, poly2 = Polygon(p1), Polygon(p2)
+ if not poly1.is_valid: poly1 = poly1.buffer(0)
+ if not poly2.is_valid: poly2 = poly2.buffer(0)
+ inter = poly1.intersection(poly2)
+ return inter.area if not inter.is_empty else 0
+ except:
+ return 0
+
+def IoU(b1, b2):
+ p1 = rotate_rectangle(b1.xmin, b1.ymin, b1.xmax, b1.ymax, b1.angle)
+ p2 = rotate_rectangle(b2.xmin, b2.ymin, b2.xmax, b2.ymax, b2.angle)
+ inter = polygon_intersection_area(p1, p2)
+ area1 = polygon_area(p1)
+ area2 = polygon_area(p2)
+ union = area1 + area2 - inter
+ return inter / union if union > 1e-6 else 0
+
+def NMS(boxes):
+ if not boxes: return []
+ boxes = sorted(boxes, key=lambda x: x.score, reverse=True)
+ keep = []
+ for i, b1 in enumerate(boxes):
+ if b1.classId == -1: continue
+ keep.append(b1)
+ for j in range(i+1, len(boxes)):
+ b2 = boxes[j]
+ if b2.classId == b1.classId and IoU(b1, b2) > nmsThresh:
+ b2.classId = -1
+ return keep
+
+from scipy.special import expit
+def sigmoid(x): return expit(x)
+
+def softmax(x, axis=-1):
+ x = np.asarray(x)
+ x_max = np.max(x, axis=axis, keepdims=True)
+ e = np.exp(x - x_max)
+ return e / (e.sum(axis=axis, keepdims=True) + 1e-9)
+
+ARANGE16 = np.arange(16).reshape(1,1,16,1)
+
+def process(out, mw, mh, stride, angle_feature, index, scale_w=1, scale_h=1):
+ angle_feature = angle_feature.reshape(-1)
+ xywh = out[:, :64, :]
+ conf = sigmoid(out[:, 64:, :]).reshape(-1)
+ boxes = []
+ class_num = len(CLASSES)
+
+ total = mh * mw * class_num
+ for ik in range(total):
+ if conf[ik] <= objectThresh: continue
+ w = ik % mw
+ h = (ik // mw) % mh
+ c = ik // (mw * mh)
+
+ xywh_ = xywh[0, :, h * mw + w].reshape(1,4,16,1)
+ xywh_ = softmax(xywh_, axis=2)
+ xywh_ = np.sum(xywh_ * ARANGE16, axis=2).reshape(-1)
+
+ xy_add = xywh_[:2] + xywh_[2:]
+ xy_sub = (xywh_[2:] - xywh_[:2]) / 2
+
+ angle = (angle_feature[index + h*mw + w] - 0.25) * math.pi
+
+ cos_a, sin_a = math.cos(angle), math.sin(angle)
+ xy_rot = np.array([
+ xy_sub[0]*cos_a - xy_sub[1]*sin_a,
+ xy_sub[0]*sin_a + xy_sub[1]*cos_a
+ ])
+
+ cx = (xy_rot[0] + w + 0.5) * stride
+ cy = (xy_rot[1] + h + 0.5) * stride
+ w_box = xy_add[0] * stride
+ h_box = xy_add[1] * stride
+
+ xmin = (cx - w_box/2) * scale_w
+ ymin = (cy - h_box/2) * scale_h
+ xmax = (cx + w_box/2) * scale_w
+ ymax = (cy + h_box/2) * scale_h
+
+ boxes.append(DetectBox(c, float(conf[ik]), xmin, ymin, xmax, ymax, float(angle)))
+
+ return boxes
+
+# ---------------- RKNN 推理接口 ----------------
+def myFunc(rknn, frame, lock=None):
+ try:
+ # --- 推理前 letterbox ---
+ img_resized, scale, offset_x, offset_y = letterbox_resize(frame, (640,640))
+ infer_img = cv2.cvtColor(img_resized, cv2.COLOR_BGR2RGB)
+ infer_input = np.expand_dims(infer_img.astype(INPUT_DTYPE), 0)
+
+ results = rknn.inference([infer_input])
+ if not results or len(results) < 1:
+ return frame
+
+ outputs = []
+ for x in results[:-1]:
+ if x is None: continue
+ # 决定 stride 与索引
+ if x.shape[2] == 20:
+ stride, index = 32, 20*4*20*4 + 20*2*20*2
+ elif x.shape[2] == 40:
+ stride, index = 16, 20*4*20*4
+ elif x.shape[2] == 80:
+ stride, index = 8, 0
+ else:
+ continue
+
+ feature = x.reshape(1,65,-1)
+ outputs += process(
+ feature,
+ x.shape[3],
+ x.shape[2],
+ stride,
+ results[-1],
+ index,
+ 1.0, 1.0 # 输出坐标保持在640×640下
+ )
+
+ if not outputs:
+ return frame
+
+ predbox = NMS(outputs)
+ if len(predbox) < 2:
+ return frame
+
+ box1, box2 = sorted(predbox, key=lambda x: x.score, reverse=True)[:2]
+
+ out_frame = frame.copy()
+
+ # ========== 还原到原图坐标 ==========
+ def restore_to_original(b):
+ xmin = int((b.xmin - offset_x) / scale)
+ ymin = int((b.ymin - offset_y) / scale)
+ xmax = int((b.xmax - offset_x) / scale)
+ ymax = int((b.ymax - offset_y) / scale)
+ return xmin, ymin, xmax, ymax
+
+ for box in [box1, box2]:
+ xmin, ymin, xmax, ymax = restore_to_original(box)
+
+ if DRAW_BOX:
+ # 旋转框顶点(注意:旋转必须在原图坐标系)
+ pts = rotate_rectangle(xmin, ymin, xmax, ymax, box.angle)
+ cv2.polylines(out_frame, [np.array(pts, np.int32)], True, (0,255,0), 2)
+
+ if DRAW_SCORE:
+ cv2.putText(
+ out_frame, f"{box.score:.2f}",
+ (xmin, max(10, ymin - 6)),
+ cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,255,0), 2
+ )
+
+ return out_frame
+
+ except Exception as e:
+ print(f"[func ❌] 推理异常: {e}")
+ return frame
+
diff --git a/rknn-multi-threaded-nosigmoid/func(复件).py b/rknn-multi-threaded-nosigmoid/func(复件).py
new file mode 100644
index 0000000..dfb4eaa
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/func(复件).py
@@ -0,0 +1,254 @@
+# 下面代码基于你给出的 yolov5 示例做最小修改的适配版
+import cv2
+import numpy as np
+
+OBJ_THRESH, NMS_THRESH, IMG_SIZE = 0.25, 0.45, 640
+
+CLASSES = ("person", "bicycle", "car", "motorbike ", "aeroplane ", "bus ", "train", "truck ", "boat", "traffic light",
+ "fire hydrant", "stop sign ", "parking meter", "bench", "bird", "cat", "dog ", "horse ", "sheep", "cow", "elephant",
+ "bear", "zebra ", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee", "skis", "snowboard", "sports ball", "kite",
+ "baseball bat", "baseball glove", "skateboard", "surfboard", "tennis racket", "bottle", "wine glass", "cup", "fork", "knife ",
+ "spoon", "bowl", "banana", "apple", "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza ", "donut", "cake", "chair", "sofa",
+ "pottedplant", "bed", "diningtable", "toilet ", "tvmonitor", "laptop\t", "mouse\t", "remote ", "keyboard ", "cell phone", "microwave ",
+ "oven ", "toaster", "sink", "refrigerator ", "book", "clock", "vase", "scissors ", "teddy bear ", "hair drier", "toothbrush ")
+
+# ---------- 保留你原来的辅助函数(process/filter/nms/xywh2xyxy) ----------
+def xywh2xyxy(x):
+ y = np.copy(x)
+ y[:, 0] = x[:, 0] - x[:, 2] / 2
+ y[:, 1] = x[:, 1] - x[:, 3] / 2
+ y[:, 2] = x[:, 0] + x[:, 2] / 2
+ y[:, 3] = x[:, 1] + x[:, 3] / 2
+ return y
+
+
+def process(input, mask, anchors):
+ # input: (grid_h, grid_w, 3, attrs) attrs >=5+num_classes
+ # anchors: list of (w,h) pairs for the 3 anchors
+ anchors = [anchors[i] for i in mask]
+ grid_h, grid_w = map(int, input.shape[0:2])
+
+ box_confidence = input[..., 4]
+ box_confidence = np.expand_dims(box_confidence, axis=-1)
+
+ box_class_probs = input[..., 5:]
+
+ # YOLO11 style decode used originally in your code:
+ box_xy = input[..., :2] * 2 - 0.5
+
+ # build grid
+ col = np.tile(np.arange(0, grid_w), grid_w).reshape(-1, grid_w)
+ row = np.tile(np.arange(0, grid_h).reshape(-1, 1), grid_h)
+ col = col.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
+ row = row.reshape(grid_h, grid_w, 1, 1).repeat(3, axis=-2)
+ grid = np.concatenate((col, row), axis=-1)
+ box_xy += grid
+ box_xy *= int(IMG_SIZE / grid_h)
+
+ box_wh = pow(input[..., 2:4] * 2, 2)
+ # multiply by provided anchors (we will use unit anchors if we want to neutralize)
+ box_wh = box_wh * anchors
+
+ return np.concatenate((box_xy, box_wh), axis=-1), box_confidence, box_class_probs
+
+
+def filter_boxes(boxes, box_confidences, box_class_probs):
+ boxes = boxes.reshape(-1, 4)
+ box_confidences = box_confidences.reshape(-1)
+ box_class_probs = box_class_probs.reshape(-1, box_class_probs.shape[-1])
+
+ _box_pos = np.where(box_confidences >= OBJ_THRESH)
+ boxes = boxes[_box_pos]
+ box_confidences = box_confidences[_box_pos]
+ box_class_probs = box_class_probs[_box_pos]
+
+ class_max_score = np.max(box_class_probs, axis=-1)
+ classes = np.argmax(box_class_probs, axis=-1)
+ _class_pos = np.where(class_max_score >= OBJ_THRESH)
+
+ return boxes[_class_pos], classes[_class_pos], (class_max_score * box_confidences)[_class_pos]
+
+
+def nms_boxes(boxes, scores):
+ x = boxes[:, 0]
+ y = boxes[:, 1]
+ w = boxes[:, 2] - boxes[:, 0]
+ h = boxes[:, 3] - boxes[:, 1]
+
+ areas = w * h
+ order = scores.argsort()[::-1]
+
+ keep = []
+ eps = 1e-7
+ while order.size > 0:
+ i = order[0]
+ keep.append(i)
+
+ xx1 = np.maximum(x[i], x[order[1:]])
+ yy1 = np.maximum(y[i], y[order[1:]])
+ xx2 = np.minimum(x[i] + w[i], x[order[1:]] + w[order[1:]])
+ yy2 = np.minimum(y[i] + h[i], y[order[1:]] + h[order[1:]])
+
+ w1 = np.maximum(0.0, xx2 - xx1 + 1e-5)
+ h1 = np.maximum(0.0, yy2 - yy1 + 1e-5)
+ inter = w1 * h1
+
+ denom = (areas[i] + areas[order[1:]] - inter)
+ denom = np.maximum(denom, eps)
+ ovr = inter / denom
+ inds = np.where(ovr <= NMS_THRESH)[0]
+ order = order[inds + 1]
+ return np.array(keep)
+
+
+# ---------- 关键:把你的 9 输出拼成原来 yolov5_post_process 需要的 input_data 格式 ----------
+def yolov11_to_yolov5_style_input(outputs):
+ """
+ outputs: list of 9 tensors (1, C, H, W) in this order per your print:
+ [reg80, cls80, obj80, reg40, cls40, obj40, reg20, cls20, obj20]
+ We will convert each scale to a (H, W, 3, 5+num_classes) array and repeat
+ the same per-anchor slice so that your existing yolov5_post_process can be reused.
+ To avoid anchor scaling changing box_wh, we set anchors to 1x1 in later call.
+ """
+ input_data = []
+ # scales: (indices and corresponding H,W from tensors)
+ for i in range(0, 9, 3):
+ reg = outputs[i][0] # (64, H, W)
+ cls = outputs[i+1][0] # (80, H, W)
+ obj = outputs[i+2][0] # (1, H, W)
+
+ # find H,W from reg
+ H = reg.shape[1]
+ W = reg.shape[2]
+
+ # xywh: assume first 4 channels of reg are x,y,w,h per cell
+ xywh = reg[0:4, :, :] # (4, H, W)
+ xywh = np.transpose(xywh, (1, 2, 0)) # (H, W, 4)
+
+ # obj and cls to H,W,?
+ obj_hw = np.transpose(obj[0, :, :], (0, 1)) # (H, W)
+ cls_hw = np.transpose(cls, (1, 2, 0)) # (H, W, 80)
+
+ # build one anchor slice: [x,y,w,h,obj, cls80] -> shape (H, W, 5+80)
+ slice_hw = np.concatenate([xywh, obj_hw[..., None], cls_hw], axis=-1) # (H, W, 85)
+
+ # repeat to make 3 anchors per cell (so shape becomes H,W,3,85)
+ slice_3 = np.repeat(slice_hw[:, :, None, :], 3, axis=2) # (H, W, 3, 85)
+
+ input_data.append(slice_3)
+
+ return input_data
+
+
+def yolov5_post_process_adapted(input_data):
+ """
+ 复用你原来的 yolov5_post_process,但使用 unit anchors so that
+ box_wh 不会被不正确放缩。
+ """
+ masks = [[0, 1, 2], [3, 4, 5], [6, 7, 8]] # unused in anchors here, but kept for compatibility
+ # use neutral anchors (1,1) to avoid scaling change
+ anchors = [[1, 1], [1, 1], [1, 1], [1, 1], [1, 1],
+ [1, 1], [1, 1], [1, 1], [1, 1]]
+
+ boxes, classes, scores = [], [], []
+ # input_data already is list of 3 arrays shaped (H,W,3,85)
+ for input in input_data:
+ # process() expects shape (grid_h, grid_w, 3, attrs)
+ b, c, s = process(input, [0,1,2], anchors) # mask and anchors values used inside process
+ b, c, s = filter_boxes(b, c, s)
+ boxes.append(b)
+ classes.append(c)
+ scores.append(s)
+
+ if len(boxes) == 0:
+ return None, None, None
+
+ boxes = np.concatenate(boxes)
+ boxes = xywh2xyxy(boxes)
+ classes = np.concatenate(classes)
+ scores = np.concatenate(scores)
+
+ # nms per class
+ nboxes, nclasses, nscores = [], [], []
+ for cls_id in set(classes):
+ inds = np.where(classes == cls_id)
+ b = boxes[inds]
+ c = classes[inds]
+ s = scores[inds]
+
+ keep = nms_boxes(b, s)
+
+ nboxes.append(b[keep])
+ nclasses.append(c[keep])
+ nscores.append(s[keep])
+
+ if not nclasses and not nscores:
+ return None, None, None
+
+ return np.concatenate(nboxes), np.concatenate(nclasses), np.concatenate(nscores)
+
+
+# ---------- draw 保持原样 ----------
+def draw(image, boxes, scores, classes):
+ for box, score, cl in zip(boxes, scores, classes):
+ top, left, right, bottom = box
+ top = int(top)
+ left = int(left)
+ cv2.rectangle(image, (top, left), (int(right), int(bottom)), (255, 0, 0), 2)
+ cv2.putText(image, '{0} {1:.2f}'.format(CLASSES[cl], score),
+ (top, left - 6),
+ cv2.FONT_HERSHEY_SIMPLEX,
+ 0.6, (0, 0, 255), 2)
+
+
+# ---------- 最终 myFunc(替换你原来的 myFunc) ----------
+def myFunc(rknn_lite, IMG):
+ # 1. BGR → RGB
+ img_rgb = cv2.cvtColor(IMG, cv2.COLOR_BGR2RGB)
+
+ # 2. Resize → 640x640
+ img_resized = cv2.resize(img_rgb, (IMG_SIZE, IMG_SIZE))
+
+ # 3. HWC → CHW
+ img_chw = img_resized.transpose(2, 0, 1)
+
+ # 4. expand batch
+ img_input = np.expand_dims(img_chw, axis=0).astype(np.uint8)
+
+ # ========= Inference =========
+ outputs = rknn_lite.inference(inputs=[img_input])
+ if outputs is None:
+ print("⚠️ 推理失败")
+ return IMG, False
+
+ # ========= reshape heads =========
+ # YOLO11 和 YOLOv5 一样有 3 个输出 head(S=80/40/20)
+ head0 = outputs[0].reshape([3, -1] + list(outputs[0].shape[-2:])) # (3,85,80,80)
+ head1 = outputs[1].reshape([3, -1] + list(outputs[1].shape[-2:])) # (3,85,40,40)
+ head2 = outputs[2].reshape([3, -1] + list(outputs[2].shape[-2:])) # (3,85,20,20)
+
+ # transpose 到符合 yolov5 后处理尺寸 (H,W,3,85)
+ input_data = [
+ np.transpose(head0, (2, 3, 0, 1)),
+ np.transpose(head1, (2, 3, 0, 1)),
+ np.transpose(head2, (2, 3, 0, 1)),
+ ]
+
+ # ========= YOLOv5 后处理 =========
+ boxes, classes, scores = yolov5_post_process(input_data)
+
+ # 只保留 car 类别(COCO = 类别 2)
+ if boxes is not None:
+ keep = np.where(classes == 2)
+ boxes = boxes[keep]
+ scores = scores[keep]
+ classes = classes[keep]
+
+ # ========= 画框 =========
+ img_out = IMG.copy()
+ if boxes is not None and len(boxes) > 0:
+ draw(img_out, boxes, scores, classes)
+
+ return img_out, True
+
+
diff --git a/rknn-multi-threaded-nosigmoid/main.py b/rknn-multi-threaded-nosigmoid/main.py
new file mode 100644
index 0000000..99d4d3c
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/main.py
@@ -0,0 +1,54 @@
+import cv2
+import time
+from rknnpool import rknnPoolExecutor
+# 图像处理函数,实际应用过程中需要自行修改
+from func_cls5 import myFunc
+
+# ---------------- 用户配置 ----------------
+RTSP_URL = "rtsp://admin:XJ123456@192.168.250.61:554/streaming/channels/102"
+modelPath = "./rknnModel/cls5.rknn"
+# 打开视频流
+cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
+if not cap.isOpened():
+ print("❌ 无法打开视频流")
+ exit(0)
+# 线程数, 增大可提高帧率
+TPEs = 3
+# 初始化rknn池
+pool = rknnPoolExecutor(
+ rknnModel=modelPath,
+ TPEs=TPEs,
+ func=myFunc)
+
+# 初始化异步所需要的帧
+if (cap.isOpened()):
+ for i in range(TPEs + 1):
+ ret, frame = cap.read()
+ if not ret:
+ cap.release()
+ del pool
+ exit(-1)
+ pool.put(frame)
+
+frames, loopTime, initTime = 0, time.time(), time.time()
+while (cap.isOpened()):
+ frames += 1
+ ret, frame = cap.read()
+ if not ret:
+ break
+ pool.put(frame)
+ frame, flag = pool.get()
+ if flag == False:
+ break
+ cv2.imshow('test', frame)
+ if cv2.waitKey(1) & 0xFF == ord('q'):
+ break
+ if frames % 30 == 0:
+ print("30帧平均帧率:\t", 30 / (time.time() - loopTime), "帧")
+ loopTime = time.time()
+
+print("总平均帧率\t", frames / (time.time() - initTime))
+# 释放cap和rknn线程池
+cap.release()
+cv2.destroyAllWindows()
+pool.release()
diff --git a/rknn-multi-threaded-nosigmoid/main_cls.py b/rknn-multi-threaded-nosigmoid/main_cls.py
new file mode 100644
index 0000000..ff28e93
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/main_cls.py
@@ -0,0 +1,54 @@
+import cv2
+import time
+from rknnpool import rknnPoolExecutor
+# 图像处理函数,实际应用过程中需要自行修改
+from func_cls import myFunc
+
+# ---------------- 用户配置 ----------------
+RTSP_URL = "rtsp://admin:XJ123456@192.168.250.61:554/streaming/channels/102"
+modelPath = "./rknnModel/cls.rknn"
+# 打开视频流
+cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
+if not cap.isOpened():
+ print("❌ 无法打开视频流")
+ exit(0)
+# 线程数, 增大可提高帧率
+TPEs = 3
+# 初始化rknn池
+pool = rknnPoolExecutor(
+ rknnModel=modelPath,
+ TPEs=TPEs,
+ func=myFunc)
+
+# 初始化异步所需要的帧
+if (cap.isOpened()):
+ for i in range(TPEs + 1):
+ ret, frame = cap.read()
+ if not ret:
+ cap.release()
+ del pool
+ exit(-1)
+ pool.put(frame)
+
+frames, loopTime, initTime = 0, time.time(), time.time()
+while (cap.isOpened()):
+ frames += 1
+ ret, frame = cap.read()
+ if not ret:
+ break
+ pool.put(frame)
+ frame, flag = pool.get()
+ if flag == False:
+ break
+ cv2.imshow('test', frame)
+ if cv2.waitKey(1) & 0xFF == ord('q'):
+ break
+ if frames % 30 == 0:
+ print("30帧平均帧率:\t", 30 / (time.time() - loopTime), "帧")
+ loopTime = time.time()
+
+print("总平均帧率\t", frames / (time.time() - initTime))
+# 释放cap和rknn线程池
+cap.release()
+cv2.destroyAllWindows()
+pool.release()
diff --git a/rknn-multi-threaded-nosigmoid/main_obb.py b/rknn-multi-threaded-nosigmoid/main_obb.py
new file mode 100644
index 0000000..e118043
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/main_obb.py
@@ -0,0 +1,54 @@
+import cv2
+import time
+from rknnpool import rknnPoolExecutor
+# 图像处理函数,实际应用过程中需要自行修改
+from func_obb import myFunc
+
+# ---------------- 用户配置 ----------------
+RTSP_URL = "rtsp://admin:XJ123456@192.168.250.61:554/streaming/channels/102"
+modelPath = "./rknnModel/obb.rknn"
+# 打开视频流
+cap = cv2.VideoCapture(RTSP_URL, cv2.CAP_FFMPEG)
+if not cap.isOpened():
+ print("❌ 无法打开视频流")
+ exit(0)
+# 线程数, 增大可提高帧率
+TPEs = 3
+# 初始化rknn池
+pool = rknnPoolExecutor(
+ rknnModel=modelPath,
+ TPEs=TPEs,
+ func=myFunc)
+
+# 初始化异步所需要的帧
+if (cap.isOpened()):
+ for i in range(TPEs + 1):
+ ret, frame = cap.read()
+ if not ret:
+ cap.release()
+ del pool
+ exit(-1)
+ pool.put(frame)
+
+frames, loopTime, initTime = 0, time.time(), time.time()
+while (cap.isOpened()):
+ frames += 1
+ ret, frame = cap.read()
+ if not ret:
+ break
+ pool.put(frame)
+ frame, flag = pool.get()
+ if flag == False:
+ break
+ cv2.imshow('test', frame)
+ if cv2.waitKey(1) & 0xFF == ord('q'):
+ break
+ if frames % 30 == 0:
+ print("30帧平均帧率:\t", 30 / (time.time() - loopTime), "帧")
+ loopTime = time.time()
+
+print("总平均帧率\t", frames / (time.time() - initTime))
+# 释放cap和rknn线程池
+cap.release()
+cv2.destroyAllWindows()
+pool.release()
diff --git a/rknn-multi-threaded-nosigmoid/performance.sh b/rknn-multi-threaded-nosigmoid/performance.sh
new file mode 100644
index 0000000..325c918
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/performance.sh
@@ -0,0 +1,31 @@
+# 请切换到root用户
+
+# CPU定频
+echo "CPU0-3 可用频率:"
+sudo cat /sys/devices/system/cpu/cpufreq/policy0/scaling_available_frequencies
+sudo echo userspace > /sys/devices/system/cpu/cpufreq/policy0/scaling_governor
+sudo echo 1800000 > /sys/devices/system/cpu/cpufreq/policy0/scaling_setspeed
+echo "CPU0-3 当前频率:"
+sudo cat /sys/devices/system/cpu/cpufreq/policy0/cpuinfo_cur_freq
+
+echo "CPU4-5 可用频率:"
+sudo cat /sys/devices/system/cpu/cpufreq/policy4/scaling_available_frequencies
+sudo echo userspace > /sys/devices/system/cpu/cpufreq/policy4/scaling_governor
+sudo echo 2400000 > /sys/devices/system/cpu/cpufreq/policy4/scaling_setspeed
+echo "CPU4-5 当前频率:"
+sudo cat /sys/devices/system/cpu/cpufreq/policy4/cpuinfo_cur_freq
+
+echo "CPU6-7 可用频率:"
+sudo cat /sys/devices/system/cpu/cpufreq/policy6/scaling_available_frequencies
+sudo echo userspace > /sys/devices/system/cpu/cpufreq/policy6/scaling_governor
+sudo echo 2400000 > /sys/devices/system/cpu/cpufreq/policy6/scaling_setspeed
+echo "CPU6-7 当前频率:"
+sudo cat /sys/devices/system/cpu/cpufreq/policy6/cpuinfo_cur_freq
+
+# NPU定频
+echo "NPU 可用频率:"
+sudo cat /sys/class/devfreq/fdab0000.npu/available_frequencies
+sudo echo userspace > /sys/class/devfreq/fdab0000.npu/governor
+sudo echo 1000000000 > /sys/class/devfreq/fdab0000.npu/userspace/set_freq
+echo "NPU 当前频率:"
+sudo cat /sys/class/devfreq/fdab0000.npu/cur_freq
\ No newline at end of file
diff --git a/rknn-multi-threaded-nosigmoid/rkcat.sh b/rknn-multi-threaded-nosigmoid/rkcat.sh
new file mode 100644
index 0000000..66abb15
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/rkcat.sh
@@ -0,0 +1,5 @@
+# 查看温度
+sensors
+# 查看NPU占用
+echo "当前NPU占用:"
+sudo cat /sys/kernel/debug/rknpu/load
diff --git a/rknn-multi-threaded-nosigmoid/rknnModel/cls.rknn b/rknn-multi-threaded-nosigmoid/rknnModel/cls.rknn
new file mode 100644
index 0000000..eca9557
Binary files /dev/null and b/rknn-multi-threaded-nosigmoid/rknnModel/cls.rknn differ
diff --git a/rknn-multi-threaded-nosigmoid/rknnModel/cls5.rknn b/rknn-multi-threaded-nosigmoid/rknnModel/cls5.rknn
new file mode 100644
index 0000000..054e4be
Binary files /dev/null and b/rknn-multi-threaded-nosigmoid/rknnModel/cls5.rknn differ
diff --git a/rknn-multi-threaded-nosigmoid/rknnModel/obb.rknn b/rknn-multi-threaded-nosigmoid/rknnModel/obb.rknn
new file mode 100644
index 0000000..5b56d23
Binary files /dev/null and b/rknn-multi-threaded-nosigmoid/rknnModel/obb.rknn differ
diff --git a/rknn-multi-threaded-nosigmoid/rknnModel/yolo11.rknn b/rknn-multi-threaded-nosigmoid/rknnModel/yolo11.rknn
new file mode 100644
index 0000000..9308156
Binary files /dev/null and b/rknn-multi-threaded-nosigmoid/rknnModel/yolo11.rknn differ
diff --git a/rknn-multi-threaded-nosigmoid/rknnModel/yolo11n.rknn b/rknn-multi-threaded-nosigmoid/rknnModel/yolo11n.rknn
new file mode 100644
index 0000000..8f5c9a5
Binary files /dev/null and b/rknn-multi-threaded-nosigmoid/rknnModel/yolo11n.rknn differ
diff --git a/rknn-multi-threaded-nosigmoid/rknnModel/yolov5s_relu_tk2_RK3588_i8.rknn b/rknn-multi-threaded-nosigmoid/rknnModel/yolov5s_relu_tk2_RK3588_i8.rknn
new file mode 100644
index 0000000..3efd367
Binary files /dev/null and b/rknn-multi-threaded-nosigmoid/rknnModel/yolov5s_relu_tk2_RK3588_i8.rknn differ
diff --git a/rknn-multi-threaded-nosigmoid/rknnpool.py b/rknn-multi-threaded-nosigmoid/rknnpool.py
new file mode 100644
index 0000000..58f77a1
--- /dev/null
+++ b/rknn-multi-threaded-nosigmoid/rknnpool.py
@@ -0,0 +1,59 @@
+from queue import Queue
+from rknnlite.api import RKNNLite
+from concurrent.futures import ThreadPoolExecutor, as_completed
+
+
+def initRKNN(rknnModel="./rknnModel/yolov5s.rknn", id=0):
+ rknn_lite = RKNNLite()
+ ret = rknn_lite.load_rknn(rknnModel)
+ if ret != 0:
+ print("Load RKNN rknnModel failed")
+ exit(ret)
+ if id == 0:
+ ret = rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0)
+ elif id == 1:
+ ret = rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_1)
+ elif id == 2:
+ ret = rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_2)
+ elif id == -1:
+ ret = rknn_lite.init_runtime(core_mask=RKNNLite.NPU_CORE_0_1_2)
+ else:
+ ret = rknn_lite.init_runtime()
+ if ret != 0:
+ print("Init runtime environment failed")
+ exit(ret)
+ print(rknnModel, "\t\tdone")
+ return rknn_lite
+
+
+def initRKNNs(rknnModel="./rknnModel/yolov5s.rknn", TPEs=1):
+ rknn_list = []
+ for i in range(TPEs):
+ rknn_list.append(initRKNN(rknnModel, i % 3))
+ return rknn_list
+
+
+class rknnPoolExecutor():
+ def __init__(self, rknnModel, TPEs, func):
+ self.TPEs = TPEs
+ self.queue = Queue()
+ self.rknnPool = initRKNNs(rknnModel, TPEs)
+ self.pool = ThreadPoolExecutor(max_workers=TPEs)
+ self.func = func
+ self.num = 0
+
+ def put(self, frame):
+ self.queue.put(self.pool.submit(
+ self.func, self.rknnPool[self.num % self.TPEs], frame))
+ self.num += 1
+
+ def get(self):
+ if self.queue.empty():
+ return None, False
+ fut = self.queue.get()
+ return fut.result(), True
+
+ def release(self):
+ self.pool.shutdown()
+ for rknn_lite in self.rknnPool:
+ rknn_lite.release()