【深度学习】ubuntu系统下docker部署cvat的自动标注功能(yolov8 segmentation)

news2024/11/22 16:31:27

cvat部署自动标注教程

  • 前言
  • step1. 拷贝yolov8项目
  • step2. 创建yolov8的本地镜像
  • step3. 在cvat中构建我们的工作空间

前言

  安装docker和cvat的流程我这里就不赘述了,这样的教程还是挺多的,但是对于使用docker在cvat上部署自动标注算法的整个详细流程,网上可查到的资料可以说几乎没有,我在踩了几天的坑之后,摸索出来一套流程,现记录于此,方便自己也方便有需要的朋友查看。

step1. 拷贝yolov8项目

  在github上git clone yolov8的项目,将其整个拷贝到我们的ubuntu系统中,比如我的yolov8的项目就放在这个路径下:在这里插入图片描述

step2. 创建yolov8的本地镜像

  这一步需要我们写一个Dockerfile来创建v8项目的本地镜像,这样我们在cvat里构建项目的时候就可以将我们的main.py和main_handler.py与我们的源代码项目关联起来,这样我们就可以自由的import我们所需要的项目中的类和方法了(即使不在v8的项目路径下)。这个Dockerfile需要放在yolov8项目的主目录下,那么这个Dockerfile应该怎么写呢?这里我给出了一个我的一个模板:
在这里插入图片描述
这里的FROM 的意思就是我们所需要映射的v8的环境镜像,没有环境我们是运行不了代码的,FROM后面的地址可以在这个网站上进行拉取:ultralytics,我们点击这里的copy即可复制命令:
在这里插入图片描述
  在这一步有可能有很多小伙伴无法成功拉取,多半是因为没有设置一个合适的源,我们在终端中进入 etc/docker/daemon.json中(如果没有这个就创建一个),然后添加以下内容:

{
  "registry-mirrors": ["https://docker.1panel.live"]
}

或者:

{
    "registry-mirrors": [
            "https://docker.211678.top",
            "https://docker.1panel.live",
            "https://hub.rat.dev",
            "https://docker.m.daocloud.io",
            "https://do.nark.eu.org",
            "https://dockerpull.com",
            "https://dockerproxy.cn",
            "https://docker.awsl9527.cn"
      ]
}

然后我们保存一下,然后重启一下docker服务:

systemctl restart docker

  然后将终端打开,将镜像拉到我们本地,然后我们可以通过在终端输入指令:docker images 来查看我们的镜像是否pull下来:
在这里插入图片描述
可以看到我们已经拉好了这个镜像,此时我们回到刚才我们创建好Dockerfile的那个路径下,这里我的Dockerfile名称为Dockerfile_cvat,名字是什么不重要:
在这里插入图片描述
然后我们在这个路径下打开一个终端,输入指令:
docker build -t yolov8_seg -f Dockerfile_cvat .
即可创建一个本地的项目镜像了。同样我们可以使用docker images指令来查看是否生成成功。

step3. 在cvat中构建我们的工作空间

  进入cvat/serverless/pytorch 路径下,然后新建一个叫yolov8的文件夹,然后在里面再新建一个叫nuclio的文件夹,这里面新建一个function-gpu.yaml的文件:
在这里插入图片描述
然后便是准备我们的main.py了,cvat会在工作空间的这个py脚本中寻找init_context方法和handler方法(前者用来初始化模型等,后者用来推理并返回cvat可以接收的结果)。以下是我的v8 seg的推理代码:

import cv2
import torch
import numpy as np
import torch.nn.functional as F
from ultralytics.data.augment import LetterBox
from ultralytics.nn.autobackend import AutoBackend
import glob
import json
import base64
from PIL import Image
import io

def convert_mask_to_polygon(mask, original_image):
    contours = None
    mask_h, mask_w = mask.shape[0], mask.shape[1]
    img_h, img_w = original_image.shape[0], original_image.shape[1]
    h_ratio, w_ratio = mask_h / img_h, mask_w / img_w
    mask = cv2.resize(mask, (img_w, img_h))

    if int(cv2.__version__.split('.')[0]) > 3:
        # contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)[0]
        contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0]

    else:
        # contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_TC89_KCOS)[1]
        contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1]

    contours = max(contours, key=lambda arr: arr.size)
    if contours.shape.count(1):
        contours = np.squeeze(contours)
    if contours.size < 3 * 2:
        raise Exception('Less then three point have been detected. Can not build a polygon')

    polygon = []
    for point in contours:
        polygon.append(np.array([int(point[0]), int(point[1])]))

    return np.array(polygon)

