Respan Dataset Explorer

Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.

5,167,182physical rows
86shards
0.00%qualified row coverage
0.00%qualified cell coverage
Random row JSON API

turns-00085.parquet:54848

71dfcfea5145fbd111425f46
turn 7/16gpt-4.1-mini-2025-04-14ChineseTaiwan1524 words
degenerate_repetitionAbsentFinal dense release
USER
请参照下述代码中读取npy文件的方式:
# -*- coding: utf-8 -*-
"""
@Author  :   zhwzhong
@License :   (C) Copyright 2013-2018, hit
@Contact :   <PRESIDIO_ANONYMIZED_EMAIL_ADDRESS>
@Software:   PyCharm
@File    :   quick_test.py
@Time    :   2021/8/31 20:32
@Desc    :
"""
import glob
import math
import tqdm
import numpy as np
import torch

import utility
from option import args
from data import get_dataloader
from importlib import import_module

args.scale = 8
args.down_type = 'nearest'

device = torch.device('cpu' if args.cpu else 'cuda')
module = import_module('models.' + args.model_name.lower())
model = module.make_model(args).to(device)

model = torch.nn.parallel.DataParallel(model, device_ids=list(range(args.num_gpus)))
print(utility.get_parameter_number(model))
device_id = torch.cuda.current_device()


load_name = './pre_trained/net_{}_x{}.pth'.format(args.down_type, args.scale)
print(load_name)
checkpoint = torch.load(load_name, map_location=lambda storage, loc: storage.cuda(device_id))
model.load_state_dict(checkpoint['state'])
model.eval()

test_set = ['Middlebury', 'Lu', 'test', 'Sintel'] # , 'Middlebury', 'test', 'Sintel'
for test_name in test_set:
    sum_rmse = []
    for gt_name in tqdm.tqdm(sorted(glob.glob('./test_data/{}/gt/*.npy'.format(test_name)))):
        gt_img = np.load(gt_name)
        rgb_img = np.load(gt_name.replace('gt', 'rgb'))

        # Following DKN, we use bicubic in PIL to degrade GT image (for bicubic), and crop the border
        # before calculate the RMSE values. (reference: https://github.com/cvlab-yonsei/dkn/issues/1)
        module = max(int(math.pow(2, 1 + args.num_pyramid)), args.scale)
        tmp_gt = utility.mod_crop(gt_img, modulo=module)
        if args.down_type == 'nearest':
            tmp_gt = (tmp_gt - np.min(tmp_gt)) / (np.max(tmp_gt) - np.min(tmp_gt))
            lr_img = utility.get_lowers(tmp_gt, factor=args.scale, mode=args.down_direction)
        else:
            tmp_gt = (tmp_gt - np.min(tmp_gt)) / (np.max(tmp_gt) - np.min(tmp_gt))
            lr_img = utility.get_lowers(tmp_gt, factor=args.scale, mode='bicubic')
        lr_up = utility.get_lowers(lr_img, factor=1 / args.scale, mode='bicubic')
        lr_img, gt_img = np.expand_dims(lr_img, 0), np.expand_dims(gt_img, 0)
        lr_up = np.expand_dims(lr_up, 0)
        if args.guide_channels == 1:
            rgb_img = np.expand_dims(utility.rgb2gray(rgb_img), 2)

        rgb_img = np.float32(np.transpose(rgb_img, axes=(2, 0, 1))) / 255.

        gt_img, rgb_img = utility.mod_crop(gt_img, modulo=module), utility.mod_crop(rgb_img, modulo=module)

        lr_img, lr_up, gt_img, rgb_img = utility.np_to_tensor(lr_img, lr_up, gt_img, rgb_img)

        lr_img, lr_up, gt_img, rgb_img = lr_img.unsqueeze(0), lr_up.unsqueeze(0), gt_img.unsqueeze(0), rgb_img.unsqueeze(0)
        lr_img, lr_up, gt_img, rgb_img = lr_img.to(device), lr_up.to(device), gt_img.to(device), rgb_img.to(device)

        out = model(lr=lr_img.contiguous(), rgb=rgb_img.contiguous(), lr_up=lr_up.contiguous())[-1]


        if test_name == 'test':
            mul_ratio = 100
        elif test_name == 'Sintel':
            mul_ratio = 255
        else:
            mul_ratio = 1

        rmse, _ = utility.root_mean_sqrt_error(im_pred=out.contiguous(), im_true=gt_img.contiguous(), border=6, mul_ratio=mul_ratio, is_train=False)
        sum_rmse.append(rmse)

    print('{}: {:.2f}'.format(test_name, np.mean(sum_rmse)))
修改下述代码:
import os
import cv2
import glob
import torch
import numpy as np
from ahmf import AHMF

import warnings
warnings.filterwarnings("ignore", category=UserWarning)

