【安装、配置、汉化】CVAT: 团队协作与自动标注的图像标注工具

本文记录了安装标注工具CVAT的过程,并且使用一种相当笨的方法进行汉化

CVAT简介

在人工智能领域,数据标注是构建高效机器学习模型的关键步骤之一。Computer Vision Annotation Tool (CVAT) 作为一款功能强大的开源标注工具。
CVAT有两大最关键的功能:支持团队协作标注;自动化标注。
这使得CVAT很适合当作人工智能团队的标注工具,但尽管CVAT提供了一个直观的在线版本,对于追求深度定制和团队使用的项目来说,本地部署则显得尤为重要。本地部署CVAT主要优势是允许团队根据具体业务需求进行二次开发,提升工具的适用性。

安装配置CVAT

CVAT项目团队已经将软件封装为Docker镜像,极大地简化了安装流程。遵循官方文档的指导,即便是新手也能顺利进行安装。然而,在实际操作中,网络状况可能会成为一道难以逾越的障碍,尤其是镜像下载环节。

github:https://github.com/cvat-ai/cvat
官方文档:https://docs.cvat.ai/docs/administration/basics/installation/

面对需要连接外网导致的镜像拉去失败,网上有许多资料,主要是更换镜像源或使用VPN。这些方法有时能奏效,但实际上就在不久之前,我使用这些方法也不能拉取镜像。近期,借助稳定且高速的网络环境,终于成功克服了这一难关,实现了CVAT的本地部署。

首先需要确保你的电脑安装了docker,接下来的流程对于windows和linux差别不大
最基础的安装:
使用 Git 从 GitHub 存储库克隆 CVAT 源代码。
以下命令将克隆最新的开发分支:

git clone https://github.com/cvat-ai/cvat
cd cvat

如果要使用网络或通过其他系统访问 CVAT,这一步是多台电脑进行团队协作的关键,如果不更改只能在自己的电脑上使用,需要导出环境变量CVAT_HOST,不同系统方法略有不同
linux:

export CVAT_HOST=你的ip

windows:

SET CVAT_HOST=你的ip

然后进行构建镜像,也就是这一步需要使用VPN(网上资料说换源可以,但我自己试一试不行,甚至vpn有时也不行),反正主要还是取决于网络

2024/07/29 今日在服务器上配置时,发现换源还是可以的,不过可能一些镜像源被下架了什么的附上进入成功几个镜像源(不知道哪个成功的),如果又不行就再去找找其他镜像源:
https://hub.uuuadc.top/,
https://docker.anyhub.us.kg/,
https://dockerhub.jobcher.com/,
https://dockerhub.icu/,
https://docker.ckyl.me/,
https://docker.awsl9527.cn/
换源的方法就是修改docker.json,网上找找就有

docker compose up -d

等待构建成功后就可以直接使用了,如果之前设置了ip,就使用http://ip:8080;如果没有设置,就使用http://localhost:8080就能登陆CVAT,注意要将ip换成你刚刚设置的环境变量,其他的电脑也是直接访问这个网站。
普通注册一个用户时,默认没有查看任务列表的权限。为此,可以创建一个超级用户。超级用户可以使用 管理员面板,用于将正确的组分配给其他用户。请使用以下命令 下面:

sudo docker exec -it cvat_server bash -ic 'python3 ~/manage.py createsuperuser'

在windows中建议直接使用以下这个,一样的效果

#进入镜像
docker exec -it cvat_server /bin/bash
#运行程序
python3 ~/manage.py createsuperuser

CVAT汉化

搜索网上的资料以及项目中的issues,对界面进行汉化有很多种方法,比如使用gettext、使用i8next等方法,但我尝试以上两种方法暂没有成功,应该是坏境的问题。由于我没有前端经验,对于相关工作了解较少,以下应该是最简单但较麻烦的一种实现(直接找到对应的文本进行修改),主要在主文件夹中的cvat-ui文件夹里进行修改

项目的作者似乎从19年就开始做相关的工作,在issues中,可以关注官方是否推出其他语言支持

修改图标

在修改cvat-ui->dist中可以修改网站的图标

在cvat-ui>src>assets中修改其他图标
在这里插入图片描述

修改代码

主要的界面代码都在cvat-ui>src->components文件夹中
在components文件夹中寻找,发现login-page等文件夹可能和登录页面有关,修改即可。
比如修改login-page文件夹中的login-form.tsx代码,把所有英文文本换为中文

import React, { useState } from 'react';
import { Link } from 'react-router-dom';

import Form from 'antd/lib/form';
import Button from 'antd/lib/button';
import Input from 'antd/lib/input';
import { Col, Row } from 'antd/lib/grid';
import Title from 'antd/lib/typography/Title';
import Text from 'antd/lib/typography/Text';
import Icon from '@ant-design/icons';
import {
    BackArrowIcon, ClearIcon,
} from 'icons';

import CVATSigningInput, { CVATInputType } from 'components/signing-common/cvat-signing-input';
import { CombinedState } from 'reducers';
import { useAuthQuery, usePlugins } from 'utils/hooks';

export interface LoginData {
    credential: string;
password: string;
}

