YOLO模型实战:4mm产品检测3秒搞定!分割法实现490个目标零漏检

需求:
产品很小(4*4mm),需要人工一盘盘检查是否有缺失少放之类的,一线人员反馈看一两盘没问题,看多了之后眼睛会花,想要改善这种作业方式。

一盘有490个产品,考虑到产品小,特征不好提取,于是购买了海康的2000万像素CCD相机和16mm焦距镜头,经过实测,发现放大后,产品还是比较模糊,至少字体看不清,暂时先不管了,训练后看看结果怎么样。

过程

一开始收集了不同角度不同光线的图片进行训练,大概20多张,100多个标注点,训练后识别效果不理想,以为是训练数据不够,又不断增加,增加到60多张图,标注点400多个,还是不行,最后又加到100张图,标注点1000多个,每次出来的结果都是和下面那张图一样,部分能识别,部分无法识别。

怎么识别都没法识别全部产品
怎么识别都识别不了全部产品

 没办法,只好求助deepseek,得到的结论有几个可能:
1、可能是物体太小,解析度不行,所以训练在多图也无意义

2、训练尺寸太小,需要加大,比如1280、1920、以及更大的尺寸

3、训练图不够,训练特征收集的过少

4、模型不行,更换识别能力更好的模型

根据这几点又一一验证了下:

① 首先物体太小,那我就不识别整盘,把镜头拉近,这样解析度高很多,可以看清产品上的文字了,再次去识别的时候,发现有效果,但是还会缺失一些标注,又倒腾了大半天,还是没法做到识别全部。

② 训练图太小,训练时我分别使用了640、1280、1920、以及更高尺寸的2000-3000多的尺寸去训练,结果还是无法做到100%全部识别

③ 训练图太少,上面提到过,就是增加更多的图片去训练,有高清的、模糊的、倾斜的、反光的,反正各个角度都放了,还是一样不行

④ 模型不行,一开始 是使用yolov8系列的模型,不断从yolov8n到最吃资源的yolov8x ,再从yolov11n到yolov11x系列,识别效果都不行。

有点绷不住了,研究了好几天都不行,没达到预期效果。

最终的办法

还得是AI,最终采用的办法是分割的方式去实现,我就在想着,之前把镜头拉近了产品,解析度更高,为什么也没法识别全部呢?等会就有答案了

根据AI的方法,就是先切分再合并输出:

一开始试了下,半边图识别的效果也一般般,没法全部识别。 

为了验证AI的方法,我又重新按不同参数进行训练各种模型,直接上训练代码,主体不变,主要是imgsz会变,每次训练都会从640、1280、1920进行训练,更高的没试了,训练太费时间。

batch记得改动,尺寸越大,资源消耗越大,容易出现资源不足停止训练,所以尺寸越大,batch要越小,训练的时候可以看到内存占用的逐步调整。

######直接在python中训练的
# 导入ultralytics库中的YOLO类(用于目标检测模型)
# 如果提示无法导入,需要先安装该库:pip install ultralytics
from ultralytics import YOLO
# 导入PyTorch库(YOLO底层依赖的深度学习框架)
#import torch

# 主程序入口
if __name__ == "__main__":
    # 加载预训练的YOLO模型,模型文件路径为:
    # D:/点数识别/model/yolo11n.pt
    model = YOLO('D:/点数识别/model/yolo11n.pt')
    
    # 开始训练模型,参数说明:
    model.train(
        # 数据集配置文件路径,包含训练/验证数据的路径和类别信息
        data='D:/点数识别/data.yaml',
        # 训练轮数(整个数据集会被遍历330次)
        epochs=330,
        # 输入图像的尺寸(640x640像素)
        imgsz=640,
        # 每个批次的样本数量(16张图像)
        batch=16,
        # 使用GPU进行训练(CUDA设备0)
        device='cuda:0',
        # 训练结果保存的项目目录
        project='D:/点数识别/my_train',
        # 当前实验的名称(用于区分不同训练实验)
        name='exp_small_objects_640',
        # 边界框损失权重(控制检测框回归的强度)
        box=3,
        # 马赛克数据增强的概率(30%的概率使用马赛克增强)
        mosaic=0.3,
        # 使用矩形训练(优化内存使用,不填充图像)
        rect=True,
        )

每个模型训练完毕后,我都会使用一张裁剪后的图去验证,直至验证全部识别,看哪个模型识别成功率高,最后经过实测,1280尺寸的识别成功率最高,1920尺寸还会出现个别无法识别。

##完整的分割识别再合并代码
##感谢deepseek提供的技术支持
def initialize_components():
    """
    初始化模型和检测器组件
    """
    global model
    
    # 加载YOLO目标检测模型
    model_path = os.path.join(os.path.dirname(__file__), '../model/yolov8n.pt')
    model = YOLO(model_path)
    print('组件初始化完成')

