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()