interface Props {
    renderResetPassword: boolean;
renderRegistrationComponent: boolean;
renderBasicLoginComponent: boolean;
fetching: boolean;
onSubmit(loginData: LoginData): void;
}

function LoginFormComponent(props: Props): JSX.Element {
const {
    fetching, onSubmit, renderResetPassword, renderRegistrationComponent, renderBasicLoginComponent,
} = props;

const authQuery = useAuthQuery();
const [form] = Form.useForm();
const [credential, setCredential] = useState('');
const pluginsToRender = usePlugins(
    (state: CombinedState) => state.plugins.components.loginPage.loginForm,
props,
{ credential },
);

let resetSearch = authQuery ? new URLSearchParams(authQuery).toString() : '';
if (credential.includes('@')) {
    const updatedAuthQuery = authQuery ? { ...authQuery, email: credential } : { email: credential };
resetSearch = new URLSearchParams(updatedAuthQuery).toString();
}

const forgotPasswordLink = (
    <Col className='cvat-credentials-link'>
<Text strong>
<Link to={{ pathname: '/auth/password/reset', search: resetSearch }}>
    忘记密码?
    </Link>
    </Text>
    </Col>
);

return (
    <div className='cvat-login-form-wrapper'>
<Row justify='space-between' className='cvat-credentials-navigation'>
{
    credential && (
        <Col>
    <Icon
    component={BackArrowIcon}
onClick={() => {
    setCredential('');
form.setFieldsValue({ credential: '' });
}}
/>
</Col>
)
}
{
    !credential && renderRegistrationComponent && (
        <Row>
    <Col className='cvat-credentials-link'>
<Text strong>
新用户?&nbsp;
<Link to={{
    pathname: '/auth/register',
    search: authQuery ? new URLSearchParams(authQuery).toString() : '',
                                    }}
                                    >
                                        注册账户
                                    </Link>
                                </Text>
                            </Col>
                        </Row>
                    )
                }
                {
                    renderResetPassword && forgotPasswordLink
                }
            </Row>
            <Col>
                <Title level={2}> 登录 </Title>
            </Col>
            <Form
                className={`cvat-login-form ${credential ? 'cvat-login-form-extended' : ''}`}
                form={form}
                onFinish={(loginData: LoginData) => {
                    onSubmit(loginData);
                }}
            >
                {renderBasicLoginComponent && (
                    <>
                        <Form.Item
                            className='cvat-credentials-form-item'
                            name='credential'
                        >
                            <Input
                                autoComplete='username'
                                prefix={<Text>邮箱或用户名</Text>}
                                className={credential ? 'cvat-input-floating-label-above' : 'cvat-input-floating-label'}
                                suffix={credential && (
                                    <Icon
                                        component={ClearIcon}
                                        onClick={() => {
                                            setCredential('');
                                            form.setFieldsValue({ credential: '', password: '' });
                                        }}
                                    />
                                )}
                                onChange={(event) => {
                                    const { value } = event.target;
                                    setCredential(value);
                                    if (!value) form.setFieldsValue({ credential: '', password: '' });
                                }}
                            />
                        </Form.Item>
                        {
                            credential && (
                                <Form.Item
                                    className='cvat-credentials-form-item'
                                    name='password'
                                    rules={[
                                        {
                                            required: true,
                                            message: '请输入密码',
                                        },
                                    ]}
                                >
                                    <CVATSigningInput
                                        type={CVATInputType.PASSWORD}
                                        id='password'
                                        placeholder='密码'
                                        autoComplete='current-password'
                                    />
                                </Form.Item>
                            )
                        }
                        {
                            !!credential && (
                                <Form.Item>
                                    <Button
                                        className='cvat-credentials-action-button'
                                        loading={fetching}
                                        disabled={!credential}
                                        htmlType='submit'
                                    >
                                        下一步
                                    </Button>
                                </Form.Item>
                            )
                        }
                    </>
                )}
                {
                    pluginsToRender.map(({ component: Component }, index) => (
                        <Component targetProps={props} targetState={{ credential }} key={index} />
                    ))
                }
            </Form>
        </div>
    );
}

export default React.memo(LoginFormComponent);

修改完的效果:
image.png
以此类推,每个页面都有对应的文件夹,例如项目:project,任务:task,作业:job;哪个页面没有汉化就找对应的文件夹,进入修改相关文件
但要注意不要修改到类名等位置,只修改文本,否则项目容易无法运行

重构docker镜像

修改完代码需要从本地对镜像进行重构,否则无法得到新的docker镜像
根据官方文档中,运行以下命令停止并删除所有容器

docker compose down

在修改过代码的主文件夹中重新构建docker镜像

docker compose -f docker-compose.yml -f docker-compose.dev.yml build

我在构建时依然需要使用VPN,并且在构建时遇到一些问题,可以进行参考:


报错:未找到eb1ac09分支

运行dockerfile时报错了
image.png这个问题是在构建 Docker 镜像的过程中,git checkout eb1ac09 命令未能找到指定的提交哈希值,可能是因为 Git 仓库中不存在 eb1ac09这个特定的提交记录。
尝试改了几次dockerfile依然报错,则将它换成拉取最新的分支

