使用 YOLOv5 进行图像分割的实操案例

简介: 使用 YOLOv5 进行图像分割的实操案例

如何训练 YOLOv5 进行分割?简单来讲,包括几个步骤:


  • 为图像分割准备数据集
  • 在自定义数据集上训练 YOLOv5
  • 使用 YOLOv5 进行推理


准备数据集


第一步,您需要以适当的格式准备数据集。这种格式与用于检测的 YOLOv5 格式非常相似。您需要创建类似如下所示的目录:


让我们看一下 data.yaml 文件的内部。该文件具有与检测任务相同的结构。其结构如下图所示:

data.yaml 文件的结构


train - path to your train images
val - path to your validation images
nc - number of classes
names - сlass names

让我们看一下 .txt 文件的内部。

第一个元素是“0”,表示类别数。下一个值是多边形的 x 和 y 坐标。这些坐标被归一化为原始图像的大小。如果你想查看带有这个多边形的图像,可以使用下面的函数。第一个打开图像和标记文件,第二个显示图像和标记。

    def read_image_label(path_to_img: str, path_to_txt: str, normilize: bool = False) -> Tuple[np.array, np.array]:
        # read image
        image = cv2.imread(path_to_img)
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        img_h, img_w = image.shape[:2]
        # read .txt file for this image
        with open(path_to_txt, "r") as f:
            txt_file = f.readlines()[0].split()
            cls_idx = txt_file[0]
            coords = txt_file[1:]
            polygon = np.array([[eval(x), eval(y)] for x, y in zip(coords[0::2], coords[1::2])]) # convert list of coordinates to numpy massive
        # Convert normilized coordinates of polygons to coordinates of image
        if normilize:
            polygon[:,0] = polygon[:,0]*img_w
            polygon[:,1] = polygon[:,1]*img_h
        return image, polygon.astype(np.int)
    def show_image_mask(img: np.array, polygon: np.array, alpha: float = 0.7):
        # Create zero array for mask
        mask = np.zeros((img.shape[0], img.shape[1]), dtype=np.uint8)
        overlay = img.copy()
        # Draw polygon on the image and mask
        cv2.fillPoly(mask, pts=[polygon], color=(255, 255, 255))
        cv2.fillPoly(img, pts=[polygon], color=(255, 0, 0))
        cv2.addWeighted(overlay, alpha, image, 1 - alpha, 0, image)
        # Plot image with mask
        fig = plt.figure(figsize=(22,18))
        axes = fig.subplots(nrows=1, ncols=2)
        axes[0].imshow(img)
        axes[1].imshow(mask, cmap="Greys_r")
        axes[0].set_title("Original image with mask")
        axes[1].set_title("Mask")
        plt.show()
    

    经过上述代码处理后的结果如下所示:

    在某些情况下,您可能没有多边形数据,但有二进制掩码。因此拥有将二进制掩码转换为多边形的功能将很有用。此类功能的示例如下所示:

      def mask_to_polygon(mask: np.array, report: bool = False) -> List[int]:
          contours, _ = cv2.findContours(mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
          polygons = []
          for object in contours:
              coords = []
              for point in object:
                  coords.append(int(point[0][0]))
                  coords.append(int(point[0][1]))
              polygons.append(coords)
          if report:
              print(f"Number of points = {len(polygons[0])}")
          return np.array(polygons).ravel().tolist()
      polygons = mask_to_polygon(mask, report=True)

      得到结果如下:


      Number of points: 1444

      其中 x 和 y 坐标分别为 722 个点。


      原则上,我们可以继续在此基础上训练模型,但我想举一个例子,说明另一个函数,它可以减少将掩码转换为多边形后获得的点的数目。当您不想突出显示具有太多点的对象时,这很有用。

        def reduce_polygon(polygon: np.array, angle_th: int = 0, distance_th: int = 0) -> np.array(List[int]):
            angle_th_rad = np.deg2rad(angle_th)
            points_removed = [0]
            while len(points_removed):
                points_removed = list()
                for i in range(0, len(polygon)-2, 2):
                    v01 = polygon[i-1] - polygon[i]
                    v12 = polygon[i] - polygon[i+1]
                    d01 = np.linalg.norm(v01)
                    d12 = np.linalg.norm(v12)
                    if d01 < distance_th and d12 < distance_th:
                        points_removed.append(i)
                        continue
                        angle = np.arccos(np.sum(v01*v12) / (d01 * d12))
                        if angle < angle_th_rad:
                            points_removed.append(i)
                polygon = np.delete(polygon, points_removed, axis=0)
            return polygon
        def show_result_reducing(polygon: List[List[int]]) -> List[Tuple[int, int]]:
            original_polygon = np.array([[x, y] for x, y in zip(polygon[0::2], polygon[1::2])])
            tic = time()
            reduced_polygon = reduce_polygon(original_polygon, angle_th=1, distance_th=20)
            toc = time()
            fig = plt.figure(figsize=(16,5))
            axes = fig.subplots(nrows=1, ncols=2)
            axes[0].scatter(original_polygon[:, 0], original_polygon[:, 1], label=f"{len(original_polygon)}", c='b', marker='x', s=2)
            axes[1].scatter(reduced_polygon[:, 0], reduced_polygon[:, 1], label=f"{len(reduced_polygon)}", c='b', marker='x', s=2)
            axes[0].invert_yaxis()
            axes[1].invert_yaxis()
            axes[0].set_title("Original polygon")
            axes[1].set_title("Reduced polygon")
            axes[0].legend()
            axes[1].legend()
            plt.show()
            print("\n\n", f'[bold black] Original_polygon length[/bold black]: {len(original_polygon)}\n', 
                  f'[bold black] Reduced_polygon length[/bold black]: {len(reduced_polygon)}\n'
                  f'[bold black]Running time[/bold black]: {round(toc - tic, 4)} seconds')
            return reduced_polygon

        函数的输出如下所示:

        x 和 y 分别有 722 个点。经过处理之后,x 和 y 分别变成了 200 点。


        至此,我们继续训练模型。


        在自定义数据集上训练 YOLOv5


        在这里,您需要执行以下步骤:

          git clone https://github.com/ultralytics/yolov5.git
          pip install -r requirements.txt

          当您将 YOLOv5 完整项目代码 git clone 到您本地并安装库后,您就可以开始学习过程了。此处有使用预训练模型。

            python3 se
            python3 segment/train.py 
            --data "/Users/vladislavefremov/Downloads/Instance_Segm_2/data.yaml"
            --weights yolov5s-seg.pt 
            --img 640 
            --batch-size 2 
            --epochs 50

            训练结束后,可以看看验证集上的结果:

            模型在验证集上的预测


            如果你想了解更多关于 YOLOv5 参数的信息,可以查看官方代码(https://github.com/ultralytics/yolov5


            使用 YOLOv5 推理


            我们已经训练了模型,现在我们可以从照片、包含照片的目录、视频、包含视频的目录等进行推理。


            让我们对一个视频进行推理,看看最后的结果。


            python3 segment/predict.py 
            --weights "/home/user/Disk/Whales/weights/whale_3360/weights/best.pt" 
            --source "/home/user/Disk/Whales/Video" 
            --imgsz 1280 
            --name video_whale

            得到视频地址:https://youtu.be/_j8sA6VUil4


            推理后得到的结果是什么形式?多边形且含有类索引 x 和 y 坐标的绝对值。


            结论


            在本文中,我们研究了如何为 YOLOv5 算法的分割准备数据;快速将 mask 矩阵转换为多边形的函数。我们了解了如何训练 YOLOv5 算法并在训练后进行推理。

            相关文章
            |
            9月前
            |
            编解码 缓存 并行计算
            YOLOv5入门实践(4)——手把手教你训练自己的数据集
            YOLOv5入门实践(4)——手把手教你训练自己的数据集
            1171 0
            YOLOv5入门实践(4)——手把手教你训练自己的数据集
            |
            6天前
            |
            机器学习/深度学习 存储 算法
            【轻量化:实操】动手实现神经网络中的裁枝操作(附演示代码&yolo系列)
            【轻量化:实操】动手实现神经网络中的裁枝操作(附演示代码&yolo系列)
            99 1
            |
            9月前
            |
            机器学习/深度学习 算法 PyTorch
            深度学习实践篇 第四章:模型训练与示例
            简要介绍pytorch中模型训练的流程和部分原理知识。
            |
            9月前
            |
            XML 计算机视觉 数据格式
            YOLOv5入门实践(2)——手把手教你利用labelimg标注数据集
            YOLOv5入门实践(2)——手把手教你利用labelimg标注数据集
            3263 0
            YOLOv5入门实践(2)——手把手教你利用labelimg标注数据集
            |
            9月前
            |
            XML 数据采集 存储
            Yolo-V5目标检测 项目实战
            Yolo-V5目标检测 项目实战
            |
            10月前
            |
            机器学习/深度学习 数据库 计算机视觉
            图像分类基础与实战
            图像分类基础与实战(1)
            |
            10月前
            |
            异构计算
            |
            10月前
            |
            机器学习/深度学习 编解码 自然语言处理
            |
            10月前
            |
            机器学习/深度学习 人工智能 计算机视觉
            【YOLOv8】实战一:手把手教你使用YOLOv8实现实时目标检测
            【YOLOv8】实战一:手把手教你使用YOLOv8实现实时目标检测
            10794 0
            【YOLOv8】实战一:手把手教你使用YOLOv8实现实时目标检测
            |
            机器学习/深度学习 人工智能 资源调度
            如何用YOLOv5玩转半监督(附源码实现)
            如何用YOLOv5玩转半监督(附源码实现)
            306 0