def preprocess_letterbox(image):
    letterbox = LetterBox(new_shape=640, stride=32, auto=True)
    image = letterbox(image=image)
    image = (image[..., ::-1] / 255.0).astype(np.float32)  # BGR to RGB, 0 - 255 to 0.0 - 1.0
    image = image.transpose(2, 0, 1)[None]  # BHWC to BCHW (n, 3, h, w)
    image = torch.from_numpy(image)
    return image


def preprocess_warpAffine(image, dst_width=640, dst_height=640):
    scale = min((dst_width / image.shape[1], dst_height / image.shape[0]))
    ox = (dst_width - scale * image.shape[1]) / 2
    oy = (dst_height - scale * image.shape[0]) / 2
    M = np.array([
        [scale, 0, ox],
        [0, scale, oy]
    ], dtype=np.float32)

    img_pre = cv2.warpAffine(image, M, (dst_width, dst_height), flags=cv2.INTER_LINEAR,
                             borderMode=cv2.BORDER_CONSTANT, borderValue=(114, 114, 114))
    IM = cv2.invertAffineTransform(M)

    # cv2.namedWindow("pshow", cv2.WINDOW_NORMAL)
    # cv2.imshow("pshow", img_pre)
    # cv2.waitKey(0)

    img_pre = (img_pre[..., ::-1] / 255.0).astype(np.float32)
    img_pre = img_pre.transpose(2, 0, 1)[None]
    img_pre = torch.from_numpy(img_pre)

    return img_pre, IM


def iou(box1, box2):
    def area_box(box):
        return (box[2] - box[0]) * (box[3] - box[1])

    left = max(box1[0], box2[0])
    top = max(box1[1], box2[1])
    right = min(box1[2], box2[2])
    bottom = min(box1[3], box2[3])
    cross = max((right - left), 0) * max((bottom - top), 0)
    union = area_box(box1) + area_box(box2) - cross
    if cross == 0 or union == 0:
        return 0
    return cross / union


def NMS(boxes, iou_thres):
    remove_flags = [False] * len(boxes)

    keep_boxes = []
    for i, ibox in enumerate(boxes):
        if remove_flags[i]:
            continue

        keep_boxes.append(ibox)
        for j in range(i + 1, len(boxes)):
            if remove_flags[j]:
                continue

            jbox = boxes[j]
            if (ibox[5] != jbox[5]):
                continue
            if iou(ibox, jbox) > iou_thres:
                remove_flags[j] = True
    return keep_boxes


def postprocess(pred, conf_thres=0.25, iou_thres=0.45):
    # 输入是模型推理的结果,即8400个预测框
    # 1,8400,116 [cx,cy,w,h,class*80,32]
    boxes = []
    for item in pred[0]:
        cx, cy, w, h = item[:4]
        label = item[4:-32].argmax()
        confidence = item[4 + label]
        if confidence < conf_thres:
            continue
        left = cx - w * 0.5
        top = cy - h * 0.5
        right = cx + w * 0.5
        bottom = cy + h * 0.5
        boxes.append([left, top, right, bottom, confidence, label, *item[-32:]])

    boxes = sorted(boxes, key=lambda x: x[4], reverse=True)

    return NMS(boxes, iou_thres)


def crop_mask(masks, boxes):
    # masks -> n, 160, 160  原始 masks
    # boxes -> n, 4         检测框,映射到 160x160 尺寸下的
    n, h, w = masks.shape
    x1, y1, x2, y2 = torch.chunk(boxes[:, :, None], 4, 1)  # x1 shape(n,1,1)
    r = torch.arange(w, device=masks.device, dtype=x1.dtype)[None, None, :]  # rows shape(1,1,w)
    c = torch.arange(h, device=masks.device, dtype=x1.dtype)[None, :, None]  # cols shape(1,h,1)

    return masks * ((r >= x1) * (r < x2) * (c >= y1) * (c < y2))


