first commit
5
.idea/.gitignore
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# 默认忽略的文件
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# 基于编辑器的 HTTP 客户端请求
|
||||||
|
/httpRequests/
|
||||||
12
.idea/ML_xiantiao.iml
generated
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="PYTHON_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="jdk" jdkName="yolov11" jdkType="Python SDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
<component name="PyDocumentationSettings">
|
||||||
|
<option name="format" value="PLAIN" />
|
||||||
|
<option name="myDocStringFormat" value="Plain" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
6
.idea/inspectionProfiles/profiles_settings.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<settings>
|
||||||
|
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||||
|
<version value="1.0" />
|
||||||
|
</settings>
|
||||||
|
</component>
|
||||||
7
.idea/misc.xml
generated
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Black">
|
||||||
|
<option name="sdkName" value="yolov11" />
|
||||||
|
</component>
|
||||||
|
<component name="ProjectRootManager" version="2" project-jdk-name="yolov11" project-jdk-type="Python SDK" />
|
||||||
|
</project>
|
||||||
8
.idea/modules.xml
generated
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/ML_xiantiao.iml" filepath="$PROJECT_DIR$/.idea/ML_xiantiao.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
63
camera_basler_capture_img/camera_basler_capture_test1img.py
Normal file
@ -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)
|
||||||
108
camera_basler_capture_img/camera_main.py
Normal file
@ -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()
|
||||||
BIN
class_xiantiao_pc/1.png
Normal file
|
After Width: | Height: | Size: 394 KiB |
BIN
class_xiantiao_pc/2.png
Normal file
|
After Width: | Height: | Size: 388 KiB |
93
class_xiantiao_pc/chose_ROI.py
Normal file
@ -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)
|
||||||
85
class_xiantiao_pc/class_xiantiao.py
Normal file
@ -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()
|
||||||
63
class_xiantiao_pc/cls_quexian.py
Normal file
@ -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()
|
||||||
115
class_xiantiao_pc/detect.py
Normal file
@ -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("产品存在缺陷")
|
||||||
108
class_xiantiao_pc/divid_val.py
Normal file
@ -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 # 随机种子
|
||||||
|
)
|
||||||
71
class_xiantiao_pc/image_test.py
Normal file
@ -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)
|
||||||
57
class_xiantiao_pc/resize_dataset_image.py
Normal file
@ -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()
|
||||||
1
class_xiantiao_pc/roi_1/1/1_rois1.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
3,0,694,182
|
||||||
BIN
class_xiantiao_pc/test_image/train.cache
Normal file
BIN
class_xiantiao_pc/test_image/train/1.jpg
Normal file
|
After Width: | Height: | Size: 564 KiB |
1
class_xiantiao_pc/test_image/train/1.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 0.614113 0.455275 0.664973 0.245135
|
||||||
BIN
class_xiantiao_pc/test_image/train/1(另一个复件).jpg
Normal file
|
After Width: | Height: | Size: 564 KiB |
1
class_xiantiao_pc/test_image/train/1(另一个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 0.614113 0.455275 0.664973 0.245135
|
||||||
BIN
class_xiantiao_pc/test_image/train/1(复件).jpg
Normal file
|
After Width: | Height: | Size: 564 KiB |
1
class_xiantiao_pc/test_image/train/1(复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 0.614113 0.455275 0.664973 0.245135
|
||||||
BIN
class_xiantiao_pc/test_image/train/1(第 3 个复件).jpg
Normal file
|
After Width: | Height: | Size: 564 KiB |
1
class_xiantiao_pc/test_image/train/1(第 3 个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 0.614113 0.455275 0.664973 0.245135
|
||||||
BIN
class_xiantiao_pc/test_image/train/1(第 4 个复件).jpg
Normal file
|
After Width: | Height: | Size: 564 KiB |
1
class_xiantiao_pc/test_image/train/1(第 4 个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 0.614113 0.455275 0.664973 0.245135
|
||||||
BIN
class_xiantiao_pc/test_image/train/1(第 5 个复件).jpg
Normal file
|
After Width: | Height: | Size: 564 KiB |
1
class_xiantiao_pc/test_image/train/1(第 5 个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1 0.614113 0.455275 0.664973 0.245135
|
||||||
BIN
class_xiantiao_pc/test_image/train/2.jpg
Normal file
|
After Width: | Height: | Size: 740 KiB |
2
class_xiantiao_pc/test_image/train/2.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
0 0.325184 0.190563 0.102830 0.112333
|
||||||
|
0 0.532322 0.519080 0.408382 0.188687
|
||||||
BIN
class_xiantiao_pc/test_image/train/2(另一个复件).jpg
Normal file
|
After Width: | Height: | Size: 740 KiB |
2
class_xiantiao_pc/test_image/train/2(另一个复件).txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
0 0.325184 0.190563 0.102830 0.112333
|
||||||
|
0 0.532322 0.519080 0.408382 0.188687
|
||||||
BIN
class_xiantiao_pc/test_image/train/2(复件).jpg
Normal file
|
After Width: | Height: | Size: 740 KiB |
2
class_xiantiao_pc/test_image/train/2(复件).txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
0 0.325184 0.190563 0.102830 0.112333
|
||||||
|
0 0.532322 0.519080 0.408382 0.188687
|
||||||
BIN
class_xiantiao_pc/test_image/train/2(第 3 个复件).jpg
Normal file
|
After Width: | Height: | Size: 740 KiB |
2
class_xiantiao_pc/test_image/train/2(第 3 个复件).txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
0 0.325184 0.190563 0.102830 0.112333
|
||||||
|
0 0.532322 0.519080 0.408382 0.188687
|
||||||
BIN
class_xiantiao_pc/test_image/train/2(第 4 个复件).jpg
Normal file
|
After Width: | Height: | Size: 740 KiB |
2
class_xiantiao_pc/test_image/train/2(第 4 个复件).txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
0 0.325184 0.190563 0.102830 0.112333
|
||||||
|
0 0.532322 0.519080 0.408382 0.188687
|
||||||
BIN
class_xiantiao_pc/test_image/train/2(第 5 个复件).jpg
Normal file
|
After Width: | Height: | Size: 740 KiB |
2
class_xiantiao_pc/test_image/train/2(第 5 个复件).txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
0 0.325184 0.190563 0.102830 0.112333
|
||||||
|
0 0.532322 0.519080 0.408382 0.188687
|
||||||
BIN
class_xiantiao_pc/test_image/train/3.jpg
Normal file
|
After Width: | Height: | Size: 516 KiB |
1
class_xiantiao_pc/test_image/train/3.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.670369 0.354613 0.214830 0.096716
|
||||||
BIN
class_xiantiao_pc/test_image/train/3(另一个复件).jpg
Normal file
|
After Width: | Height: | Size: 516 KiB |
1
class_xiantiao_pc/test_image/train/3(另一个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.670369 0.354613 0.214830 0.096716
|
||||||
BIN
class_xiantiao_pc/test_image/train/3(复件).jpg
Normal file
|
After Width: | Height: | Size: 516 KiB |
1
class_xiantiao_pc/test_image/train/3(复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.670369 0.354613 0.214830 0.096716
|
||||||
BIN
class_xiantiao_pc/test_image/train/3(第 3 个复件).jpg
Normal file
|
After Width: | Height: | Size: 516 KiB |
1
class_xiantiao_pc/test_image/train/3(第 3 个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.670369 0.354613 0.214830 0.096716
|
||||||
BIN
class_xiantiao_pc/test_image/train/3(第 4 个复件).jpg
Normal file
|
After Width: | Height: | Size: 516 KiB |
1
class_xiantiao_pc/test_image/train/3(第 4 个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.670369 0.354613 0.214830 0.096716
|
||||||
BIN
class_xiantiao_pc/test_image/train/3(第 5 个复件).jpg
Normal file
|
After Width: | Height: | Size: 516 KiB |
1
class_xiantiao_pc/test_image/train/3(第 5 个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.670369 0.354613 0.214830 0.096716
|
||||||
BIN
class_xiantiao_pc/test_image/train/4.jpg
Normal file
|
After Width: | Height: | Size: 921 KiB |
1
class_xiantiao_pc/test_image/train/4.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.535815 0.542963 0.570926 0.593823
|
||||||
BIN
class_xiantiao_pc/test_image/train/4(另一个复件).jpg
Normal file
|
After Width: | Height: | Size: 921 KiB |
1
class_xiantiao_pc/test_image/train/4(另一个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.535815 0.542963 0.570926 0.593823
|
||||||
BIN
class_xiantiao_pc/test_image/train/4(复件).jpg
Normal file
|
After Width: | Height: | Size: 921 KiB |
1
class_xiantiao_pc/test_image/train/4(复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.535815 0.542963 0.570926 0.593823
|
||||||
BIN
class_xiantiao_pc/test_image/train/4(第 3 个复件).jpg
Normal file
|
After Width: | Height: | Size: 921 KiB |
1
class_xiantiao_pc/test_image/train/4(第 3 个复件).txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0 0.535815 0.542963 0.570926 0.593823
|
||||||
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_205.jpg
Normal file
|
After Width: | Height: | Size: 794 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_206.jpg
Normal file
|
After Width: | Height: | Size: 622 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_207.jpg
Normal file
|
After Width: | Height: | Size: 751 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_208.jpg
Normal file
|
After Width: | Height: | Size: 723 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_209.jpg
Normal file
|
After Width: | Height: | Size: 724 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_210.jpg
Normal file
|
After Width: | Height: | Size: 755 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_211.jpg
Normal file
|
After Width: | Height: | Size: 727 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_212.jpg
Normal file
|
After Width: | Height: | Size: 654 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_213.jpg
Normal file
|
After Width: | Height: | Size: 675 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_214.jpg
Normal file
|
After Width: | Height: | Size: 708 KiB |
@ -0,0 +1 @@
|
|||||||
|
0 0.493643 0.417761 0.436794 0.658617
|
||||||
|
After Width: | Height: | Size: 708 KiB |
@ -0,0 +1 @@
|
|||||||
|
0 0.493643 0.417761 0.436794 0.658617
|
||||||
|
After Width: | Height: | Size: 708 KiB |
@ -0,0 +1 @@
|
|||||||
|
0 0.493643 0.417761 0.436794 0.658617
|
||||||
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_215.jpg
Normal file
|
After Width: | Height: | Size: 722 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_216.jpg
Normal file
|
After Width: | Height: | Size: 751 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_217.jpg
Normal file
|
After Width: | Height: | Size: 718 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_218.jpg
Normal file
|
After Width: | Height: | Size: 757 KiB |
BIN
class_xiantiao_pc/test_image/train/微信图片_20251216095823_219.jpg
Normal file
|
After Width: | Height: | Size: 643 KiB |
@ -0,0 +1 @@
|
|||||||
|
1 0.682482 0.234558 0.050639 0.296622
|
||||||
|
After Width: | Height: | Size: 643 KiB |
@ -0,0 +1 @@
|
|||||||
|
1 0.682482 0.234558 0.050639 0.296622
|
||||||
|
After Width: | Height: | Size: 643 KiB |