DPT,全称Dense Prediction Transformer,即密集预测Transformer,由Intel ISL实验室在ICCV2021上发布。核心定位:专为像素级密集预测设计的纯ViT架构,仅支持两大任务:
单目相对深度估计(MiDaS3核心模型)
语义分割(无实例区分,全域像素分类)
DPT创新核心:
ViT编码器全程无下采样,Token数量全程保持不变,原生保留高分辨率细节;
每一层多头自注意力均具备全局感受野,可对整张图像的所有像素进行相互建模,在长距离依赖建模方面远超CNN;
分层提取ViT多尺度Token,通过卷积解码器渐进融合并上采样,还原至原图尺寸输出。
注:官方仅开源推理代码与网络结构,未提供数据集加载、训练等相关代码。
模型:DPT
AI加速卡:910C
CPU架构:ARM
CANN:9.0.0
docker run -it -u root -d --net=host \
--privileged \
--ipc=host \
--device=/dev/davinci_manager \
--device=/dev/devmm_svm \
--device=/dev/hisi_hdc \
-v /usr/local/Ascend/driver:/usr/local/Ascend/driver \
-v /usr/local/dcmi:/usr/local/dcmi \
-v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \
-v /usr/local/sbin:/usr/local/sbin \
-v /usr/local/Ascend/driver/tools/hccn_tool:/usr/local/Ascend/driver/tools/hccn_tool \
--name dpt \
quay.io/ascend/cann:9.0.0-a3-ubuntu22.04-py3.11 \
/bin/bash
注:后续操作均是在容器中进行。
cd /root
git clone https://github.com/isl-org/DPT.gitapt update
apt install libxcb1 libx11-xcb1
apt install -y libgl1-mesa-glx
apt install -y libglib2.0-0 libsm6 libxext6 libxrender-dev libgomp1
apt-get install -y zlib1g-dev build-essential
pip3 install torch==2.5.1 torchvision==0.20.1 torch_npu==2.5.1```
pip3 install timm opencv-python-headless h5py scipycd /root/DPT
git apply dpt_npu_adapt.patch主要代码修改说明如下:
1、设备选择、cudnn移除、channels_last移除、插值模式、FP16逻辑
2、torch.load增加weights_only参数 | PyTorch 2.5.1安全要求 需提前将权重文档下载好放到weights/目录下
(1)推理脚本内容参考
"""Compute depth maps for images in the input folder.
"""
import os
import glob
import torch
import torch_npu
import cv2
import argparse
import util.io
from torchvision.transforms import Compose
from dpt.models import DPTDepthModel
from dpt.midas_net import MidasNet_large
from dpt.transforms import Resize, NormalizeImage, PrepareForNet
def run(input_path, output_path, model_path, model_type="dpt_hybrid", optimize=True):
"""Run MonoDepthNN to compute depth maps.
Args:
input_path (str): path to input folder
output_path (str): path to output folder
model_path (str): path to saved model
"""
print("initialize")
if torch.npu.is_available():
device = torch.device("npu:0")
else:
device = torch.device("cpu")
print("device: %s" % device)
# load network
if model_type == "dpt_large": # DPT-Large
net_w = net_h = 384
model = DPTDepthModel(
path=model_path,
backbone="vitl16_384",
non_negative=True,
enable_attention_hooks=False,
)
normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
elif model_type == "dpt_hybrid": # DPT-Hybrid
net_w = net_h = 384
model = DPTDepthModel(
path=model_path,
backbone="vitb_rn50_384",
non_negative=True,
enable_attention_hooks=False,
)
normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
elif model_type == "dpt_hybrid_kitti":
net_w = 1216
net_h = 352
model = DPTDepthModel(
path=model_path,
scale=0.00006016,
shift=0.00579,
invert=True,
backbone="vitb_rn50_384",
non_negative=True,
enable_attention_hooks=False,
)
normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
elif model_type == "dpt_hybrid_nyu":
net_w = 640
net_h = 480
model = DPTDepthModel(
path=model_path,
scale=0.000305,
shift=0.1378,
invert=True,
backbone="vitb_rn50_384",
non_negative=True,
enable_attention_hooks=False,
)
normalization = NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5])
elif model_type == "midas_v21": # Convolutional model
net_w = net_h = 384
model = MidasNet_large(model_path, non_negative=True)
normalization = NormalizeImage(
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]
)
else:
assert (
False
), f"model_type '{model_type}' not implemented, use: --model_type [dpt_large|dpt_hybrid|dpt_hybrid_kitti|dpt_hybrid_nyu|midas_v21]"
transform = Compose(
[
Resize(
net_w,
net_h,
resize_target=None,
keep_aspect_ratio=True,
ensure_multiple_of=32,
resize_method="minimal",
image_interpolation_method=cv2.INTER_CUBIC,
),
normalization,
PrepareForNet(),
]
)
model.eval()
if optimize and device.type == "npu":
model = model.half()
model.to(device)
# get input
img_names = glob.glob(os.path.join(input_path, "*"))
num_images = len(img_names)
# create output folder
os.makedirs(output_path, exist_ok=True)
print("start processing")
for ind, img_name in enumerate(img_names):
if os.path.isdir(img_name):
continue
print(" processing {} ({}/{})".format(img_name, ind + 1, num_images))
# input
img = util.io.read_image(img_name)
if args.kitti_crop is True:
height, width, _ = img.shape
top = height - 352
left = (width - 1216) // 2
img = img[top : top + 352, left : left + 1216, :]
img_input = transform({"image": img})["image"]
# compute
with torch.no_grad():
sample = torch.from_numpy(img_input).to(device).unsqueeze(0)
if optimize and device.type == "npu":
sample = sample.half()
prediction = model.forward(sample)
prediction = (
torch.nn.functional.interpolate(
prediction.unsqueeze(1).float(),
size=img.shape[:2],
mode="bicubic",
align_corners=False,
)
.squeeze()
.cpu()
.numpy()
)
if model_type == "dpt_hybrid_kitti":
prediction *= 256
if model_type == "dpt_hybrid_nyu":
prediction *= 1000.0
filename = os.path.join(
output_path, os.path.splitext(os.path.basename(img_name))[0]
)
util.io.write_depth(filename, prediction, bits=2, absolute_depth=args.absolute_depth)
print("finished")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-i", "--input_path", default="input", help="folder with input images"
)
parser.add_argument(
"-o",
"--output_path",
default="output_monodepth",
help="folder for output images",
)
parser.add_argument(
"-m", "--model_weights", default=None, help="path to model weights"
)
parser.add_argument(
"-t",
"--model_type",
default="dpt_hybrid",
help="model type [dpt_large|dpt_hybrid|midas_v21]",
)
parser.add_argument("--kitti_crop", dest="kitti_crop", action="store_true")
parser.add_argument("--absolute_depth", dest="absolute_depth", action="store_true")
parser.add_argument("--optimize", dest="optimize", action="store_true")
parser.add_argument("--no-optimize", dest="optimize", action="store_false")
parser.set_defaults(optimize=True)
parser.set_defaults(kitti_crop=False)
parser.set_defaults(absolute_depth=False)
args = parser.parse_args()
default_models = {
"midas_v21": "weights/midas_v21-f6b98070.pt",
"dpt_large": "weights/dpt_large-midas-2f21e586.pt",
"dpt_hybrid": "weights/dpt_hybrid-midas-501f0c75.pt",
"dpt_hybrid_kitti": "weights/dpt_hybrid_kitti-cb926ef4.pt",
"dpt_hybrid_nyu": "weights/dpt_hybrid_nyu-2ce69ec7.pt",
}
if args.model_weights is None:
args.model_weights = default_models[args.model_type]
# compute depth maps
run(
args.input_path,
args.output_path,
args.model_weights,
args.model_type,
args.optimize,
)
(2)执行推理:
cd /root/DPT
python3 run_monodepth.py -i input -o output_monodepth -t dpt_hybrid --no-optimize(3)推理结果:


测试图片:

(1)推理脚本内容参考如下:
"""Compute segmentation maps for images in the input folder.
"""
import os
import glob
import cv2
import argparse
import torch
import torch_npu
import torch.nn.functional as F
import util.io
from torchvision.transforms import Compose
from dpt.models import DPTSegmentationModel
from dpt.transforms import Resize, NormalizeImage, PrepareForNet
def run(input_path, output_path, model_path, model_type="dpt_hybrid", optimize=True):
"""Run segmentation network
Args:
input_path (str): path to input folder
output_path (str): path to output folder
model_path (str): path to saved model
"""
print("initialize")
if torch.npu.is_available():
device = torch.device("npu:0")
else:
device = torch.device("cpu")
print("device: %s" % device)
net_w = net_h = 480
# load network
if model_type == "dpt_large":
model = DPTSegmentationModel(
150,
path=model_path,
backbone="vitl16_384",
)
elif model_type == "dpt_hybrid":
model = DPTSegmentationModel(
150,
path=model_path,
backbone="vitb_rn50_384",
)
else:
assert (
False
), f"model_type '{model_type}' not implemented, use: --model_type [dpt_large|dpt_hybrid]"
transform = Compose(
[
Resize(
net_w,
net_h,
resize_target=None,
keep_aspect_ratio=True,
ensure_multiple_of=32,
resize_method="minimal",
image_interpolation_method=cv2.INTER_CUBIC,
),
NormalizeImage(mean=[0.5, 0.5, 0.5], std=[0.5, 0.5, 0.5]),
PrepareForNet(),
]
)
model.eval()
if optimize and device.type == "npu":
model = model.half()
model.to(device)
# get input
img_names = glob.glob(os.path.join(input_path, "*"))
num_images = len(img_names)
# create output folder
os.makedirs(output_path, exist_ok=True)
print("start processing")
for ind, img_name in enumerate(img_names):
print(" processing {} ({}/{})".format(img_name, ind + 1, num_images))
# input
img = util.io.read_image(img_name)
img_input = transform({"image": img})["image"]
# compute
with torch.no_grad():
sample = torch.from_numpy(img_input).to(device).unsqueeze(0)
if optimize and device.type == "npu":
sample = sample.half()
out = model.forward(sample)
prediction = torch.nn.functional.interpolate(
out.float(), size=img.shape[:2], mode="bicubic", align_corners=False
)
prediction = torch.argmax(prediction, dim=1) + 1
prediction = prediction.squeeze().cpu().numpy()
# output
filename = os.path.join(
output_path, os.path.splitext(os.path.basename(img_name))[0]
)
util.io.write_segm_img(filename, img, prediction, alpha=0.5)
print("finished")
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"-i", "--input_path", default="input", help="folder with input images"
)
parser.add_argument(
"-o", "--output_path", default="output_semseg", help="folder for output images"
)
parser.add_argument(
"-m",
"--model_weights",
default=None,
help="path to the trained weights of model",
)
# 'vit_large', 'vit_hybrid'
parser.add_argument("-t", "--model_type", default="dpt_hybrid", help="model type")
parser.add_argument("--optimize", dest="optimize", action="store_true")
parser.add_argument("--no-optimize", dest="optimize", action="store_false")
parser.set_defaults(optimize=True)
args = parser.parse_args()
default_models = {
"dpt_large": "weights/dpt_large-ade20k-b12dca68.pt",
"dpt_hybrid": "weights/dpt_hybrid-ade20k-53898607.pt",
}
if args.model_weights is None:
args.model_weights = default_models[args.model_type]
# compute segmentation maps
run(
args.input_path,
args.output_path,
args.model_weights,
args.model_type,
args.optimize,
)
(2)执行推理脚本:
cd /root/DPT
python3 run_segmentation.py -i input -o output_semseg -t dpt_hybrid --no-optimize(3)推理结果:

(1)数据集准备:
下载NYU Depth V2数据集,数据集组织形式:
组织后的目录结构:
/root/DPT/input/nyu_dpt_input/
input/ # 654张RGB图像(rgb_XXXXX.png)
gt/ # 654张GT深度图(按rgb_XXXXX/sync_depth_XXXXX.png子目录组织)
(2)运行深度估计推理
cd /root/DPT
python3 run_monodepth.py -i input/nyu_dpt_input/input -o output_nyu_eval -t dpt_hybrid_nyu --absolute_depth --no-optimize(3)运行评估
cd /root/DPT
python3 eval_with_pngs.py --pred_path ./output_nyu_eval/ --gt_path ./input/nyu_dpt_input/gt/ --dataset nyu --max_depth_eval 10 --eigen_crop评估脚本内容参考如下:
# Copyright (C) 2019 Jin Han Lee
#
# This file is a part of BTS.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>
from __future__ import absolute_import, division, print_function
import os
import argparse
import fnmatch
import cv2
import numpy as np
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '1'
def convert_arg_line_to_args(arg_line):
for arg in arg_line.split():
if not arg.strip():
continue
yield arg
parser = argparse.ArgumentParser(description='BTS TensorFlow implementation.', fromfile_prefix_chars='@')
parser.convert_arg_line_to_args = convert_arg_line_to_args
parser.add_argument('--pred_path', type=str, help='path to the prediction results in png', required=True)
parser.add_argument('--gt_path', type=str, help='root path to the groundtruth data', required=False)
parser.add_argument('--dataset', type=str, help='dataset to test on, nyu or kitti', default='nyu')
parser.add_argument('--eigen_crop', help='if set, crops according to Eigen NIPS14', action='store_true')
parser.add_argument('--garg_crop', help='if set, crops according to Garg ECCV16', action='store_true')
parser.add_argument('--min_depth_eval', type=float, help='minimum depth for evaluation', default=1e-3)
parser.add_argument('--max_depth_eval', type=float, help='maximum depth for evaluation', default=80)
parser.add_argument('--do_kb_crop', help='if set, crop input images as kitti benchmark images', action='store_true')
args = parser.parse_args()
def compute_errors(gt, pred):
thresh = np.maximum((gt / pred), (pred / gt))
d1 = (thresh < 1.25).mean()
d2 = (thresh < 1.25 ** 2).mean()
d3 = (thresh < 1.25 ** 3).mean()
rmse = (gt - pred) ** 2
rmse = np.sqrt(rmse.mean())
rmse_log = (np.log(gt) - np.log(pred)) ** 2
rmse_log = np.sqrt(rmse_log.mean())
abs_rel = np.mean(np.abs(gt - pred) / gt)
sq_rel = np.mean(((gt - pred)**2) / gt)
err = np.log(pred) - np.log(gt)
silog = np.sqrt(np.mean(err ** 2) - np.mean(err) ** 2) * 100
err = np.abs(np.log10(pred) - np.log10(gt))
log10 = np.mean(err)
return silog, log10, abs_rel, sq_rel, rmse, rmse_log, d1, d2, d3
def test():
global gt_depths, missing_ids, pred_filenames
gt_depths = []
missing_ids = set()
pred_filenames = []
for root, dirnames, filenames in os.walk(args.pred_path):
for pred_filename in fnmatch.filter(filenames, '*.png'):
if 'cmap' in pred_filename or 'gt' in pred_filename:
continue
dirname = root.replace(args.pred_path, '')
pred_filenames.append(os.path.join(dirname, pred_filename))
num_test_samples = len(pred_filenames)
pred_depths = []
for i in range(num_test_samples):
pred_depth_path = os.path.join(args.pred_path, pred_filenames[i])
pred_depth = cv2.imread(pred_depth_path, -1)
if pred_depth is None:
print('Missing: %s ' % pred_depth_path)
missing_ids.add(i)
continue
if args.dataset == 'nyu':
pred_depth = pred_depth.astype(np.float32) / 1000.0
else:
pred_depth = pred_depth.astype(np.float32) / 256.0
pred_depths.append(pred_depth)
print('Raw png files reading done')
print('Evaluating {} files'.format(len(pred_depths)))
if args.dataset == 'kitti':
for t_id in range(num_test_samples):
file_dir = pred_filenames[t_id].split('.')[0]
filename = file_dir.split('_')[-1]
directory = file_dir.replace('_' + filename, '')
gt_depth_path = os.path.join(args.gt_path, directory, 'proj_depth/groundtruth/image_02', filename + '.png')
depth = cv2.imread(gt_depth_path, -1)
if depth is None:
print('Missing: %s ' % gt_depth_path)
missing_ids.add(t_id)
continue
depth = depth.astype(np.float32) / 256.0
gt_depths.append(depth)
elif args.dataset == 'nyu':
for t_id in range(num_test_samples):
file_dir = pred_filenames[t_id].split('.')[0]
filename = file_dir.split('_')[-1]
directory = file_dir.replace('_rgb_'+file_dir.split('_')[-1], '')
gt_depth_path = os.path.join(args.gt_path, directory, 'sync_depth_' + filename + '.png')
depth = cv2.imread(gt_depth_path, -1)
if depth is None:
print('Missing: %s ' % gt_depth_path)
missing_ids.add(t_id)
continue
depth = depth.astype(np.float32) / 1000.0
gt_depths.append(depth)
print('GT files reading done')
print('{} GT files missing'.format(len(missing_ids)))
print('Computing errors')
eval(pred_depths)
print('Done.')
def eval(pred_depths):
num_samples = len(pred_depths)
pred_depths_valid = []
i = 0
for t_id in range(num_samples):
if t_id in missing_ids:
continue
pred_depths_valid.append(pred_depths[t_id])
num_samples = num_samples - len(missing_ids)
silog = np.zeros(num_samples, np.float32)
log10 = np.zeros(num_samples, np.float32)
rms = np.zeros(num_samples, np.float32)
log_rms = np.zeros(num_samples, np.float32)
abs_rel = np.zeros(num_samples, np.float32)
sq_rel = np.zeros(num_samples, np.float32)
d1 = np.zeros(num_samples, np.float32)
d2 = np.zeros(num_samples, np.float32)
d3 = np.zeros(num_samples, np.float32)
for i in range(num_samples):
gt_depth = gt_depths[i]
pred_depth = pred_depths_valid[i]
pred_depth[pred_depth < args.min_depth_eval] = args.min_depth_eval
pred_depth[pred_depth > args.max_depth_eval] = args.max_depth_eval
pred_depth[np.isinf(pred_depth)] = args.max_depth_eval
gt_depth[np.isinf(gt_depth)] = 0
gt_depth[np.isnan(gt_depth)] = 0
valid_mask = np.logical_and(gt_depth > args.min_depth_eval, gt_depth < args.max_depth_eval)
if args.do_kb_crop:
height, width = gt_depth.shape
top_margin = int(height - 352)
left_margin = int((width - 1216) / 2)
pred_depth_uncropped = np.zeros((height, width), dtype=np.float32)
pred_depth_uncropped[top_margin:top_margin + 352, left_margin:left_margin + 1216] = pred_depth
pred_depth = pred_depth_uncropped
if args.garg_crop or args.eigen_crop:
gt_height, gt_width = gt_depth.shape
eval_mask = np.zeros(valid_mask.shape)
if args.garg_crop:
eval_mask[int(0.40810811 * gt_height):int(0.99189189 * gt_height), int(0.03594771 * gt_width):int(0.96405229 * gt_width)] = 1
elif args.eigen_crop:
if args.dataset == 'kitti':
eval_mask[int(0.3324324 * gt_height):int(0.91351351 * gt_height), int(0.0359477 * gt_width):int(0.96405229 * gt_width)] = 1
else:
eval_mask[45:471, 41:601] = 1
valid_mask = np.logical_and(valid_mask, eval_mask)
silog[i], log10[i], abs_rel[i], sq_rel[i], rms[i], log_rms[i], d1[i], d2[i], d3[i] = compute_errors(gt_depth[valid_mask], pred_depth[valid_mask])
print("{:>7}, {:>7}, {:>7}, {:>7}, {:>7}, {:>7}, {:>7}, {:>7}, {:>7}".format(
'd1', 'd2', 'd3', 'AbsRel', 'SqRel', 'RMSE', 'RMSElog', 'SILog', 'log10'))
print("{:7.3f}, {:7.3f}, {:7.3f}, {:7.3f}, {:7.3f}, {:7.3f}, {:7.3f}, {:7.3f}, {:7.3f}".format(
d1.mean(), d2.mean(), d3.mean(),
abs_rel.mean(), sq_rel.mean(), rms.mean(), log_rms.mean(), silog.mean(), log10.mean()))
return silog, log10, abs_rel, sq_rel, rms, log_rms, d1, d2, d3
def main():
test()
if __name__ == '__main__':
main()
(4)评估结果

与论文结果对比:
| 指标 | 论文结果 | NPU结果 | 是否一致 |
|---|---|---|---|
| d1 (δ<1.25) | 0.904 | 0.904 | 一致 |
| d2 (δ<1.25²) | 0.988 | 0.988 | 一致 |
| d3 (δ<1.25³) | 0.998 | 0.998 | 一致 |
| AbsRel | 0.109 | 0.109 | 一致 |
| SqRel | 0.054 | 0.054 | 一致 |
| RMSE | 0.357 | 0.357 | 一致 |
| RMSElog | 0.129 | 0.129 | 一致 |
| SILog | 9.521 | 9.521 | 一致 |
| log10 | 0.045 | 0.045 | 一致 |
结论:DPT模型在昇腾NPU上的NYUv2深度估计评估结果与原论文完全一致,9项指标均无差异,适配验证通过。
(1)数据集准备
下载 ADE20K 数据集(ADE20K202117_01.zip,约1.7GB),解压到 /root/DPT/input/ 目录。
(2)运行评估
多尺度+翻转推理
cd /root/DPT
for i in 0 50 100 150 ... 1950; do
python3 -u eval_ade20k_ms_flip.py $i $(($i+50))
done注:多尺度+翻转每张图6次推理(3尺度×2翻转),需每50张一个子进程
然后聚合结果,执行评估:
import numpy as np, os
tp = np.zeros(151, dtype=np.int64)
fp = np.zeros(151, dtype=np.int64)
fn = np.zeros(151, dtype=np.int64)
# For ms_flip: batch size 50
for f in sorted(os.listdir('eval_ade20k_results_ms_flip_3s')):
d = np.load(os.path.join('eval_ade20k_results_ms_flip_3s', f))
tp += d['tp']; fp += d['fp']; fn += d['fn']
ious = []
for c in range(1, 151):
d = tp[c] + fp[c] + fn[c]
ious.append(tp[c]/d if d > 0 else 0)
print(f"mIoU = {np.mean(ious)*100:.2f}%")(3)评估结果

结论:评估结果48.40%与论文结果49.02%基本一致(-0.62%)。论文以及官方代码都没有公开其具体的尺度设置和增强策略,可能使用了更多尺度(如5尺度)或额外增强。