首页 > 编程语言 >Depth Anything强大的单目深度估计Python与C++模型部署

Depth Anything强大的单目深度估计Python与C++模型部署

时间:2024-08-08 16:27:02浏览次数:15  
标签:Anything Python 模型 depth 单目 image vals cv size

引言

最近看到一个叫Depth Anything单目深度估计模型火了,于是抽空把代码和模型下载下来体验了一下,发现确实是很强大。
论文链接:https://arxiv.org/pdf/2401.10891.pdf
代码链接:https://github.com/LiheYoung/Depth-Anything
项目主页: https://depth-anything.github.io/

本文只记录如何使用官方代码跑demo,以及如何导出onnx模型并用onnxruntime做部署。
在这里插入图片描述

1. 使用官方代码跑demo

首先从GitHub把代码clone下来:

git clone https://github.com/LiheYoung/Depth-Anything

然后安装依赖库:

cd Depth-Anything
pip install -r requirements.txt

依赖库比较少,搭建环境非常简单,装好以后就可以跑demo了。如果是输入图像,那么可以执行下面的脚本:

python run.py --encoder <vits | vitb | vitl> --img-path <img-directory | single-img | txt-file> --outdir <outdir>

比如:

python run.py --encoder vitb --img-path ../test.jpg --outdir output/

--img-path参数可以是单张图像的路径、存放多张图片的文件夹的路径、存放一系列图像路径的TXT文件的路径。目前官方发布了三个模型:depth_anything_vits14.pth,depth_anything_vitb14.pth,depth_anything_vitl14.pth,分别与参数里的vits, vitb,vitl对应。执行上面的命令后,会自动从Huggingface的网站上下载对应的模型。「不过需要注意的是,国内目前无法访问Huggingface。怎么办呢?不用慌,我们可以使用Huggingface的镜像网站。首先在命令行执行下面的命令设置一下环境变量:

export HF_ENDPOINT=https://hf-mirror.com

然后再运行run.py脚本就可以愉快地跑模型了,脚本会把结果输出到--outdir参数指定的目录中。下面是我用nuScenes数据集中的图片跑出

如果需要跑视频,那么可以用run_video.py脚本:

python run_video.py --encoder vitb --video-path assets/examples_video --outdir output/

Python Onnx模型部署

2.1 导出onnx模型

导出onnx模型的方法可以参考下面这个仓库:

https://github.com/fabio-sim/Depth-Anything-ONNX

把代码下载下来后运行export.py脚本即可导出onnx模型:

python export.py --model s # s对应vits模型

导出的onnx模型默认存放在weights/目录下。在这个脚本中,同样会去Huggingface网站上下载PyTorch模型,因此需要用镜像网站替换一下。替换方法很简单,就是把代码中指定的链接将huggingface.co直接替换为hf-mirror.com

depth_anything.to(device).load_state_dict(
      torch.hub.load_state_dict_from_url(
          f"https://hf-mirror.com/spaces/LiheYoung/Depth-Anything/resolve/main/checkpoints/depth_anything_vit{model}14.pth",
          map_location="cpu",
      ),
      strict=True,
  )

另外,这个脚本导出onnx模型的时候是使用的动态参数模型,如果不想用动态参数可以把dynamic_axes参数去掉改成静态模式。导出的onnx模型还可以使用onnx-simplifier工具简化一下。

2.2 用onnxruntime部署onnx模型

部署Depth Anything模型也是差不多的流程。加载好onnx模型后,首先需要对输入图像做预处理,预处理的时候需要做减均值再除以标准差对图像数据做规范化,其他处理操作与YOLOv8RT-DETR是一样的。预处理函数preprocess的实现如下:

def preprocess(
    bgr_image,
    width,
    height,
    mean=[123.675, 116.28, 103.53],
    std=[58.395, 57.12, 57.375],
):
    image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)
    image = cv2.resize(image, (width, height)).astype(np.float32)
    image = (image - mean) / std
    image = np.transpose(image, (2, 0, 1)).astype(np.float32)
    input_tensor = np.expand_dims(image, axis=0)
    return input_tensor

输入数据准备好以后,就可以送入模型进行推理:

outputs = session.run(None, {session.get_inputs()[0].name: input_tensor})

得到模型推理结果后,只需要做一点简单的后处理操作就可以了:

depth = outputs[0][0]  
depth = np.transpose(depth, [1, 2, 0]) #chw->hwc

depth = cv2.normalize(depth,None, 0,255,cv2.NORM_MINMAX,cv2.CV_8UC1)
colormap = cv2.applyColorMap(depth,cv2.COLORMAP_INFERNO)
colormap = cv2.resize(colormap,(image_width,image_height))
combined_results = cv2.hconcat([image, colormap])

后处理的时候首先调用OpenCVnormalize函数将深度图的像素值调整到[0,255]的范围内,然后调用applyColorMap函数对深度图进行伪彩色化:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

模型输入尺寸设置为518x518batch size设置为1,在GeForce RTX 3090显卡上3个模型的耗时如下:

模型模型精度耗时
depth_anything_vits14.onnxFP3216 ms
depth_anything_vitb14.onnxFP3242 ms
depth_anything_vitl14.onnxFP3290 ms

C++ Onnx模型部署

#include "dpt.h"

Dpt::Dpt()
{
    blob_pool_allocator.set_size_compare_ratio(0.f);
    workspace_pool_allocator.set_size_compare_ratio(0.f);
}


int Dpt::load(std::string param_path, std::string bin_path, int _target_size, 
    const float* _mean_vals, const float* _norm_vals, bool use_gpu)
{
    dpt_.clear();
    blob_pool_allocator.clear();
    workspace_pool_allocator.clear();

    ncnn::set_cpu_powersave(2);
    ncnn::set_omp_num_threads(ncnn::get_big_cpu_count());

    dpt_.opt = ncnn::Option();

#if NCNN_VULKAN
    dpt_.opt.use_vulkan_compute = use_gpu;
#endif

    dpt_.opt.num_threads = ncnn::get_big_cpu_count();
    dpt_.opt.blob_allocator = &blob_pool_allocator;
    dpt_.opt.workspace_allocator = &workspace_pool_allocator;

   /* char parampath[256];
    char modelpath[256];
    sprintf(parampath, "dpt%s.param", modeltype);
    sprintf(modelpath, "dpt%s.bin", modeltype);*/

    dpt_.load_param(param_path.c_str());
    dpt_.load_model(bin_path.c_str());

    target_size_ = _target_size;
    mean_vals_[0] = _mean_vals[0];
    mean_vals_[1] = _mean_vals[1];
    mean_vals_[2] = _mean_vals[2];
    norm_vals_[0] = _norm_vals[0];
    norm_vals_[1] = _norm_vals[1];
    norm_vals_[2] = _norm_vals[2];
    
    color_map_ = cv::Mat(target_size_, target_size_, CV_8UC3);

    return 0;
}

int Dpt::detect(const cv::Mat& rgb, cv::Mat& depth_color)
{

    int width = rgb.cols;
    int height = rgb.rows;

    // pad to multiple of 32
    int w = width;
    int h = height;
    float scale = 1.f;
    if (w > h)
    {
        scale = (float)target_size_ / w;
        w = target_size_;
        h = h * scale;
    }
    else
    {
        scale = (float)target_size_ / h;
        h = target_size_;
        w = w * scale;
    }

    ncnn::Mat in = ncnn::Mat::from_pixels_resize(rgb.data, ncnn::Mat::PIXEL_RGB, width, height, w, h);

    // pad to target_size rectangle
    int wpad = target_size_ - w;
    int hpad = target_size_ - h;
    ncnn::Mat in_pad;
    ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 0.f);

    in_pad.substract_mean_normalize(mean_vals_, norm_vals_);

    ncnn::Extractor ex = dpt_.create_extractor();

    ex.input("image", in_pad);

    ncnn::Mat out;
    ex.extract("depth", out);

    cv::Mat depth(out.h, out.w, CV_32FC1, (void*)out.data);
    cv::normalize(depth, depth, 0, 255, cv::NORM_MINMAX, CV_8UC1);
    cv::applyColorMap(depth, color_map_, cv::ColormapTypes::COLORMAP_INFERNO);
    cv::resize(color_map_(cv::Rect(wpad / 2, hpad / 2, w, h)), depth_color, rgb.size());

    return 0;
}