# 确保仓库是最新的
RUN cd smokescreen && \
    git fetch --all && \
    git checkout main && \
    go build -o /tmp/smokescreen


报错:安装软件错误

解决了上一个报错,由于网络问题可能还会遇到以下错误

failed to solve: process “/bin/sh -c apt-get update && DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq curl g++ gcc git libgeos-dev libldap2-dev libsasl2-dev make nasm pkg-config python3-dev python3-pip libxml2-dev libxmlsec1-dev libxmlsec1-openssl && rm -rf /var/lib/apt/lists/*” did not complete successfully: exit code: 100

image.png
这个错误信息表明在尝试通过apt-get安装一系列软件包时遇到了问题,导致退出代码为100。在Debian和Ubuntu系统中,apt-get命令的退出代码100通常表示在处理依赖关系时遇到问题,可能是由于包之间的冲突、缺少依赖项或软件源中的问题。一般来说是由于网络问题
将dockerfile中的这段代码

RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \
        bzip2 \
        ca-certificates \
        curl \
        git \
        libgeos-c1v5 \
        libgl1 \
        libgomp1 \
        libldap-2.5-0 \
        libpython3.10 \
        libsasl2-2 \
        libxml2 \
        libxmlsec1 \
        libxmlsec1-openssl \
        nginx \
        p7zip-full \
        poppler-utils \
        python3 \
        python3-venv \
        supervisor \
        tzdata \
        unrar \
    && ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata && \
    rm -rf /var/lib/apt/lists/* && \
    echo 'application/wasm wasm' >> /etc/mime.types

更换为以下代码

# 更改APT软件源到清华镜像站点
RUN sed -i 's/archive.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \
    sed -i 's/security.ubuntu.com/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list

# 更新软件包列表并安装所需软件包
RUN apt-get update && \
    DEBIAN_FRONTEND=noninteractive apt-get --no-install-recommends install -yq \
        bzip2 \
        ca-certificates \
        curl \
        git \
        libgeos-c1v5 \
        libgl1 \
        libgomp1 \
        libldap-2.5-0 \
        libpython3.10 \
        libsasl2-2 \
        libxml2 \
        libxmlsec1 \
        libxmlsec1-openssl \
        nginx \
        p7zip-full \
        poppler-utils \
        python3 \
        python3-venv \
        supervisor \
        tzdata \
        unrar \
    && ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime && \
    dpkg-reconfigure -f noninteractive tzdata && \
    rm -rf /var/lib/apt/lists/* && \
    echo 'application/wasm wasm' >> /etc/mime.types


2024年更新报错:git下载项目失败

在服务器上配置时,重新构建镜像经常失败,有一项报错为git下载datumaro失败
根据报错信息,在cvat\requirements中找到base.txt和base.in,其中有一句代码:

datumaro @ git+https://github.com/cvat-ai/datumaro.git@82982b16b178eb3f39c707795bb68a3306610abf

根据名字去gitee中找对应的项目,以下举例一种,如果失败了可以找找其他的
更换为:

datumaro @ git+https://gitee.com/tianyue_688/datumaro.git@82982b16b178eb3f39c707795bb68a3306610abf


2024/7/31更新报错:yarn "node"版本错误

在服务器进行构建镜像时,报了以下错误:

在这里插入图片描述
根据报错信息显示是node.js版本不符合导致,尝试了多个方法后,发现使用忽略报错的方式有效:
在dockerbuild.ui中找到报错的位置

RUN DISABLE_HUSKY=1 yarn --frozen-lockfile

在这一句的上方添加:

RUN yarn config set ignore-engines true

这是忽略yarn报错的指令,虽然我也担心忽略报错会有影响,不过实测能够解决这个问题,成功构建

2024/7/31更新报错:卡在[4/4] Building fresh packages…

同样是上一个报错中的位置,当不报版本错误后,building的时候直接卡住,不继续往下走了
收集资料后发现,在当前文件夹即主文件夹中添加一个文件

.yarnrc

注意文件名要对,然后在这个文件中加入以下内容:

registry "https://registry.npm.taobao.org"
sass_binary_site "https://npm.taobao.org/mirrors/node-sass/"
phantomjs_cdnurl "http://cnpmjs.org/downloads"
electron_mirror "https://npm.taobao.org/mirrors/electron/"
sqlite3_binary_host_mirror "https://foxgis.oss-cn-shanghai.aliyuncs.com/"
profiler_binary_host_mirror "https://npm.taobao.org/mirrors/node-inspector/"
chromedriver_cdnurl "https://cdn.npm.taobao.org/dist/chromedriver"

重新构建则可以解决问题(不过网上也有些人说这个方法没用,我是加入后直接解决了,可供参考)


启动镜像

一般来说可以成功,多尝试几次就可以完成构建
然后启动容器

docker compose up -d

重新设置外部访问

export CVAT_HOST=190.168.xxx.xxx

但这却是是一种傻方法,费时费力,如果有更快的方法对CVAT进行汉化欢迎讨论

部分界面汉化效果

image.png
image.png
在这里插入图片描述
在这里插入图片描述

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值