def mod_crop(*imgs, modulo):
    def crop(img):
        if img.ndim == 2:
            h, w = img.shape
            crop_h, crop_w = h % modulo, w % modulo
            return img[crop_h // 2 : h - (crop_h - crop_h // 2),
                       crop_w // 2 : w - (crop_w - crop_w // 2)]
        elif img.ndim == 3:
            c, h, w = img.shape
            crop_h, crop_w = h % modulo, w % modulo
            return img[:, crop_h // 2 : h - (crop_h - crop_h // 2),
                       crop_w // 2 : w - (crop_w - crop_w // 2)]
        else:
            raise ValueError(f"Unsupported image dimension: {img.ndim}")
    out = [crop(img) for img in imgs]
    return out if len(out) > 1 else out[0]

def np_to_tensor(*imgs, input_range=1.0, output_range=1.0):
    def _to_tensor(img):
        arr = img.astype(np.float32)
        if arr.ndim == 2:
            arr = np.expand_dims(arr, 0)
        tensor = torch.from_numpy(arr).float()
        tensor *= output_range / input_range
        return tensor
    out = [_to_tensor(img) for img in imgs]
    return out if len(out) > 1 else out[0]

def quantize(img, rgb_range):
    pixel_range = 255 / rgb_range
    return img.mul(pixel_range).clamp(0, 255).round().div(pixel_range)

def imresize(img, scale_factor):
    if img.ndim == 2:
        h, w = img.shape
        new_h, new_w = int(h * scale_factor), int(w * scale_factor)
        resized = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
        return resized
    elif img.ndim == 3:
        c, h, w = img.shape
        new_h, new_w = int(h * scale_factor), int(w * scale_factor)
        img_hw_c = img.transpose(1, 2, 0)
        resized = cv2.resize(img_hw_c, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
        return resized.transpose(2, 0, 1)
    else:
        raise ValueError("Input array must be 2D or 3D")

def normalize_img(img):
    if img.dtype == np.uint8:
        return img.astype(np.float32) / 255.0, 255
    elif img.dtype == np.uint16:
        return img.astype(np.float32) / 65535.0, 65535
    else:
        return img.astype(np.float32), 255

def calc_rmse(pred, target):
    """计算RMSE,输入均为numpy整数数组"""
    pred = pred.astype(np.float64)
    target = target.astype(np.float64)
    return np.sqrt(np.mean((pred - target) ** 2))

def main():
    scale = 4    
    model = AHMF(scale=scale, act='PReLU', in_channels=1, guidance_channels=3)
    ckpt_path = f'/home/jianruobing/code/AHMF-main/AHMF-test/model_x{scale}.pth'
    checkpoint = torch.load(ckpt_path, map_location='cpu')
    model.load_state_dict(checkpoint['state_dict'])
    model = model.cuda().eval()
    
    input_dir = '/home/jianruobing/code/AHMF-main/imgs'
    output_dir = './output'
    os.makedirs(output_dir, exist_ok=True)

    exts = ['png', 'jpg', 'jpeg']
    all_files = []
    for ext in exts:
        all_files.extend(glob.glob(os.path.join(input_dir, f'*_depth.{ext}')))
        all_files.extend(glob.glob(os.path.join(input_dir, f'*_depth_x4.{ext}')))
        all_files.extend(glob.glob(os.path.join(input_dir, f'*_rgb.{ext}')))

    groups = {}
    for fpath in all_files:
        fname = os.path.basename(fpath)
        if '_depth_x4' in fname:
            prefix = fname.split('_depth_x4')[0]
            key = 'depth_x4'
        elif '_depth' in fname:
            prefix = fname.split('_depth')[0]
            key = 'depth'
        elif '_rgb' in fname:
            prefix = fname.split('_rgb')[0]
            key = 'rgb'
        else:
            continue
        groups.setdefault(prefix, {})[key] = fpath

    print(f'Found {len(groups)} image groups.')

    for prefix, files in groups.items():
        if not {'depth', 'depth_x4', 'rgb'}.issubset(files.keys()):
            print(f'Warning: missing files for {prefix}, skipped.')
            continue

        dep = cv2.imread(files['depth'], cv2.IMREAD_UNCHANGED)
        lr = cv2.imread(files['depth_x4'], cv2.IMREAD_UNCHANGED)
        rgb = cv2.imread(files['rgb'], cv2.IMREAD_COLOR)

        print(f"{prefix}: Input dep dtype = {dep.dtype}, shape = {dep.shape}")
        print(f"{prefix}: Input rgb dtype = {rgb.dtype}, shape = {rgb.shape}")
        print(f"{prefix}: Input lr dtype = {lr.dtype}, shape = {lr.shape}")

        if dep is None or lr is None or rgb is None:
            print(f'Failed loading images for {prefix}')
            continue
        
        rgb = cv2.cvtColor(rgb, cv2.COLOR_BGR2RGB)

        # 归一化输入图像,获取最大值(255或65535)
        dep_norm, dep_max = normalize_img(dep)
        lr_norm, lr_max = normalize_img(lr)
        rgb_norm = rgb.astype(np.float32) / 255.0

        dep_norm, rgb_norm = mod_crop(np.expand_dims(dep_norm, 0), rgb_norm.transpose(2, 0, 1), modulo=32)
        lr_norm = mod_crop(lr_norm, modulo=32)

        # 上采样 lr
        lr_up = imresize(lr_norm, scale)

        if lr_norm.ndim == 2:
            lr_norm = np.expand_dims(lr_norm, 0)
        if lr_up.ndim == 2:
            lr_up = np.expand_dims(lr_up, 0)

        dep_t, rgb_t, lr_t, lr_up_t = np_to_tensor(dep_norm, rgb_norm, lr_norm, lr_up)
        dep_t = dep_t.unsqueeze(0).cuda()
        rgb_t = rgb_t.unsqueeze(0).cuda()
        lr_t = lr_t.unsqueeze(0).cuda()
        lr_up_t = lr_up_t.unsqueeze(0).cuda()
        
        inputs = {
            'img_gt': dep_t,
            'img_rgb': rgb_t,
            'img_lr': lr_t,
            'lr_up': lr_up_t,
        }

        with torch.no_grad():
            out = model(inputs)['img_out']

        # 反归一化到原始像素范围
        out_img = out.squeeze().cpu().numpy()
        out_img = np.clip(out_img, 0, 1) * dep_max

        # 转为对应整数格式
        if dep.dtype == np.uint8:
            out_img_int = np.round(out_img).astype(np.uint8)
            dep_int = dep
        elif dep.dtype == np.uint16:
            out_img_int = np.round(out_img).astype(np.uint16)
            dep_int = dep
        else:
            out_img_int = out_img.astype(np.float32)
            dep_int = dep.astype(np.float32)

        # 计算RMSE,基于整型图像
        rmse_val = calc_rmse(out_img_int, dep_int)
        print(f'{prefix}: RMSE = {rmse_val:.4f}')

        save_path = os.path.join(output_dir, f'{prefix}_sr.png')
        # 保存时强制转换为兼容格式
        cv2.imwrite(save_path, out_img_int)
        print(f'Saved SR image to {save_path}')

if __name__ == '__main__':
    main()
ASSISTANT
你给的代码主要是用cv2读取图像的,同时你想“参照下面代码中读取npy的方式”,看起来是想把图像读取与预处理方式和`quick_test.py`里对npy读取及处理的兼容性结合起来。

在你现有代码中,读取深度图、低分辨率图为`cv2.imread(..., cv2.IMREAD_UNCHANGED)`,RGB图为彩色读取(然后转RGB),然后对深度图进行归一化(`normalize_img`),对RGB做归一化后转CHW,`mod_crop`裁剪后转tensor,送入模型。  
而`quick_test.py`里是直接用`np.load`加载npy数组,然后类似归一化和裁边。

---

### 你要“参照读取npy的方式”,我理解的主要改动点是:

- 你要读取的`depth`、`depth_x4`、`rgb`都是用npy格式存在的。
- 你要对npy数组进行同样的归一化、裁剪及tensor转化处理。
- 后续模型输入和RMSE计算逻辑保持不变。

---

### 基于你给的代码,我给你一份修改方案:

```python
import os
import glob
import torch
import numpy as np
from ahmf import AHMF

import warnings
warnings.filterwarnings("ignore", category=UserWarning)

def mod_crop(*imgs, modulo):
    def crop(img):
        if img.ndim == 2:
            h, w = img.shape
            crop_h, crop_w = h % modulo, w % modulo
            return img[crop_h // 2 : h - (crop_h - crop_h // 2),
                       crop_w // 2 : w - (crop_w - crop_w // 2)]
        elif img.ndim == 3:
            c, h, w = img.shape
            crop_h, crop_w = h % modulo, w % modulo
            return img[:, crop_h // 2 : h - (crop_h - crop_h // 2),
                       crop_w // 2 : w - (crop_w - crop_w // 2)]
        else:
            raise ValueError(f"Unsupported image dimension: {img.ndim}")
    out = [crop(img) for img in imgs]
    return out if len(out) > 1 else out[0]

def np_to_tensor(*imgs, input_range=1.0, output_range=1.0):
    def _to_tensor(img):
        arr = img.astype(np.float32)
        if arr.ndim == 2:
            arr = np.expand_dims(arr, 0)
        tensor = torch.from_numpy(arr).float()
        tensor *= output_range / input_range
        return tensor
    out = [_to_tensor(img) for img in imgs]
    return out if len(out) > 1 else out[0]

def normalize_img(img):
    # 保持原数据类型,并归一化(和quick_test类似)
    if img.dtype == np.uint8:
        return img.astype(np.float32) / 255.0, 255
    elif img.dtype == np.uint16:
        return img.astype(np.float32) / 65535.0, 65535
    else:
        # float类型或者其它
        max_val = float(np.max(img)) if np.max(img) > 1.0 else 1.0
        return img.astype(np.float32) / max_val, max_val

def calc_rmse(pred, target):
    pred = pred.astype(np.float64)
    target = target.astype(np.float64)
    return np.sqrt(np.mean((pred - target) ** 2))

def main():
    scale = 4    
    model = AHMF(scale=scale, act='PReLU', in_channels=1, guidance_channels=3)
    ckpt_path = f'/home/jianruobing/code/AHMF-main/AHMF-test/model_x{scale}.pth'
    checkpoint = torch.load(ckpt_path, map_location='cpu')
    model.load_state_dict(checkpoint['state_dict'])
    model = model.cuda().eval()
    
    input_dir = '/home/jianruobing/code/AHMF-main/imgs'
    output_dir = './output'
    os.makedirs(output_dir, exist_ok=True)

    # 查找npy文件
    all_files = []
    all_files.extend(glob.glob(os.path.join(input_dir, '*_depth.npy')))
    all_files.extend(glob.glob(os.path.join(input_dir, '*_depth_x4.npy')))
    all_files.extend(glob.glob(os.path.join(input_dir, '*_rgb.npy')))

    # 按前缀分组
    groups = {}
    for fpath in all_files:
        fname = os.path.basename(fpath)
        if '_depth_x4' in fname:
            prefix = fname.split('_depth_x4')[0]
            key = 'depth_x4'
        elif '_depth' in fname:
            prefix = fname.split('_depth')[0]
            key = 'depth'
        elif '_rgb' in fname:
            prefix = fname.split('_rgb')[0]
            key = 'rgb'
        else:
            continue
        groups.setdefault(prefix, {})[key] = fpath

    print(f'Found {len(groups)} image groups.')

    for prefix, files in groups.items():
        if not {'depth', 'depth_x4', 'rgb'}.issubset(files.keys()):
            print(f'Warning: missing files for {prefix}, skipped.')
            continue

        dep = np.load(files['depth'])
        lr = np.load(files['depth_x4'])
        rgb = np.load(files['rgb'])

        print(f"{prefix}: Input dep dtype = {dep.dtype}, shape = {dep.shape}")
        print(f"{prefix}: Input rgb dtype = {rgb.dtype}, shape = {rgb.shape}")
        print(f"{prefix}: Input lr dtype = {lr.dtype}, shape = {lr.shape}")

        # 归一化
        dep_norm, dep_max = normalize_img(dep)
        lr_norm, lr_max = normalize_img(lr)
        # rgb假设shape H, W, C,转CHW并归一化(0~1)
        rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0

        # mod_crop,深度和rgb都扩1维(channel),rgb转CHW在前面完成了
        dep_norm = mod_crop(np.expand_dims(dep_norm, 0), modulo=32)
        rgb_norm = mod_crop(rgb_norm, modulo=32)
        lr_norm = mod_crop(lr_norm, modulo=32)

        # lr上采样用cv2一样双线性插值
        h, w = lr_norm.shape[-2], lr_norm.shape[-1]  # 对应深度h,w
        import cv2
        lr_up = cv2.resize(lr_norm, (w * scale, h * scale), interpolation=cv2.INTER_LINEAR)

        # 确保shape维度对应
        if lr_norm.ndim == 2:
            lr_norm = np.expand_dims(lr_norm, 0)
        if lr_up.ndim == 2:
            lr_up = np.expand_dims(lr_up, 0)

        # 转tensor并增加batch维
        dep_t, rgb_t, lr_t, lr_up_t = np_to_tensor(dep_norm, rgb_norm, lr_norm, lr_up)
        dep_t = dep_t.unsqueeze(0).cuda()
        rgb_t = rgb_t.unsqueeze(0).cuda()
        lr_t = lr_t.unsqueeze(0).cuda()
        lr_up_t = lr_up_t.unsqueeze(0).cuda()

        inputs = {
            'img_gt': dep_t,
            'img_rgb': rgb_t,
            'img_lr': lr_t,
            'lr_up': lr_up_t,
        }

        with torch.no_grad():
            out = model(inputs)['img_out']

        # 反归一化还原
        out_img = out.squeeze().cpu().numpy()
        out_img = np.clip(out_img, 0, 1) * dep_max

        # 转回整数格式做RMSE计算
        if dep.dtype == np.uint8:
            out_img_int = np.round(out_img).astype(np.uint8)
            dep_int = dep
        elif dep.dtype == np.uint16:
            out_img_int = np.round(out_img).astype(np.uint16)
            dep_int = dep
        else:
            # float或者其它类型(如int32),保持float32,RMSE时会自动转换
            out_img_int = out_img.astype(np.float32)
            dep_int = dep.astype(np.float32)

        rmse_val = calc_rmse(out_img_int, dep_int)
        print(f'{prefix}: RMSE = {rmse_val:.4f}')

        # 保存结果图为png,需先转uint8或uint16
        save_path = os.path.join(output_dir, f'{prefix}_sr.png')
        import cv2
        cv2.imwrite(save_path, out_img_int)
        print(f'Saved SR image to {save_path}')

if __name__ == '__main__':
    main()
```

---

### 说明

- 读取`npy`方式用的是`np.load`,对应你给的`quick_test.py`读取方式。
- 归一化方式和`quick_test.py`的处理保持吻合(归一化到[0,1],保留最大值)。
- rgb从HWC转换为CHW:`np.transpose(rgb, (2,0,1))`,并除255归一化。
- `mod_crop`改造成接受CHW格式图像。
- 双线性插值用cv2的`cv2.resize`实现,与之前函数一致。
- 转Tensor部分用你示例里的`np_to_tensor`。
- RMSE计算调用你的`calc_rmse`。
- 输出后恢复到原始像素值范围,转为整数类型保存PNG。

这样修改后,你的输入数据结构和预处理流程基本与`quick_test.py`似的,方便对接。

如果你还有其它细节需要同步,可以告诉我!

turns-00085.parquet:54849

0abcaebb5f13da2dd2db4af6
turn 8/16gpt-4.1-mini-2025-04-14ChineseTaiwan470 words
degenerate_repetitionAbsentFinal dense release
USER
Found 6 image groups.
sintel_frame_0001_bix2: Input dep dtype = uint8, shape = (218, 512)
sintel_frame_0001_bix2: Input rgb dtype = uint8, shape = (218, 512, 3)
sintel_frame_0001_bix2: Input lr dtype = uint8, shape = (54, 128)
depth.shape=torch.Size([1, 64, 32, 128]), guidance.shape=torch.Size([1, 64, 48, 128])
Traceback (most recent call last):
  File "test3.py", line 169, in <module>
    main()
  File "test3.py", line 141, in main
    out = model(inputs)['img_out']
  File "/home/jianruobing/anaconda3/envs/UHDFour/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "/home/jianruobing/code/AHMF-main/AHMF-test/ahmf.py", line 364, in forward
    tmp = self.fuse_conv[i](depth=depth_out[3 - i],
  File "/home/jianruobing/anaconda3/envs/UHDFour/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "/home/jianruobing/code/AHMF-main/AHMF-test/ahmf.py", line 293, in forward
    depth, guide = self.attention_layer(depth=depth, guidance=guide)
  File "/home/jianruobing/anaconda3/envs/UHDFour/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "/home/jianruobing/code/AHMF-main/AHMF-test/ahmf.py", line 273, in forward
    fuse_feature = self.squeeze(torch.cat((depth, guidance), 1))
RuntimeError: Sizes of tensors must match except in dimension 1. Expected size 32 but got size 48 for tensor number 1 in the list.
ASSISTANT
你的报错信息:

```
RuntimeError: Sizes of tensors must match except in dimension 1. Expected size 32 but got size 48 for tensor number 1 in the list.
```

发生在模型中对depth和guidance(rgb)两个张量在`dim=1`以外的其他维度拼接时报错,意思是两个tensor在拼接维度以外的尺寸不一致。

具体到你的日志:

```
depth.shape=torch.Size([1, 64, 32, 128])  # B, C, H, W 这里H=32
guidance.shape=torch.Size([1, 64, 48, 128])  # B, C, H, W 这里H=48
```

第3维(height)不匹配,分别是`32`和`48`。

---

## 结合你的代码和说明,排查原因:

### 1. 查看输入数据流程是否一致、对应的空间尺寸错开了

你定义的 `mod_crop` 对深度图和rgb的处理是:

```python
dep_norm = mod_crop(np.expand_dims(dep_norm, 0), modulo=32)
rgb_norm = mod_crop(rgb_norm, modulo=32)
```

这里 `dep_norm` 是 `[1, H, W]`,`rgb_norm` 是 `[C, H, W]`。`mod_crop`函数针对二维单通道和三维图(C,H,W)处理挺合理。

问题可能是:

- 你对`dep_norm`先`expand_dims`成`(1, H, W)`,然后裁剪后依然是`(1, h_crop, w_crop)`
- `rgb_norm`是`(3, H, W)`,裁剪后`(3, h_crop, w_crop)`,注意这里维度差异。

但最终给模型的是 `dep_t` 和 `rgb_t`,维度应该是一致的。

----

### 2. 你打印的`depth.shape=torch.Size([1, 64, 32, 128])`和`guidance.shape=torch.Size([1, 64, 48, 128])`

一般通道数64,空间尺寸32*128 vs 48*128,这表明:

- 你的深度图对应的`H`是32
- 指导图的`H`是48

为啥尺寸会差这么多?

---

### 3. 原因很可能是`rgb_norm`和`dep_norm`起初空间尺寸不匹配

可能在原始图片的裁剪,归一化,或者resize环节导致空间大小不一致。

---

### 4. 结合你代码`main`中相关部分:

```python
dep_norm = mod_crop(np.expand_dims(dep_norm, 0), modulo=32)
rgb_norm = mod_crop(rgb_norm, modulo=32)
lr_norm = mod_crop(lr_norm, modulo=32)
```

如果`dep_norm`是 `(1, H, W)`,裁剪后H是32;而`rgb_norm`是 `(3, H, W)`,但从打印结果看H是48。

意味着:

- `dep_norm`的尺寸是`32x128`
- `rgb_norm`裁剪过后尺寸是`48x128`

这里明显不匹配。

---

### 5. 可能是因为你的`rgb_norm`在`normalize_img`之前没有裁剪,也可能是`rgb_norm`还没转成`CHW`之前做了裁剪,顺序问题。

---

### 6. 建议你修改**裁剪顺序**,确保`rgb`转`CHW`后,再统一裁剪:

```python
rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0  # 转CHW归一化

# 然后再统一裁剪:
dep_norm, rgb_norm, lr_norm = mod_crop(np.expand_dims(dep_norm, 0), rgb_norm, lr_norm, modulo=32)
```

这样`dep_norm`是(1, h, w),`rgb_norm`是(3, h, w),`lr_norm`是(1, h, w),都被一致裁剪,保证`h,w`相同。

---

### 7. 你当前的代码是:

```python
dep_norm = mod_crop(np.expand_dims(dep_norm, 0), modulo=32)
rgb_norm = mod_crop(rgb_norm, modulo=32)
lr_norm = mod_crop(lr_norm, modulo=32)
```

把上面改成:

```python
dep_norm, rgb_norm, lr_norm = mod_crop(np.expand_dims(dep_norm, 0), rgb_norm, lr_norm, modulo=32)
```

---

### 8. 另一点是你打印的`depth.shape=torch.Size([1, 64, 32, 128])`,这里`64`是通道,`32 x 128`是空间尺寸,`guidance.shape`是`[1, 64, 48, 128]`,通道数也相同,但是空间高度不同。

这说明模型内部对`rgb`做了某些下采样或者padding,可能也和数据输入尺寸有关,保持输入尺寸一致极其重要。

---

### 9. 如果mod_crop调整后仍出现,则建议你打印所有输入张量的shape,特别是输入模型之前的:

```python
print(f"dep_t shape: {dep_t.shape}")
print(f"rgb_t shape: {rgb_t.shape}")
print(f"lr_t shape: {lr_t.shape}")
print(f"lr_up_t shape: {lr_up_t.shape}")
```

确认它们空间大小是不是一致。

---

## 总结

**最重要修改**:

```python
# 转到CHW之后,一起裁剪
rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0

dep_norm, rgb_norm, lr_norm = mod_crop(np.expand_dims(dep_norm, 0), rgb_norm, lr_norm, modulo=32)
```

而不是分开单独裁剪。

---

## 附完整裁剪部分示例

```python
# 归一化
dep_norm, dep_max = normalize_img(dep)
lr_norm, lr_max = normalize_img(lr)
rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0

# 统一裁剪
dep_norm, rgb_norm, lr_norm = mod_crop(np.expand_dims(dep_norm, 0), rgb_norm, lr_norm, modulo=32)
```

---

这样你就能保证输入模型的`dep_t`和`rgb_t`的空间尺寸一致,防止在模型内部拼接时报错。  
确认修改后重新运行试试。

---

如果问题依旧,请贴出`dep_norm.shape`, `rgb_norm.shape`, `lr_norm.shape`裁剪前后的尺寸,我帮你详细分析。

turns-00085.parquet:54850

3360ea54c02cd094f0d576e3
turn 9/16gpt-4.1-mini-2025-04-14ChineseTaiwan143 words
degenerate_repetitionAbsentFinal dense release
USER
mod_crop这个函数是起什么作用的
ASSISTANT
`mod_crop` 这个函数的作用是**裁剪图像的高和宽,使其尺寸变成指定数字 `modulo` 的整数倍**,避免输入图像尺寸因为不能被网络结构中特定模块(如卷积、下采样、金字塔、对齐等)整除而发生形状不匹配的问题。

---

### 为什么需要`mod_crop`?

多数深度学习模型的卷积结构,比如有池化或者步长为2的卷积层,多次下采样后,输入尺寸必须满足一定的“对齐”条件(尺寸必须是某个数的倍数),否则网络中间的feature map尺寸会导致shape不匹配错误。

举个例子:

- 网络有4层下采样,每次采样尺度缩小2倍,则输入高宽须是 \(2^4 = 16\) 的倍数。
- 如果输入图像尺寸是 `(218, 512)`,218 不是16的倍数,就会导致网络中间尺寸错误。
- `mod_crop`函数会切掉边界几行几列,裁剪成满足条件的尺寸。

---

### `mod_crop`怎么做的?

对每张图像的高和宽计算 `crop_h = h % modulo`,即高维度除以 `modulo` 的余数,然后把余数均匀从两边裁剪:

示例(二维图像):

```python
def crop(img):
    h, w = img.shape
    crop_h, crop_w = h % modulo, w % modulo
    # 从上下裁剪 crop_h 行(尽量均匀裁)
    img_cropped = img[
        crop_h // 2 : h - (crop_h - crop_h // 2),
        crop_w // 2 : w - (crop_w - crop_w // 2)
    ]
    return img_cropped
```

三维(C,H,W)时高宽同理:

```python
def crop(img):
    c, h, w = img.shape
    crop_h, crop_w = h % modulo, w % modulo
    img_cropped = img[
        :,
        crop_h // 2 : h - (crop_h - crop_h // 2),
        crop_w // 2 : w - (crop_w - crop_w // 2)
    ]
    return img_cropped
```

---

### 总结

- `mod_crop`用来保证网络输入尺寸满足被`modulo`整除的要求。
- 它通过裁剪图像边界像素,实现把图像高宽调整为`modulo`的倍数。
- 这是深度学习图像处理中的经典预处理步骤,尤其是对多尺度、多层网络结构很重要。

如果没有这个裁剪,网络的卷积下采样过程可能出现尺寸不对齐,报错或者模型性能下降。

turns-00085.parquet:54851

d1696c84fe530bfc2094ba60
turn 10/16gpt-4.1-mini-2025-04-14ChineseTaiwan142 words
degenerate_repetitionAbsentFinal dense release
USER
module = max(int(math.pow(2, 1 + args.num_pyramid)), args.scale)这个代码又是什么意思
ASSISTANT
这段代码:

```python
module = max(int(math.pow(2, 1 + args.num_pyramid)), args.scale)
```

的含义是计算一个“模数”(`module`),用于保证图像尺寸符合网络结构对输入大小的要求。

---

### 具体解释:

- `args.num_pyramid` 是网络里**金字塔层数**(pyramid层数,通常表示多尺度特征提取的层级数),比如金字塔层数为2或3。
- `math.pow(2, 1 + args.num_pyramid)`:计算 \(2^{1 + \text{num_pyramid}}\),也就是2的`(1 + num_pyramid)`次方。

举个例子:

| `args.num_pyramid` | 计算结果 (2^(1 + num_pyramid)) |
|-------------------|-------------------------------|
| 1                 | \(2^{2}=4\)                   |
| 2                 | \(2^{3}=8\)                   |
| 3                 | \(2^{4}=16\)                  |

- 然后将这个值和 `args.scale` (通常是超分辨率缩放倍数,如4、8等)做比较,取较大值作为 `module`。

---

### 为什么要这么做?

- 通常金字塔结构下,网络对输入尺寸有要求:输入的高宽必须是 \(2^{1 + \text{num_pyramid}}\) 的倍数,否则多次下采样、特征融合时,尺寸会不匹配。
- 同时,因为是超分网络,最终输出尺寸会放大 `args.scale` 倍,所以尺寸也至少应该是 `args.scale` 的倍数。
- 取两者中较大者,保证:
  - 满足网络金字塔结构尺寸限制;
  - 同时满足尺度放大约束。

---

### 代码作用总结:

```python
module = max(2^{1 + num_pyramid}, scale)
```

- `module` 即这个“模数”,用来裁剪输入图像的高宽,使其是 `module` 的整数倍。

- 举例:如果 `num_pyramid=2`, `scale=8`,则

```python
module = max(2^{3}, 8) = max(8, 8) = 8
```

此时输入图的高宽必须是8的倍数。

- 如果 `num_pyramid=3`,`scale=4`,则

```python
module = max(2^{4}, 4) = max(16, 4) = 16
```

输入尺寸需是16的倍数。

---

### 使用场景

配合前面`mod_crop`函数,将输入图像裁剪到高宽为 `module` 的整数倍,从而保证模型运行时尺寸匹配,无shape报错。

---

如果还想更深入理解,可以告诉我 `args.num_pyramid`、`args.scale` 的具体取值,我可以帮你算算实际尺寸要求。

turns-00085.parquet:54852

290afdbe906576125cc2c2ba
turn 11/16gpt-4.1-mini-2025-04-14ChineseTaiwan466 words
degenerate_repetitionAbsentFinal dense release
USER
请帮我修改下述代码,使得不能被32整除的图像被填充到能被32整除的大小,并且不影响最终图像输出
ASSISTANT
你目前的代码中使用了 `mod_crop` 函数来裁剪图像,使其尺寸能被 `module` 整除。但你希望改成**对不能被32整除的图像进行填充(padding)到能被32整除的大小**,并且**保证最终输出不受填充的影响**(即还原到原始尺寸)。下面帮你修改代码实现这个功能。

---

## 方案思路

1. **定义 `mod_pad` 函数**  
   将输入图像填充到高宽能被32整除(即`module=32`),边界在图像右侧和下侧补0(或其他值),同时记录填充像素的数量。

2. **模型输入使用填充后的图像,保证尺寸符合网络需求。**

3. **模型推理结束后,裁剪输出结果回原始大小,去除填充部分。**

---

## 代码示范

```python
import numpy as np
import torch
import cv2

def mod_pad(img, modulo=32):
    """
    对输入图像填充(pad)使其H和W变成modulo的整数倍
    pad方式是在下边和右边补0
    img: numpy array, 支持2D (H,W)或3D (C,H,W)
    返回:pad后图像和裁剪信息 (pad_bottom, pad_right)
    """
    if img.ndim == 2:
        h, w = img.shape
        pad_h = (modulo - h % modulo) % modulo
        pad_w = (modulo - w % modulo) % modulo
        padded_img = np.pad(img, ((0,pad_h),(0,pad_w)), mode='constant')
    elif img.ndim == 3:
        c, h, w = img.shape
        pad_h = (modulo - h % modulo) % modulo
        pad_w = (modulo - w % modulo) % modulo
        padded_img = np.pad(img, ((0,0),(0,pad_h),(0,pad_w)), mode='constant')
    else:
        raise ValueError(f"Unsupported img ndim {img.ndim}")
    return padded_img, pad_h, pad_w

# 示例调用:
# dep_norm_padded, pad_h, pad_w = mod_pad(np.expand_dims(dep_norm, 0), modulo=32)

# 推理后裁剪回原始尺寸
def crop_to_original(img, pad_h, pad_w):
    """
    输入张量或numpy数组,裁剪回去padding之前大小
    支持numpy数组或torch tensor,维度支持[B,C,H,W]或[C,H,W]或[H,W]
    """
    if isinstance(img, torch.Tensor):
        if img.dim() == 4:
            return img[..., :-pad_h if pad_h !=0 else None, :-pad_w if pad_w !=0 else None]
        elif img.dim() == 3:
            return img[:, :-pad_h if pad_h !=0 else None, :-pad_w if pad_w !=0 else None]
        elif img.dim() == 2:
            return img[:-pad_h if pad_h !=0 else None, :-pad_w if pad_w !=0 else None]
        else:
            raise ValueError(f"Unsupported tensor dim {img.dim()}")
    else:
        # numpy数组
        if img.ndim == 4:
            return img[..., :-pad_h if pad_h !=0 else None, :-pad_w if pad_w !=0 else None]
        elif img.ndim ==3:
            return img[:, :-pad_h if pad_h !=0 else None, :-pad_w if pad_w !=0 else None]
        elif img.ndim == 2:
            return img[:-pad_h if pad_h !=0 else None, :-pad_w if pad_w !=0 else None]
        else:
            raise ValueError(f"Unsupported numpy ndim {img.ndim}")

# -----------------
# 下面是你代码中核心部分的示范修改

# 归一化
dep_norm, dep_max = normalize_img(dep)
lr_norm, lr_max = normalize_img(lr)
rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0

# 先pad填充到32的倍数
dep_norm_padded, pad_h_dep, pad_w_dep = mod_pad(np.expand_dims(dep_norm, 0), modulo=32)
rgb_norm_padded, pad_h_rgb, pad_w_rgb = mod_pad(rgb_norm, modulo=32)
lr_norm_padded, pad_h_lr, pad_w_lr = mod_pad(lr_norm, modulo=32)

# 三个pad大小应该一样,建议断言一下
assert pad_h_dep == pad_h_rgb == pad_h_lr, "填充h不一致!"
assert pad_w_dep == pad_w_rgb == pad_w_lr, "填充w不一致!"

# 上采样 lr
import cv2
h_lr, w_lr = lr_norm_padded.shape[-2], lr_norm_padded.shape[-1]
lr_up = cv2.resize(lr_norm_padded, (w_lr * scale, h_lr * scale), interpolation=cv2.INTER_LINEAR)
if lr_up.ndim == 2:
    lr_up = np.expand_dims(lr_up, 0)

# 转tensor并加batch维
dep_t, rgb_t, lr_t, lr_up_t = np_to_tensor(dep_norm_padded, rgb_norm_padded, lr_norm_padded, lr_up)
dep_t = dep_t.unsqueeze(0).cuda()
rgb_t = rgb_t.unsqueeze(0).cuda()
lr_t = lr_t.unsqueeze(0).cuda()
lr_up_t = lr_up_t.unsqueeze(0).cuda()

inputs = {
    'img_gt': dep_t,
    'img_rgb': rgb_t,
    'img_lr': lr_t,
    'lr_up': lr_up_t,
}

with torch.no_grad():
    out = model(inputs)['img_out']

# 裁剪回原始尺寸
out = crop_to_original(out, pad_h_dep, pad_w_dep)

# 反归一化
out_img = out.squeeze().cpu().numpy()
out_img = np.clip(out_img, 0, 1) * dep_max

# 根据输入类型转整型
if dep.dtype == np.uint8:
    out_img_int = np.round(out_img).astype(np.uint8)
    dep_int = dep
elif dep.dtype == np.uint16:
    out_img_int = np.round(out_img).astype(np.uint16)
    dep_int = dep
else:
    out_img_int = out_img.astype(np.float32)
    dep_int = dep.astype(np.float32)

# 计算RMSE
rmse_val = calc_rmse(out_img_int, dep_int)
print(f'{prefix}: RMSE = {rmse_val:.4f}')

# 保存图像(注意size已经是原始大小)
cv2.imwrite(os.path.join(output_dir, f'{prefix}_sr.png'), out_img_int)
```

---

# 总结

- 使用`mod_pad`对输入图片做填充,保证尺寸为32的倍数(或其他modulo)。
- 整个推理流程使用填充后的图片输入模型,保证不报错。
- 推理完成后用`crop_to_original`裁剪掉多余的填充部分,保证输出size和原始输入一致。
- 这样改不会影响最终效果但可以避免尺寸不匹配发生错误。

如果需要,我可以帮你把完整代码用这个方式实现。

turns-00085.parquet:54853

b4e59c901e576a7f2adb44be
turn 12/16gpt-4.1-mini-2025-04-14ChineseTaiwan1258 words
degenerate_repetitionAbsentFinal dense release
USER
把上述功能加入这个代码:
import os
import glob
import torch
import numpy as np
from ahmf import AHMF

import warnings
warnings.filterwarnings("ignore", category=UserWarning)

def mod_crop(*imgs, modulo):
    def crop(img):
        if img.ndim == 2:
            h, w = img.shape
            crop_h, crop_w = h % modulo, w % modulo
            return img[crop_h // 2 : h - (crop_h - crop_h // 2),
                       crop_w // 2 : w - (crop_w - crop_w // 2)]
        elif img.ndim == 3:
            c, h, w = img.shape
            crop_h, crop_w = h % modulo, w % modulo
            return img[:, crop_h // 2 : h - (crop_h - crop_h // 2),
                       crop_w // 2 : w - (crop_w - crop_w // 2)]
        else:
            raise ValueError(f"Unsupported image dimension: {img.ndim}")
    out = [crop(img) for img in imgs]
    return out if len(out) > 1 else out[0]

def np_to_tensor(*imgs, input_range=1.0, output_range=1.0):
    def _to_tensor(img):
        arr = img.astype(np.float32)
        if arr.ndim == 2:
            arr = np.expand_dims(arr, 0)
        tensor = torch.from_numpy(arr).float()
        tensor *= output_range / input_range
        return tensor
    out = [_to_tensor(img) for img in imgs]
    return out if len(out) > 1 else out[0]

def normalize_img(img):
    # 保持原数据类型,并归一化(和quick_test类似)
    if img.dtype == np.uint8:
        return img.astype(np.float32) / 255.0, 255
    elif img.dtype == np.uint16:
        return img.astype(np.float32) / 65535.0, 65535
    else:
        # float类型或者其它
        max_val = float(np.max(img)) if np.max(img) > 1.0 else 1.0
        return img.astype(np.float32) / max_val, max_val

def calc_rmse(pred, target):
    pred = pred.astype(np.float64)
    target = target.astype(np.float64)
    return np.sqrt(np.mean((pred - target) ** 2))

def main():
    scale = 4    
    model = AHMF(scale=scale, act='PReLU', in_channels=1, guidance_channels=3)
    ckpt_path = f'/home/jianruobing/code/AHMF-main/AHMF-test/model_x{scale}.pth'
    checkpoint = torch.load(ckpt_path, map_location='cpu')
    model.load_state_dict(checkpoint['state_dict'])
    model = model.cuda().eval()
    
    input_dir = '/home/jianruobing/code/AHMF-main/imgs2npy'
    output_dir = './output'
    os.makedirs(output_dir, exist_ok=True)

    # 查找npy文件
    all_files = []
    all_files.extend(glob.glob(os.path.join(input_dir, '*_depth.npy')))
    all_files.extend(glob.glob(os.path.join(input_dir, '*_depth_x4.npy')))
    all_files.extend(glob.glob(os.path.join(input_dir, '*_rgb.npy')))

    # 按前缀分组
    groups = {}
    for fpath in all_files:
        fname = os.path.basename(fpath)
        if '_depth_x4' in fname:
            prefix = fname.split('_depth_x4')[0]
            key = 'depth_x4'
        elif '_depth' in fname:
            prefix = fname.split('_depth')[0]
            key = 'depth'
        elif '_rgb' in fname:
            prefix = fname.split('_rgb')[0]
            key = 'rgb'
        else:
            continue
        groups.setdefault(prefix, {})[key] = fpath

    print(f'Found {len(groups)} image groups.')

    for prefix, files in groups.items():
        if not {'depth', 'depth_x4', 'rgb'}.issubset(files.keys()):
            print(f'Warning: missing files for {prefix}, skipped.')
            continue

        dep = np.load(files['depth'])
        lr = np.load(files['depth_x4'])
        rgb = np.load(files['rgb'])

        print(f"{prefix}: Input dep dtype = {dep.dtype}, shape = {dep.shape}")
        print(f"{prefix}: Input rgb dtype = {rgb.dtype}, shape = {rgb.shape}")
        print(f"{prefix}: Input lr dtype = {lr.dtype}, shape = {lr.shape}")

        # 归一化
        dep_norm, dep_max = normalize_img(dep)
        lr_norm, lr_max = normalize_img(lr)
        # rgb假设shape H, W, C,转CHW并归一化(0~1)
        rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0

        # mod_crop,深度和rgb都扩1维(channel),rgb转CHW在前面完成了
        dep_norm = mod_crop(np.expand_dims(dep_norm, 0), modulo=32)
        rgb_norm = mod_crop(rgb_norm, modulo=32)
        lr_norm = mod_crop(lr_norm, modulo=32)

        # lr上采样用cv2一样双线性插值
        h, w = lr_norm.shape[-2], lr_norm.shape[-1]  # 对应深度h,w
        import cv2
        lr_up = cv2.resize(lr_norm, (w * scale, h * scale), interpolation=cv2.INTER_LINEAR)

        # 确保shape维度对应
        if lr_norm.ndim == 2:
            lr_norm = np.expand_dims(lr_norm, 0)
        if lr_up.ndim == 2:
            lr_up = np.expand_dims(lr_up, 0)

        # 转tensor并增加batch维
        dep_t, rgb_t, lr_t, lr_up_t = np_to_tensor(dep_norm, rgb_norm, lr_norm, lr_up)
        dep_t = dep_t.unsqueeze(0).cuda()
        rgb_t = rgb_t.unsqueeze(0).cuda()
        lr_t = lr_t.unsqueeze(0).cuda()
        lr_up_t = lr_up_t.unsqueeze(0).cuda()

        inputs = {
            'img_gt': dep_t,
            'img_rgb': rgb_t,
            'img_lr': lr_t,
            'lr_up': lr_up_t,
        }

        with torch.no_grad():
            out = model(inputs)['img_out']

        # 反归一化还原
        out_img = out.squeeze().cpu().numpy()
        out_img = np.clip(out_img, 0, 1) * dep_max

        # 转回整数格式做RMSE计算
        if dep.dtype == np.uint8:
            out_img_int = np.round(out_img).astype(np.uint8)
            dep_int = dep
        elif dep.dtype == np.uint16:
            out_img_int = np.round(out_img).astype(np.uint16)
            dep_int = dep
        else:
            # float或者其它类型(如int32),保持float32,RMSE时会自动转换
            out_img_int = out_img.astype(np.float32)
            dep_int = dep.astype(np.float32)

        rmse_val = calc_rmse(out_img_int, dep_int)
        print(f'{prefix}: RMSE = {rmse_val:.4f}')

        # 保存结果图为png,需先转uint8或uint16
        save_path = os.path.join(output_dir, f'{prefix}_sr.png')
        import cv2
        cv2.imwrite(save_path, out_img_int)
        print(f'Saved SR image to {save_path}')

if __name__ == '__main__':
    main()
ASSISTANT
你提供的代码用了 `mod_crop` 函数来裁剪图像,这会裁掉边界的像素,导致输出结果比原始图像小。你希望改成**对不能被32整除的图像进行“填充”**(pad)到能被32整除的大小,且**推理输出后裁剪回原始大小,不影响最终输出**。

---

我在你的代码基础上帮你写了 `mod_pad` 和输出端的裁剪函数 `crop_to_original`,并替换了`mod_crop`部分。修改后的代码如下:

```python
import os
import glob
import torch
import numpy as np
from ahmf import AHMF

import warnings
warnings.filterwarnings("ignore", category=UserWarning)

def mod_pad(img, modulo=32):
    """
    对输入图像填充(pad)使其H和W变成modulo的整数倍。
    pad方式:在下边和右边补0。
    img支持2D (H,W) 或 3D (C,H,W) numpy数组。
    返回padding后的图像,以及填充的高度和宽度数量。
    """
    if img.ndim == 2:
        h, w = img.shape
        pad_h = (modulo - h % modulo) % modulo
        pad_w = (modulo - w % modulo) % modulo
        padded_img = np.pad(img, ((0,pad_h),(0,pad_w)), mode='constant')
    elif img.ndim == 3:
        c, h, w = img.shape
        pad_h = (modulo - h % modulo) % modulo
        pad_w = (modulo - w % modulo) % modulo
        padded_img = np.pad(img, ((0,0),(0,pad_h),(0,pad_w)), mode='constant')
    else:
        raise ValueError(f"Unsupported img ndim {img.ndim}")
    return padded_img, pad_h, pad_w


def crop_to_original(img, pad_h, pad_w):
    """
    输入的img可以为numpy数组 或 pytorch tensor。
    裁剪掉之前pad增加的部分,恢复为原始大小。
    支持numpy数组及tensor,且shape支持[B,C,H,W]、[C,H,W]、[H,W]。
    """
    if isinstance(img, torch.Tensor):
        if img.dim() == 4:
            return img[..., :img.size(-2)-pad_h if pad_h != 0 else img.size(-2),
                       :img.size(-1)-pad_w if pad_w != 0 else img.size(-1)]
        elif img.dim() == 3:
            return img[:, :img.size(-2)-pad_h if pad_h != 0 else img.size(-2),
                       :img.size(-1)-pad_w if pad_w != 0 else img.size(-1)]
        elif img.dim() == 2:
            return img[:img.size(-2)-pad_h if pad_h != 0 else img.size(-2),
                       :img.size(-1)-pad_w if pad_w != 0 else img.size(-1)]
        else:
            raise ValueError(f"Unsupported tensor dim {img.dim()}")
    else:
        # numpy 数组
        if img.ndim == 4:
            return img[..., :img.shape[-2]-pad_h if pad_h != 0 else img.shape[-2],
                       :img.shape[-1]-pad_w if pad_w != 0 else img.shape[-1]]
        elif img.ndim == 3:
            return img[:, :img.shape[-2]-pad_h if pad_h != 0 else img.shape[-2],
                       :img.shape[-1]-pad_w if pad_w != 0 else img.shape[-1]]
        elif img.ndim == 2:
            return img[:img.shape[-2]-pad_h if pad_h != 0 else img.shape[-2],
                       :img.shape[-1]-pad_w if pad_w != 0 else img.shape[-1]]
        else:
            raise ValueError(f"Unsupported numpy ndim {img.ndim}")

def np_to_tensor(*imgs, input_range=1.0, output_range=1.0):
    def _to_tensor(img):
        arr = img.astype(np.float32)
        if arr.ndim == 2:
            arr = np.expand_dims(arr, 0)
        tensor = torch.from_numpy(arr).float()
        tensor *= output_range / input_range
        return tensor
    out = [_to_tensor(img) for img in imgs]
    return out if len(out) > 1 else out[0]

def normalize_img(img):
    if img.dtype == np.uint8:
        return img.astype(np.float32) / 255.0, 255
    elif img.dtype == np.uint16:
        return img.astype(np.float32) / 65535.0, 65535
    else:
        max_val = float(np.max(img)) if np.max(img) > 1.0 else 1.0
        return img.astype(np.float32) / max_val, max_val

def calc_rmse(pred, target):
    pred = pred.astype(np.float64)
    target = target.astype(np.float64)
    return np.sqrt(np.mean((pred - target) ** 2))


def main():
    scale = 4    
    model = AHMF(scale=scale, act='PReLU', in_channels=1, guidance_channels=3)
    ckpt_path = f'/home/jianruobing/code/AHMF-main/AHMF-test/model_x{scale}.pth'
    checkpoint = torch.load(ckpt_path, map_location='cpu')
    model.load_state_dict(checkpoint['state_dict'])
    model = model.cuda().eval()
    
    input_dir = '/home/jianruobing/code/AHMF-main/imgs2npy'
    output_dir = './output'
    os.makedirs(output_dir, exist_ok=True)

    all_files = []
    all_files.extend(glob.glob(os.path.join(input_dir, '*_depth.npy')))
    all_files.extend(glob.glob(os.path.join(input_dir, '*_depth_x4.npy')))
    all_files.extend(glob.glob(os.path.join(input_dir, '*_rgb.npy')))

    groups = {}
    for fpath in all_files:
        fname = os.path.basename(fpath)
        if '_depth_x4' in fname:
            prefix = fname.split('_depth_x4')[0]
            key = 'depth_x4'
        elif '_depth' in fname:
            prefix = fname.split('_depth')[0]
            key = 'depth'
        elif '_rgb' in fname:
            prefix = fname.split('_rgb')[0]
            key = 'rgb'
        else:
            continue
        groups.setdefault(prefix, {})[key] = fpath

    print(f'Found {len(groups)} image groups.')

    for prefix, files in groups.items():
        if not {'depth', 'depth_x4', 'rgb'}.issubset(files.keys()):
            print(f'Warning: missing files for {prefix}, skipped.')
            continue

        dep = np.load(files['depth'])
        lr = np.load(files['depth_x4'])
        rgb = np.load(files['rgb'])

        print(f"{prefix}: Input dep dtype = {dep.dtype}, shape = {dep.shape}")
        print(f"{prefix}: Input rgb dtype = {rgb.dtype}, shape = {rgb.shape}")
        print(f"{prefix}: Input lr dtype = {lr.dtype}, shape = {lr.shape}")

        dep_norm, dep_max = normalize_img(dep)
        lr_norm, lr_max = normalize_img(lr)
        rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0

        # 使用mod_pad填充至32的倍数
        dep_norm_pad, pad_h_dep, pad_w_dep = mod_pad(np.expand_dims(dep_norm, 0), modulo=32)
        rgb_norm_pad, pad_h_rgb, pad_w_rgb = mod_pad(rgb_norm, modulo=32)
        lr_norm_pad, pad_h_lr, pad_w_lr = mod_pad(lr_norm, modulo=32)

        assert pad_h_dep == pad_h_rgb == pad_h_lr, "填充高度不一致"
        assert pad_w_dep == pad_w_rgb == pad_w_lr, "填充宽度不一致"

        import cv2
        h_lr, w_lr = lr_norm_pad.shape[-2], lr_norm_pad.shape[-1]
        lr_up = cv2.resize(lr_norm_pad, (w_lr * scale, h_lr * scale), interpolation=cv2.INTER_LINEAR)
        if lr_up.ndim == 2:
            lr_up = np.expand_dims(lr_up, 0)

        dep_t, rgb_t, lr_t, lr_up_t = np_to_tensor(dep_norm_pad, rgb_norm_pad, lr_norm_pad, lr_up)
        dep_t = dep_t.unsqueeze(0).cuda()
        rgb_t = rgb_t.unsqueeze(0).cuda()
        lr_t = lr_t.unsqueeze(0).cuda()
        lr_up_t = lr_up_t.unsqueeze(0).cuda()

        inputs = {
            'img_gt': dep_t,
            'img_rgb': rgb_t,
            'img_lr': lr_t,
            'lr_up': lr_up_t,
        }

        with torch.no_grad():
            out = model(inputs)['img_out']

        # 裁剪回原始尺寸
        out = crop_to_original(out, pad_h_dep, pad_w_dep)

        out_img = out.squeeze().cpu().numpy()
        out_img = np.clip(out_img, 0, 1) * dep_max

        if dep.dtype == np.uint8:
            out_img_int = np.round(out_img).astype(np.uint8)
            dep_int = dep
        elif dep.dtype == np.uint16:
            out_img_int = np.round(out_img).astype(np.uint16)
            dep_int = dep
        else:
            out_img_int = out_img.astype(np.float32)
            dep_int = dep.astype(np.float32)

        rmse_val = calc_rmse(out_img_int, dep_int)
        print(f'{prefix}: RMSE = {rmse_val:.4f}')

        save_path = os.path.join(output_dir, f'{prefix}_sr.png')
        import cv2
        cv2.imwrite(save_path, out_img_int)
        print(f'Saved SR image to {save_path}')

if __name__ == '__main__':
    main()
```

---

### 主要改动点

- 移除了`mod_crop`裁剪,将其替换为`mod_pad`填充,使输入尺寸变为32的倍数
- 模型输出后,使用`crop_to_original`去除padding部分,还原到原始大小
- 保证`dep_norm`、`rgb_norm`、`lr_norm`均使用相同的padding保持一致尺寸,避免shape不匹配错误发生

---

这样能保证:

- 输入满足模型尺寸要求
- 不裁剪原始有效区域,避免精度损失
- 输出图像大小与输入大小一致,方便后续保存和评价

---

如果你需要,我可以帮你写成一个函数版本,方便集成和复用。

turns-00085.parquet:54854

f01eb7f17deeb101d6fe8fa4
turn 13/16gpt-4.1-mini-2025-04-14ChineseTaiwan594 words
degenerate_repetitionAbsentFinal dense release
USER
帮我写成一个函数版本,方便集成和复用。
ASSISTANT
好的!我帮你把“填充 + 推理 + 裁剪复原 + 计算RMSE + 保存结果”写成一个函数,方便你在项目里调用。这个函数接口设计成:

```python
def inference_with_padding(model, dep, lr, rgb, scale=4, modulo=32, device='cuda', save_path=None):
    """
    说明:
    - model: 你的超分模型,已经切换到eval模式并加载权重
    - dep: np.array,原始深度图(H,W)
    - lr: np.array,低分辨率深度图(H_lr, W_lr)
    - rgb: np.array,RGB引导图(H, W, 3)
    - scale: int,超分比例
    - modulo: int,pad到能被这个数整除
    - device: str,torch运行设备
    - save_path: str或None,若提供路径,则保存超分后的结果图像

    返回:
    - rmse_val: float,超分结果与原始深度的RMSE
    - out_img_int: np.array,超分结果的整数格式图像
    """

```

下面是完整代码:

```python
import numpy as np
import torch
import cv2

def mod_pad(img, modulo=32):
    if img.ndim == 2:
        h, w = img.shape
        pad_h = (modulo - h % modulo) % modulo
        pad_w = (modulo - w % modulo) % modulo
        padded_img = np.pad(img, ((0,pad_h),(0,pad_w)), mode='constant')
    elif img.ndim == 3:
        c, h, w = img.shape
        pad_h = (modulo - h % modulo) % modulo
        pad_w = (modulo - w % modulo) % modulo
        padded_img = np.pad(img, ((0,0),(0,pad_h),(0,pad_w)), mode='constant')
    else:
        raise ValueError(f"Unsupported img ndim {img.ndim}")
    return padded_img, pad_h, pad_w

def crop_to_original(img, pad_h, pad_w):
    if isinstance(img, torch.Tensor):
        if img.dim() == 4:
            return img[..., :img.size(-2)-pad_h if pad_h != 0 else img.size(-2),
                       :img.size(-1)-pad_w if pad_w != 0 else img.size(-1)]
        elif img.dim() == 3:
            return img[:, :img.size(-2)-pad_h if pad_h != 0 else img.size(-2),
                       :img.size(-1)-pad_w if pad_w != 0 else img.size(-1)]
        elif img.dim() == 2:
            return img[:img.size(-2)-pad_h if pad_h != 0 else img.size(-2),
                       :img.size(-1)-pad_w if pad_w != 0 else img.size(-1)]
        else:
            raise ValueError(f"Unsupported tensor dim {img.dim()}")
    else:
        if img.ndim == 4:
            return img[..., :img.shape[-2]-pad_h if pad_h != 0 else img.shape[-2],
                       :img.shape[-1]-pad_w if pad_w != 0 else img.shape[-1]]
        elif img.ndim == 3:
            return img[:, :img.shape[-2]-pad_h if pad_h != 0 else img.shape[-2],
                       :img.shape[-1]-pad_w if pad_w != 0 else img.shape[-1]]
        elif img.ndim == 2:
            return img[:img.shape[-2]-pad_h if pad_h != 0 else img.shape[-2],
                       :img.shape[-1]-pad_w if pad_w != 0 else img.shape[-1]]
        else:
            raise ValueError(f"Unsupported numpy ndim {img.ndim}")

def normalize_img(img):
    if img.dtype == np.uint8:
        return img.astype(np.float32) / 255.0, 255
    elif img.dtype == np.uint16:
        return img.astype(np.float32) / 65535.0, 65535
    else:
        max_val = float(np.max(img)) if np.max(img) > 1.0 else 1.0
        return img.astype(np.float32) / max_val, max_val

def np_to_tensor(*imgs, input_range=1.0, output_range=1.0):
    def _to_tensor(img):
        arr = img.astype(np.float32)
        if arr.ndim == 2:
            arr = np.expand_dims(arr, 0)
        tensor = torch.from_numpy(arr).float()
        tensor *= output_range / input_range
        return tensor
    out = [_to_tensor(img) for img in imgs]
    return out if len(out) > 1 else out[0]

def calc_rmse(pred, target):
    pred = pred.astype(np.float64)
    target = target.astype(np.float64)
    return np.sqrt(np.mean((pred - target) ** 2))

def inference_with_padding(model, dep, lr, rgb, scale=4, modulo=32, device='cuda', save_path=None):
    dep_norm, dep_max = normalize_img(dep)
    lr_norm, _ = normalize_img(lr)
    rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0

    dep_norm_pad, pad_h_dep, pad_w_dep = mod_pad(np.expand_dims(dep_norm, 0), modulo=modulo)
    rgb_norm_pad, pad_h_rgb, pad_w_rgb = mod_pad(rgb_norm, modulo=modulo)
    lr_norm_pad, pad_h_lr, pad_w_lr = mod_pad(lr_norm, modulo=modulo)

    assert pad_h_dep == pad_h_rgb == pad_h_lr, "Height padding not consistent"
    assert pad_w_dep == pad_w_rgb == pad_w_lr, "Width padding not consistent"

    h_lr, w_lr = lr_norm_pad.shape[-2], lr_norm_pad.shape[-1]
    lr_up = cv2.resize(lr_norm_pad, (w_lr * scale, h_lr * scale), interpolation=cv2.INTER_LINEAR)
    if lr_up.ndim == 2:
        lr_up = np.expand_dims(lr_up, 0)

    dep_t, rgb_t, lr_t, lr_up_t = np_to_tensor(dep_norm_pad, rgb_norm_pad, lr_norm_pad, lr_up)
    dep_t = dep_t.unsqueeze(0).to(device)
    rgb_t = rgb_t.unsqueeze(0).to(device)
    lr_t = lr_t.unsqueeze(0).to(device)
    lr_up_t = lr_up_t.unsqueeze(0).to(device)

    inputs = {
        'img_gt': dep_t,
        'img_rgb': rgb_t,
        'img_lr': lr_t,
        'lr_up': lr_up_t,
    }

    with torch.no_grad():
        out = model(inputs)['img_out']

    out = crop_to_original(out, pad_h_dep, pad_w_dep)
    out_img = out.squeeze().cpu().numpy()
    out_img = np.clip(out_img, 0, 1) * dep_max

    if dep.dtype == np.uint8:
        out_img_int = np.round(out_img).astype(np.uint8)
        dep_int = dep
    elif dep.dtype == np.uint16:
        out_img_int = np.round(out_img).astype(np.uint16)
        dep_int = dep
    else:
        out_img_int = out_img.astype(np.float32)
        dep_int = dep.astype(np.float32)

    rmse_val = calc_rmse(out_img_int, dep_int)

    if save_path is not None:
        cv2.imwrite(save_path, out_img_int)
        print(f"Saved SR image to {save_path}")

    return rmse_val, out_img_int
```

---

## 使用示例

```python
# 加载模型,设置eval等略
# dep, lr, rgb 已通过 np.load 加载

rmse, sr_img = inference_with_padding(
    model,
    dep,
    lr,
    rgb,
    scale=4,
    modulo=32,
    device='cuda',
    save_path='./output/sample_sr.png'
)
print(f"RMSE: {rmse}")
```

---

这段函数封装了最核心的预处理、padding、推理、去padding和结果后处理流程,可以直接调用且方便集成。  
如果要批量处理,只要循环调用该函数,传入不同输入数据即可。

如果需要,我也可以帮你写批量推理的完整调用示例。

turns-00085.parquet:54855

69c9b2f55d091326a096e855
turn 14/16gpt-4.1-mini-2025-04-14ChineseTaiwan216 words
degenerate_repetitionAbsentFinal dense release
USER
Traceback (most recent call last):
  File "test3.py", line 197, in <module>
    main()
  File "test3.py", line 147, in main
    assert pad_h_dep == pad_h_rgb == pad_h_lr, "填充高度不一致"
AssertionError: 填充高度不一致
ASSISTANT
从错误信息来看:

```plain
AssertionError: 填充高度不一致
```

说明你调用的 `mod_pad` 函数对 `dep_norm`,`rgb_norm`,`lr_norm` 三个数组计算填充高度 (`pad_h_XXX`) 时,返回的高度不一致,也就是说:

```python
pad_h_dep != pad_h_rgb or pad_h_dep != pad_h_lr
```

---

# 问题原因分析

- 三个输入数据 (`dep_norm`, `rgb_norm`, `lr_norm`) 原始尺寸不一样,原因是:
  - `dep_norm` 是深度图,shape形如 `[H, W]`。
  - `rgb_norm` 是三通道图,shape是 `[C, H, W]`。
  - `lr_norm` 是低分辨率图,shape是 `[H_lr, W_lr]`,通常尺寸是原图尺寸 `H, W` 除以 `scale`。
- 由于 `lr_norm` 是低分辨率图,通常高宽和 `dep_norm`,`rgb_norm` 不一样(缩放了)。
- 你用同样的`mod_pad`和期待一致的填充大小自然失败了。

---

# 解决方案

## 1. 只对需要保持一致尺寸的两个做pad

- `dep_norm` 和 `rgb_norm` 是同一分辨率,**必须保证它们pad后尺寸一致**。
- `lr_norm` 是低分辨率,尺寸更小,用它转成上采样(`lr_up`)后尺寸才应该和`dep_norm`保持一致。
- 所以只要保证`dep_norm`和`rgb_norm`的pad尺寸一致即可。`lr_norm`可单独pad。

## 2. 修改代码示例

按上面思路改你pad代码:

```python
# dep_norm 和 rgb_norm一起padding保证尺寸一致
dep_norm_pad, pad_h_dep, pad_w_dep = mod_pad(np.expand_dims(dep_norm, 0), modulo=32)
rgb_norm_pad, pad_h_rgb, pad_w_rgb = mod_pad(rgb_norm, modulo=32)

assert pad_h_dep == pad_h_rgb, "dep和rgb填充高度不一致"
assert pad_w_dep == pad_w_rgb, "dep和rgb填充宽度不一致"

# lr_norm单独padding(因为尺寸本来不同)
lr_norm_pad, pad_h_lr, pad_w_lr = mod_pad(lr_norm, modulo=32)
```

然后继续后续处理:

```python
import cv2
h_lr, w_lr = lr_norm_pad.shape[-2], lr_norm_pad.shape[-1]
lr_up = cv2.resize(lr_norm_pad, (w_lr * scale, h_lr * scale), interpolation=cv2.INTER_LINEAR)
if lr_up.ndim == 2:
    lr_up = np.expand_dims(lr_up, 0)
```

---

# 另外注意

- 如果`lr_up`的尺寸不是`dep_norm_pad` / `rgb_norm_pad`的倍数,导致后续模型输入也出现尺寸不匹配,需要对`lr_up`执行相同的pad或者裁剪,确保和主分辨率输入尺寸一致。
- 但常规超分模型都会保证`lr_up`经过插值后刚好和输入`dep_norm_pad`相匹配。


---

# 总结

改为:

```python
dep_norm_pad, pad_h_dep, pad_w_dep = mod_pad(np.expand_dims(dep_norm, 0), modulo=32)
rgb_norm_pad, pad_h_rgb, pad_w_rgb = mod_pad(rgb_norm, modulo=32)

assert pad_h_dep == pad_h_rgb, "dep和rgb填充高度不一致"
assert pad_w_dep == pad_w_rgb, "dep和rgb填充宽度不一致"

lr_norm_pad, pad_h_lr, pad_w_lr = mod_pad(lr_norm, modulo=32)
```

---

如果还是出现其他形状不匹配报错,请继续反馈给我,我帮你定位。

turns-00085.parquet:54856

2ae3dd5d89f5e10f2facba99
turn 15/16gpt-4.1-mini-2025-04-14ChineseTaiwan313 words
degenerate_repetitionAbsentFinal dense release
USER
Traceback (most recent call last):
  File "test3.py", line 199, in <module>
    main()
  File "test3.py", line 172, in main
    out = model(inputs)['img_out']
  File "/home/jianruobing/anaconda3/envs/UHDFour/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "/home/jianruobing/code/AHMF-main/AHMF-test/ahmf.py", line 364, in forward
    tmp = self.fuse_conv[i](depth=depth_out[3 - i],
  File "/home/jianruobing/anaconda3/envs/UHDFour/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "/home/jianruobing/code/AHMF-main/AHMF-test/ahmf.py", line 293, in forward
    depth, guide = self.attention_layer(depth=depth, guidance=guide)
  File "/home/jianruobing/anaconda3/envs/UHDFour/lib/python3.8/site-packages/torch/nn/modules/module.py", line 1501, in _call_impl
    return forward_call(*args, **kwargs)
  File "/home/jianruobing/code/AHMF-main/AHMF-test/ahmf.py", line 273, in forward
    fuse_feature = self.squeeze(torch.cat((depth, guidance), 1))
RuntimeError: Sizes of tensors must match except in dimension 1. Expected size 64 but got size 56 for tensor number 1 in the list.
ASSISTANT
这个报错说明模型内部`depth`和`guidance`两个张量在除拼接维度(dim=1,通道数)外的空间尺寸不匹配,具体:

```
Expected size 64 but got size 56 for tensor number 1 in the list
```

表示两张量在某个空间尺寸上一个是64,另一个是56。

---

## 你之前padding变成32的倍数,模型里的尺寸应该是多层下采样后尺寸不匹配。具体来说,很可能是:

- `dep`(深度图)和(等同于`lr_up`或)指导图`rgb`在网络多层下采样和特征融合时尺寸不统一。
- 这种情况通常和输入的高宽不是对应多层下采样的整数倍有关,比如 `2^(num_pyramid +1)` 倍数。
- 仅仅保证输入是`32`的倍可能还不够,可能你的网络有5次下采样,即需要64倍整数或者其他尺寸。

---

## 解决办法

### 1. 确认你的网络下采样倍率

- 查看网络源码或参数配置,确认`num_pyramid`或者网络层级下采样倍率,比如是不是5层下采样(2^5=32),或者6层下采样(2^6=64)。
- 计算需要保证输入图像尺寸是多大倍数的整数,通常是:

```python
module = max(int(math.pow(2, 1 + args.num_pyramid)), args.scale)
```

比如`num_pyramid=4`,则 `module=2^(1+4)=32`,`num_pyramid=5`,则`module=64`。

### 2. 修改padding倍数

如果你之前固定使用`modulo=32`,应当改成用**动态计算的`module`**。例如:

```python
module = max(2 ** (1 + num_pyramid), scale)
# 举例,如果 num_pyramid=4, scale=4,则 module=32
# 如果 num_pyramid=5, scale=4,则 module=64
```

用填充改为:

```python
dep_norm_pad, pad_h_dep, pad_w_dep = mod_pad(np.expand_dims(dep_norm, 0), modulo=module)
rgb_norm_pad, pad_h_rgb, pad_w_rgb = mod_pad(rgb_norm, modulo=module)
lr_norm_pad, pad_h_lr, pad_w_lr = mod_pad(lr_norm, modulo=module)
```

### 3. 仍然保证 `dep` 和 `rgb` pad大小一致,`lr` 独立pad

```python
assert pad_h_dep == pad_h_rgb, "dep和rgb填充高度不一致"
assert pad_w_dep == pad_w_rgb, "dep和rgb填充宽度不一致"
```

### 4. 保证`lr_up`尺寸和高分辨率输入尺寸匹配

```python
import cv2
h_lr, w_lr = lr_norm_pad.shape[-2], lr_norm_pad.shape[-1]
lr_up = cv2.resize(lr_norm_pad, (w_lr * scale, h_lr * scale), interpolation=cv2.INTER_LINEAR)
if lr_up.ndim == 2:
    lr_up = np.expand_dims(lr_up, 0)
```

推理后仍然裁剪回原始尺寸。

---

## 简单总结:

- 查看或确认你实际代码中的`num_pyramid`和`scale`参数(应在`args`或模型定义中),确认需要的`module`值。
- 动态设定`mod_pad`的`modulo=module`,而不要固定32。
- 保证依照`module`填充的尺寸和你的网络结构下采样要求匹配,避免尺寸不兼容问题。

---

## 你调用的示例修改(示意)

```python
num_pyramid = 4  # 需要根据你项目实际设定正确赋值
scale = 4
module = max(2 ** (1 + num_pyramid), scale)

dep_norm_pad, pad_h_dep, pad_w_dep = mod_pad(np.expand_dims(dep_norm, 0), modulo=module)
rgb_norm_pad, pad_h_rgb, pad_w_rgb = mod_pad(rgb_norm, modulo=module)

assert pad_h_dep == pad_h_rgb
assert pad_w_dep == pad_w_rgb

lr_norm_pad, pad_h_lr, pad_w_lr = mod_pad(lr_norm, modulo=module)
```

请确认你的`num_pyramid`和`scale`数值,然后尝试改成动态`module`填充。

---

如果你给我你使用模型的`num_pyramid`或者相关参数,我可以帮你具体算出需要填充成多少倍数。

turns-00085.parquet:54857

06adbdd727107a1cbd895fe8
turn 16/16gpt-4.1-mini-2025-04-14ChineseTaiwan723 words
degenerate_repetitionAbsentFinal dense release
USER
请帮我解释一下这个代码
ASSISTANT
好的,我帮你详细解释一下你给出的代码的结构和每部分作用。

---

```python
import os
import glob
import torch
import numpy as np
from ahmf import AHMF

import warnings
warnings.filterwarnings("ignore", category=UserWarning)
```
- 导入基本库:`os`和`glob`用于文件系统操作,`torch`用于模型推理,`numpy`进行数值运算,
- `AHMF` 是你超分模型的定义文件模块。
- 警告忽略,避免程序输出过多无关警告。

---

### `mod_crop` 函数
```python
def mod_crop(*imgs, modulo):
    def crop(img):
        if img.ndim == 2:
            h, w = img.shape
            crop_h, crop_w = h % modulo, w % modulo
            return img[crop_h // 2 : h - (crop_h - crop_h // 2),
                       crop_w // 2 : w - (crop_w - crop_w // 2)]
        elif img.ndim == 3:
            c, h, w = img.shape
            crop_h, crop_w = h % modulo, w % modulo
            return img[:, crop_h // 2 : h - (crop_h - crop_h // 2),
                       crop_w // 2 : w - (crop_w - crop_w // 2)]
        else:
            raise ValueError(f"Unsupported image dimension: {img.ndim}")
    out = [crop(img) for img in imgs]
    return out if len(out) > 1 else out[0]
```
- **作用**:裁剪图像,使输入图像尺寸高和宽能被 `modulo` 整除,解决卷积网络中倍数对齐问题。
- `imgs`可以传入多个图像,会依次裁剪。
- 支持2维(H,W)和3维(C,H,W)图像。
- 裁剪均匀分布在图像边缘。

---

### `np_to_tensor` 函数
```python
def np_to_tensor(*imgs, input_range=1.0, output_range=1.0):
    def _to_tensor(img):
        arr = img.astype(np.float32)
        if arr.ndim == 2:
            arr = np.expand_dims(arr, 0)  # 增加通道维
        tensor = torch.from_numpy(arr).float()
        tensor *= output_range / input_range  # 归一化比例调整
        return tensor
    out = [_to_tensor(img) for img in imgs]
    return out if len(out) > 1 else out[0]
```
- 作用:把 numpy 格式的图片数据转换成 PyTorch 张量。
- 自动将 HxW 数组转换成 1xHxW (单通道),适配模型输入。
- 能同时转换多个图片。
- 乘上缩放比例保持输入范围正确。

---

### `normalize_img` 函数
```python
def normalize_img(img):
    # 保持原数据类型,并归一化(和quick_test类似)
    if img.dtype == np.uint8:
        return img.astype(np.float32) / 255.0, 255
    elif img.dtype == np.uint16:
        return img.astype(np.float32) / 65535.0, 65535
    else:
        # float类型或者其它
        max_val = float(np.max(img)) if np.max(img) > 1.0 else 1.0
        return img.astype(np.float32) / max_val, max_val
```
- 读取图像后将其归一化到 [0,1] 的范围。
- 支持 uint8(8位图像)、uint16(16位图像)和浮点图像。
- 返回归一化结果和最大值(后续反归一化时用)。

---

### `calc_rmse` 函数
```python
def calc_rmse(pred, target):
    pred = pred.astype(np.float64)
    target = target.astype(np.float64)
    return np.sqrt(np.mean((pred - target) ** 2))
```
- 计算两个图像之间的均方根误差(RMSE),用于评估超分效果。
- 转换成64位浮点精度避免误差计算问题。

---

### `main()` 函数 - 程序入口

```python
def main():
    scale = 4    
    model = AHMF(scale=scale, act='PReLU', in_channels=1, guidance_channels=3)
    ckpt_path = f'/home/jianruobing/code/AHMF-main/AHMF-test/model_x{scale}.pth'
    checkpoint = torch.load(ckpt_path, map_location='cpu')
    model.load_state_dict(checkpoint['state_dict'])
    model = model.cuda().eval()
```
- 创建超分模型实例,设置超分比例和激活函数。
- 加载模型权重文件。
- 切换到GPU,并设成评估模式。

---

```python
    input_dir = '/home/jianruobing/code/AHMF-main/imgs2npy'
    output_dir = './output'
    os.makedirs(output_dir, exist_ok=True)
```
- 定义数据输入和结果输出路径。
- 如果输出目录不存在则创建。

---

```python
    # 查找全部测试用的npy文件,以深度图、低分辨率图和RGB图归类分组
    all_files = []
    all_files.extend(glob.glob(os.path.join(input_dir, '*_depth.npy')))
    all_files.extend(glob.glob(os.path.join(input_dir, '*_depth_x4.npy')))
    all_files.extend(glob.glob(os.path.join(input_dir, '*_rgb.npy')))

    groups = {}
    for fpath in all_files:
        fname = os.path.basename(fpath)
        if '_depth_x4' in fname:
            prefix = fname.split('_depth_x4')[0]
            key = 'depth_x4'
        elif '_depth' in fname:
            prefix = fname.split('_depth')[0]
            key = 'depth'
        elif '_rgb' in fname:
            prefix = fname.split('_rgb')[0]
            key = 'rgb'
        else:
            continue
        groups.setdefault(prefix, {})[key] = fpath

    print(f'Found {len(groups)} image groups.')
```
- 读取`input_dir`中所有`*_depth.npy`、`*_depth_x4.npy`、`*_rgb.npy`文件。
- 根据文件名前缀分组,把对应深度图、低分辨率图和RGB图放到同一组。
- 最后输出找到的组数量。

---

### 对每组数据进行超分推理:

```python
    for prefix, files in groups.items():
        if not {'depth', 'depth_x4', 'rgb'}.issubset(files.keys()):
            print(f'Warning: missing files for {prefix}, skipped.')
            continue

        dep = np.load(files['depth'])
        lr = np.load(files['depth_x4'])
        rgb = np.load(files['rgb'])

        print(f"{prefix}: Input dep dtype = {dep.dtype}, shape = {dep.shape}")
        print(f"{prefix}: Input rgb dtype = {rgb.dtype}, shape = {rgb.shape}")
        print(f"{prefix}: Input lr dtype = {lr.dtype}, shape = {lr.shape}")
```
- 检查每组必须包含完整的三种文件,没有则跳过。
- 使用`np.load`加载npy文件到内存。
- 打印图像数据类型和尺寸。

---

### 输入图像归一化和变换

```python
        dep_norm, dep_max = normalize_img(dep)
        lr_norm, lr_max = normalize_img(lr)
        rgb_norm = np.float32(np.transpose(rgb, (2, 0, 1))) / 255.0
```
- 深度图`dep`和低分辨率`lr`归一化。
- RGB图转换成 CHW 形式,并归一化到0~1。

---

### 裁剪输入图像大小

```python
        dep_norm = mod_crop(np.expand_dims(dep_norm, 0), modulo=32)
        rgb_norm = mod_crop(rgb_norm, modulo=32)
        lr_norm = mod_crop(lr_norm, modulo=32)
```
- 对深度、RGB、低分辨率进行 `mod_crop`,裁剪成能被32整除大小,避免模型尺寸不匹配。

---

### 低分辨率图像上采样

```python
        import cv2
        h, w = lr_norm.shape[-2], lr_norm.shape[-1]
        lr_up = cv2.resize(lr_norm, (w * scale, h * scale), interpolation=cv2.INTER_LINEAR)

        if lr_norm.ndim == 2:
            lr_norm = np.expand_dims(lr_norm, 0)
        if lr_up.ndim == 2:
            lr_up = np.expand_dims(lr_up, 0)
```
- 使用OpenCV双线性插值,将`lr_norm`放大到原始深度尺度大小。
- 确保`lr_norm`和`lr_up`变成`(C=1, H, W)`格式。

---

### 转tensor + 添加batch维

```python
        dep_t, rgb_t, lr_t, lr_up_t = np_to_tensor(dep_norm, rgb_norm, lr_norm, lr_up)
        dep_t = dep_t.unsqueeze(0).cuda()
        rgb_t = rgb_t.unsqueeze(0).cuda()
        lr_t = lr_t.unsqueeze(0).cuda()
        lr_up_t = lr_up_t.unsqueeze(0).cuda()
```
- 转换numpy为pytorch tensor,添加batch维。
- 并转移至GPU。

---

### 推理+后处理+计算RMSE

```python
        inputs = {
            'img_gt': dep_t,
            'img_rgb': rgb_t,
            'img_lr': lr_t,
            'lr_up': lr_up_t,
        }

        with torch.no_grad():
            out = model(inputs)['img_out']

        out_img = out.squeeze().cpu().numpy()
        out_img = np.clip(out_img, 0, 1) * dep_max

        if dep.dtype == np.uint8:
            out_img_int = np.round(out_img).astype(np.uint8)
            dep_int = dep
        elif dep.dtype == np.uint16:
            out_img_int = np.round(out_img).astype(np.uint16)
            dep_int = dep
        else:
            out_img_int = out_img.astype(np.float32)
            dep_int = dep.astype(np.float32)

        rmse_val = calc_rmse(out_img_int, dep_int)
        print(f'{prefix}: RMSE = {rmse_val:.4f}')
```
- 将inputs传给模型,执行前向推理,获得超分结果。
- 将结果归一化反转回原始深度范围。
- 根据原始深度数据类型转换超分结果。
- 计算预测与真实深度图的RMSE误差,打印。

---

### 保存结果图像

```python
        save_path = os.path.join(output_dir, f'{prefix}_sr.png')
        import cv2
        cv2.imwrite(save_path, out_img_int)
        print(f'Saved SR image to {save_path}')
```
- 保存超分结果为png图像文件。
- 用OpenCV以兼容格式写盘。

---

### 总结:

- 这是一个基于numpy和torch的测试流程脚本。
- 依次遍历目录中的npy深度图、低分辨率图、RGB图。
- 对每组数据归一化、裁剪、上采样,喂给模型做超分推理。
- 输出超分结果,做RMSE评估,保存超分图像。
- 兼顾多种输入深度图数据类型(uint8、uint16及浮点)。

---

如果你需要,我可以帮你写个加padding替代`mod_crop`防止裁剪版本,或者帮你针对代码的某部分做更详细讲解~