def process_mask(protos, masks_in, bboxes, shape, upsample=False):
    # protos   -> 32, 160, 160 分割头输出
    # masks_in -> n, 32        检测头输出的 32 维向量,可以理解为 mask 的权重
    # bboxes   -> n, 4         检测框
    # shape    -> 640, 640     输入网络中的图像 shape
    # unsample 一个 bool 值,表示是否需要上采样 masks 到图像的原始形状
    c, mh, mw = protos.shape  # CHW
    ih, iw = shape
    # 矩阵相乘 nx32 @ 32x(160x160) -> nx(160x160) -> sigmoid -> nx160x160
    masks = (masks_in.float() @ protos.float().view(c, -1)).sigmoid().view(-1, mh, mw)  # CHW

    downsampled_bboxes = bboxes.clone()
    downsampled_bboxes[:, 0] *= mw / iw
    downsampled_bboxes[:, 2] *= mw / iw
    downsampled_bboxes[:, 3] *= mh / ih
    downsampled_bboxes[:, 1] *= mh / ih

    masks = crop_mask(masks, downsampled_bboxes)  # CHW
    if upsample:
        masks = F.interpolate(masks[None], shape, mode='bilinear', align_corners=False)[0]  # CHW
    return masks.gt_(0.5)


def hsv2bgr(h, s, v):
    h_i = int(h * 6)
    f = h * 6 - h_i
    p = v * (1 - s)
    q = v * (1 - f * s)
    t = v * (1 - (1 - f) * s)

    r, g, b = 0, 0, 0

    if h_i == 0:
        r, g, b = v, t, p
    elif h_i == 1:
        r, g, b = q, v, p
    elif h_i == 2:
        r, g, b = p, v, t
    elif h_i == 3:
        r, g, b = p, q, v
    elif h_i == 4:
        r, g, b = t, p, v
    elif h_i == 5:
        r, g, b = v, p, q

    return int(b * 255), int(g * 255), int(r * 255)


def random_color(id):
    h_plane = (((id << 2) ^ 0x937151) % 100) / 100.0
    s_plane = (((id << 3) ^ 0x315793) % 100) / 100.0
    return hsv2bgr(h_plane, s_plane, 1)


def init_context(context):
    context.logger.info("Init context... 0%")

    model_path = "best_vessel.pt"
    model = AutoBackend(weights=model_path)
    context.user_data.model = model
    context.logger.info("Init context... 100%")

def handler(context, event):
    results = []
    context.logger.info("Run yolov8 model")
    data = event.body
    buf = io.BytesIO(base64.b64decode(data["image"]))
    # threshold = float(data.get("threshold", 0.5))
    # context.user_data.model.conf = threshold
    img = Image.open(buf)
    # img.save('output_123.jpg', 'JPEG')
    img = np.array(img.convert('RGB'))
    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
    # # img_pre = preprocess_letterbox(img)
    img_pre, IM = preprocess_warpAffine(img)
    names = context.user_data.model.names
    result = context.user_data.model(img_pre)

    output0 = result[0].transpose(-1, -2)  # 1,8400,116 检测头输出
    output1 = result[1][2][0]  # 32,160,160 分割头输出

    pred = postprocess(output0)
    det_num = len(pred)
    if det_num == 0:
        results.append({
            "confidence": str(0.0),
            "label": "plaque",
            "type": "polygon",
            "points": [0,0,0,0,0,0],
        })
    else:
        pred = torch.from_numpy(np.array(pred).reshape(-1, 38))

        masks = process_mask(output1, pred[:, 6:], pred[:, :4], img_pre.shape[2:], True)

        boxes = np.array(pred[:, :6])
        lr = boxes[:, [0, 2]]
        tb = boxes[:, [1, 3]]
        boxes[:, [0, 2]] = IM[0][0] * lr + IM[0][2]
        boxes[:, [1, 3]] = IM[1][1] * tb + IM[1][2]

        # draw mask
        h, w = img.shape[:2]
        for i, mask in enumerate(masks):
            mask = mask.cpu().numpy().astype(np.uint8)  # 640x640
            mask_resized = cv2.warpAffine(mask, IM, (w, h), flags=cv2.INTER_LINEAR)  # 1080x810

            #---------------------------
            #   得到轮廓的点,并且可视化分割轮廓
            #---------------------------
            mask_polygon = convert_mask_to_polygon(mask_resized, img)
            # cv2.polylines(img, [mask_polygon], True, (0, 255, 0), 2)
            l = mask_polygon.ravel().tolist()
            
            results.append({
                "confidence": str(boxes[i][4]),
                "label": names.get(int(boxes[i][5]), "unknown"),
                "type": "polygon",
                "points": l,
            })
    
    return context.Response(body=json.dumps(results), headers={}, content_type='application/json', status_code=200)
        