int Dpt::draw(cv::Mat& rgb, cv::Mat& depth_color)
{
    cv::cvtColor(depth_color, rgb, cv::COLOR_RGB2BGR);

    return 0;
}

最终的结果如下:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
源码下载地址:https://download.csdn.net/download/matt45m/89616293

标签:Anything,Python,模型,depth,单目,image,vals,cv,size
From: https://blog.csdn.net/matt45m/article/details/140903742

相关文章

  • 计算机毕业设计-基于python失物招领系统【源码+文档+PPT】
    精彩专栏推荐订阅:在下方专栏......
  • 【Python机器学习】利用AdaBoost元算法提高分类性能——基于单层决策树构建弱分类器
    单层决策树(也称决策树桩)是一种简单的决策树。它基于单个特征来做决策,由于这棵树只有一次分裂过程,因此它实际上就是一个树桩。在构造AdaBoost代码时,首先通过一个简单数据集来确保在算法上一切就绪:fromnumpyimport*defloadSimpData():datMat=matrix([[1.0,2.1],......
  • python代码混淆与编译
    python代码混淆、编译与打包考虑到生产环境部署,而python作为解释性语言,对源码没有任何保护。此文记录探索如何保护源码的过程。代码混淆代码混淆基本上就是把代码编译为字节码。工具有两种:py_compilepyarmorpy_compile示例:py_compile.compile(src_pyfile,dst_pyfile......
  • 在python中将二维数组转换为彩色图像
    我有像这样的2d整数列表:list1=[[1,30,50],[21,45,9],[97,321,100]]下一步我要将其转换为numpy数组:myarr=np.asarray(list1)下一步我将使用PIL将其转换为图像,如下所示:img=Image.fromarray(myarr,"I")img.save("my.png")问题是我不想要灰......
  • Python 汉字区位码、字符串 相互转换
    Python汉字区位码、字符串相互转换区位码简介GB2312所有字符放在一张94x94的矩阵中,矩阵中的每个位置对应一个字符(有的位置是空的,没有字符)。区位码为十进制四位数,前后两位分别代表该字符在矩阵中的行、列坐标(均从1算起),如4528“图”字,为45行、28列上的字符。能通过对区位码进......
  • python3如何使用‘pexpect’自动与串口交互?
    如何在Windows中使用python'pexpect'自动与串口(例如:COM3)交互并在开机时通过串口登录嵌入式开发板时输入用户密码?ser=serial.Serial(port=serial_port,baudrate=baudrate,bytesize=bytesize,parity=parity,stopbits=stopbits,timeout=timeout)channel=pexpect.fdpexp......
  • Python 熊猫迭代
    代码:#Importcarsdataimportpandasaspdcars=pd.read_csv('cars.csv',index_col=0)#Adaptforloopforlab,rowincars.iterrows():print(f"{[lab]}:{row['cars_per_cap']}")输出:['US']:809['A......
  • 【第九节】python中xml解析和json编解码
    目录一、PythonXML解析1.1什么是XML1.2Python对XML的解析方法1.3SAX解析xml1.4xml.dom解析xml1.6ElementTree解析XML二、Python编解码json2.1什么是json2.2使用json库2.3使用第三方库Demjson一、PythonXML解析1.1什么是XML        XML,......
  • 从 python 设置运算符符号到方法名称的映射是什么?它们与文档不匹配
    我创建了自己的Customset类,它实现了python集合的几乎所有方法。当我使用此自定义集的实例时,许多集合运算符都会失败。它们会失败,并显示类似以下内容的内容:TypeError:unsupportedoperandtype(s)for-:'Customset'andCustomset'orTypeError:'<='......
  • 21.python函数(return)
    return一、return语句1、return是指定一个返回值2、在python中创建一个函数,可以用return语句指定返回的的值,这个返回值可以是任意的类型3、return语句在同一个函数中可以出现多次,但是只有有一个得到执行,就会直接结束函数的执行。return后面的语句不执行了4、return的格式re......