def process_prediction(image_path):
    # 改为从图片文件读取
   # image_path = os.path.join(current_dir, 'test.jpg')  # 默认输入图片路径
    if not os.path.exists(image_path):
        print(f"未找到输入图片: {image_path}")
        exit()

    # 读取图片
    frame = cv2.imread(image_path)
    if frame is None:
        print("无法读取图片文件")
        exit()

    # 将图片对半切分
    height, width = frame.shape[:2]
    half_width = width // 2
    left_img = frame[:, :half_width]
    right_img = frame[:, half_width:]

    # 计算识别时间
    start_detect = time.time()

    # 对左半部分进行检测
    model.to('cuda')
    left_results = model.predict(
        source=left_img,
        conf=0.05,
        iou=0.1,
        imgsz=1280,
        rect=True,
        device='cuda'
    )
    # 创建输出目录(改为相对路径)
    soft_path=r'D:\点数识别\results'
    os.makedirs(soft_path, exist_ok=True)


    # 对右半部分进行检测
    right_results = model.predict(
        source=right_img,
        conf=0.05,
        iou=0.1,
        imgsz=1280,
        rect=True,
        device='cuda'
    )

    end_detect = time.time()
    # print(f'图片识别耗时: {end_detect - start_detect:.2f}秒')

    # 初始化计数器
    left_count = 0
    right_count = 0

    # 处理左半部分检测结果
    for result in left_results:
        left_count += len(result.boxes)
        for box in result.boxes:
            x1 = int(box.xyxy[0][0])
            y1 = int(box.xyxy[0][1])
            x2 = int(box.xyxy[0][2])
            y2 = int(box.xyxy[0][3])
            
            cls_id = int(box.cls[0])
            conf = float(box.conf[0])
            label = f'{result.names[cls_id]} {conf:.2f}'
            
            cv2.rectangle(left_img, (x1, y1), (x2, y2), (0, 255, 0), 1)
            cv2.putText(left_img, label, (x1, y1-10), 
                        cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0,255,0), 1)

    # 处理右半部分检测结果
    for result in right_results:
        right_count += len(result.boxes)
        for box in result.boxes:
            x1 = int(box.xyxy[0][0])
            y1 = int(box.xyxy[0][1])
            x2 = int(box.xyxy[0][2])
            y2 = int(box.xyxy[0][3])
            
            cls_id = int(box.cls[0])
            conf = float(box.conf[0])
            label = f'{result.names[cls_id]} {conf:.2f}'
            
            cv2.rectangle(right_img, (x1, y1), (x2, y2), (0, 255, 0), 1)
            cv2.putText(right_img, label, (x1, y1-10), 
                        cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0,255,0), 1)

    # 输出统计结果
    total_count = left_count + right_count
    print(f'左半部分识别数量: {left_count}')
    print(f'右半部分识别数量: {right_count}')
    print(f'识别总数量: {total_count}')
    
    # 保存切分后的检测结果
    left_output = os.path.join(soft_path, 'left_detection.jpg')
    right_output = os.path.join(soft_path, 'right_detection.jpg')
    cv2.imwrite(left_output, left_img)
    cv2.imwrite(right_output, right_img)

    # 合并显示结果
    combined = cv2.hconcat([left_img, right_img])
    combined_output = os.path.join(soft_path, 'combined_detection.jpg')
    cv2.imwrite(combined_output, combined)

    print(f'左半部分检测结果已保存至: {left_output}')
    print(f'右半部分检测结果已保存至: {right_output}')
    print(f'合并后的检测结果已保存至: {combined_output}')
    
    return left_count, right_count, total_count
左半边检测结果​​​​​
右半边检测结果

合并输出拼接图片

效果很OK, 由于没做定位,放上去检测的时候,对半分割时可能把产品分割成两半了,导致识别异常,这种改善比较简单,做个L型定位,然后放上就移动到一侧,这样分割的时候就不会分割到产品了,也不影响效率。

最终的用法,由于技术不行,不会做到两个程序中的内存交互,只能通过笨方法,存储到文件夹中:

 上位机界面,黑色部分是摄像头识别产品区域,490是指这个产品满盘标准数量,如果不达标就是数量异常,会弹窗提醒,并由用户确认

上位机显示界面

以上设计完成, 并验证可用,识别速度首次因为需要加载模型,所以第一次大概3-5秒左右,识别过程中保持在3秒全部识别完毕,共计490个目标,采用3060显卡,CPU的话识别慢一点,大概5-8秒左右,还有很大的优化空间。

思考
为什么之前也采用了局部画面去识别,但是没法识别全部呢?后续采用了分割的方式就能识别全部呢?
首先我认为最开始局部去识别的时候,是把镜头拉近,导致目标很清晰,也就有很多特征,所以特征太多了,识别能力反而下降部分。

如果是全图的照片去分割,因为全图距离目标比较远,特征很多都模糊的,所以细节少很多,也就是识别起来没那么多特征信息,所以识别效果很好,就像是一个不近视的和近视的人,不近视的人,看的清很多细节,就知道这个人叫什么名字,而近视的人,看到的只是人,却不知对方叫什么名,我们刚好需要的就是这种,只需要知道目标就行,不需要知道目标具体叫什么,所以模模糊糊的识别效果反而好很多。

 还有一点疑问的猜想
为什么同一个模型,在图片都没有压缩的情况下,整图识别和分割识别会差那么多呢?
我想整图识别的话会在识别过程中整体压缩到1280尺寸,细节丢失很多,但是分割后再压缩成1280尺寸,那么细节丢失会减少一半,还有一种可能性,就是yolo识别目标的上限是一次性达不到400多个,太多了就会丢失识别过程。

*本人非视觉专业或算法专业,纯粹业余人员,如有错误请指出修正,或有更好的建议可以分享优化,一起进步

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值