这是我的nuclio工作空间的内容,包含了上述所提到的function-gpu.yaml,main.py还有模型的权重:
在这里插入图片描述
最后我们cd到cvat/serverless路径下,然后打开终端,输入指令:
./deploy_gpu.sh pytorch/yolov8/plaquedetect/nuclio/
然后就会在cvat的nuclio中构建我们自动标注的功能,在终端会显示这个结果说明我们构建成功了:
在这里插入图片描述
然后我们打开nuclio的网页端可以看到已经是绿色 running的状态了:
在这里插入图片描述
然后我们进入cvat的主界面就可以开始自动标注啦~~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2168957.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【MySQL】MVCC及其实现原理

目录 1. 概念介绍 什么是MVCC 什么是当前读和快照读 MVCC的好处 2. MVCC实现原理 隐藏字段 Read View undo-log 数据可见性算法 3. RC和RR隔离级别下MVCC的差异 4. MVCC&#xff0b;Next-key-Lock 防止幻读 1. 概念介绍 什么是MVCC Multi-Version Concurrency Cont…

通信工程学习:什么是FDD频分双工

FDD:频分双工 FDD(频分双工,Frequency Division Duplexing)是一种无线通信技术,它通过将频谱划分为上行和下行两个不重叠的频段来实现同时双向通信。以下是FDD频分双工的详细解释: 一、定义与原理 定义: FDD是一种无线通信系统的工作模式,其中上行链路(从移动…

以Flask为基础的虾皮Shopee“曲线滑块验证码”识别系统部署

以Flask为基础的虾皮Shopee“曲线滑块验证码”识别系统部署 一、验证码类型二、简介三、Flask应用 一、验证码类型 验证码类型&#xff1a;此类验证码存在两个难点&#xff0c;一是有右侧有两个凹槽&#xff0c;二是滑块的运动轨迹不是直线的&#xff0c;而是沿着曲线走的&…

您的业​​务端点是否完全安全?

根据 2023 年数据泄露调查报告&#xff0c;52% 的数据泄露涉及凭证泄露。这令人担忧&#xff0c;不是吗&#xff1f; 在当今的数字世界中&#xff0c;企业严重依赖技术&#xff0c;保护您的设备&#xff08;端点&#xff09;至关重要。这些设备&#xff08;包括计算机、笔记本…

MySQL从入门到精通 - 基础篇

一、MySQL概述 1. 数据库相关概念 二、SQL &#xff08;1&#xff09;SQL通用语法 &#xff08;2&#xff09;SQL分类 &#xff08;3&#xff09;数据定义语言DDL 数据库操作 表操作 数据类型 1. 数值类型 2. 字符串类型 二进制数据&#xff1a;以二进制格式&#xff08;0和…

uniapp 知识点

自定义导航 在page.json navigationstyle":"custom"navigateTo传参 页面传参只能onLoad(option)里面拿 px和upx的关系 在750设计图中&#xff0c;1px1upx 路由 navigateBack返回上一页 重定向 其实就是把当前页面干掉了 公共组件和页面共同点 computed,watc…

基于微信小程序的智能汽车充电站系设计与实现(源码+定制+文档)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…

Spring Boot技术:构建高效网上购物平台

第3章 系统分析 3.1 可行性分析 在系统开发之初要进行系统可行分析&#xff0c;这样做的目的就是使用最小成本解决最大问题&#xff0c;一旦程序开发满足用户需要&#xff0c;带来的好处也是很多的。下面我们将从技术上、操作上、经济上等方面来考虑这个系统到底值不值得开发。…

【Vue】Vue3 的初始化过程

核心流程是patch&#xff0c;然后Patch有一个分支&#xff0c;分别处理组件和浏览器原生标签。分别对应processElement和processComponent&#xff0c;从上到下插入&#xff0c;知道处理完成&#xff0c;才把顶层div插入到浏览器。“一次性渲染&#xff0c;而不是一个个一个渲染…

[论文笔记] Chain-of-Thought Reasoning without Prompting

分析: 在CoT解码路径中,我们可以看到模型在第三个位置(𝑖? = 3)开始展示推理过程,并且给出了正确的答案“8”。模型首先识别出说话者有3个苹果,然后识别出爸爸比说话者多2个,即5个苹果,最后将这两个数量相加得到总数8个苹果。 这个例子表明,通过探索替代的解码路径…

【每天学个新注解】Day 7 Lombok注解简解(六)—@With

With 创建一个新的对象&#xff0c;该对象是当前对象的副本&#xff0c;但某些字段的值可以被更改。 1、如何使用 With 可以使用在类上&#xff0c;也可以使用在成员变量上。加在类上相当于给所有成员变量 With可以配合AccessLevel使用&#xff0c;创建出指定访问修饰符的wi…

多模态大模型学习(一)

参考&#xff1a;https://www.bilibili.com/video/BV1kT411o7a6?p2&spm_id_frompageDriver&vd_source156234c72054035c149dcb072202e6be 余弦相似度&#xff0c;让正样本内积趋近于1&#xff0c;负样本趋近于-1。度量学习。N特别大时&#xff0c;负样本远大于正样本&…

PHP之 实现https ssl证书到期提醒,通过企微发送消息

参考文章 https://blog.51cto.com/17099933344/1935194 https://blog.csdn.net/m0_37346206/article/details/127333463 https://www.cnblogs.com/tk-bolg/p/18108106 使用的企微接口 https://qyapi.weixin.qq.com/cgi-bin/message/send 查询 ssl证书到期时间 // ssl证书即将…

基于BiLSTM+Transformer混合模型实现交通流量时序预测(PyTorch版)

前言 系列专栏:【深度学习&#xff1a;算法项目实战】✨︎ 涉及医疗健康、财经金融、商业零售、食品饮料、运动健身、交通运输、环境科学、社交媒体以及文本和图像处理等诸多领域&#xff0c;讨论了各种复杂的深度神经网络思想&#xff0c;如卷积神经网络、循环神经网络、生成对…

2024年研究生数学建模“华为杯”E题——肘部法则、k-means聚类、目标检测(python)、ARIMA、逻辑回归、混淆矩阵(附:目标检测代码)

文章目录 一、情况介绍二、思路情况二、代码展示三、感受 一、情况介绍 前几天也是参加了研究生数学建模竞赛&#xff08;也就是华为杯&#xff09;&#xff0c;也是和本校的两个数学学院的朋友在网上组的队伍。昨天&#xff08;9.25&#xff09;通宵干完论文&#xff08;一条…

Windows安装openssl开发库

1 下载openssl安装包并安装 下载网址&#xff1a; https://slproweb.com/products/Win32OpenSSL.html 下载对应的安装版本。 双击安装包&#xff0c;一路下一步完成安装。注意&#xff1a;1.安装路径不要有空格&#xff1b; 2. 建议不要把DLL拷贝到系统路径。 2 编辑代码 …

“类型名称”在Go语言规范中的演变

Go语言规范&#xff08;The Go Programming Language Specification&#xff09;[1]是Go语言的核心文档&#xff0c;定义了该语言的语法、类型系统和运行时行为。Go语言规范的存在使得开发者在实现Go编译器时可以依赖一致的标准&#xff0c;它确保了语言的稳定性和一致性&#…

制造企业为何需要PLM系统?PLM系统解决方案对制造业重要性分析

制造企业为何需要PLM系统&#xff1f;PLM系统解决方案对制造业重要性分析 新华社9月23日消息&#xff0c;据全国组织机构统一社会信用代码数据服务中心统计&#xff0c;我国制造业企业总量突破600万家。数据显示&#xff0c;2024年1至8月&#xff0c;我国制造业企业数量呈现稳…

数据结构之链表(1),单链表

目录 前言 一、什么是链表 二、链表的分类 三、单链表 四、单链表的实现 五、SList.c文件完整代码 六、使用演示 总结 前言 本文讲述了什么是链表&#xff0c;以及实现了完整的单链表。 ❤️感谢支持&#xff0c;点赞关注不迷路❤️ 一、什么是链表 1.概念 概念&#xff1a;链…

【学习笔记】手写 Tomcat 六

目录 一、线程池 1. 构建线程池的类 2. 创建任务 3. 执行任务 测试 二、URL编码 解决方案 测试 三、如何接收客户端发送的全部信息 解决方案 测试 四、作业 1. 了解工厂模式 2. 了解反射技术 一、线程池 昨天使用了数据库连接池&#xff0c;我们了解了连接池的优…