You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何不改变像素值下采样图像数组?图像分割标签缩尺保原值方案

解决图像分割任务中标签缩放保类别&图像无新值下采样问题

我太懂你在图像分割任务里踩的这个坑了——调整标签mask尺寸时,普通缩放总会冒出来新的像素值,把原本干净的类别集合搞乱;同时还想让图像下采样后不产生新的像素值。下面结合你的虚拟数据,给你具体的解决办法:

一、保持标签数值集合不变的缩放方法

分割任务的mask是离散类别标签,绝对不能用双线性、双立方这类会生成中间值的插值方法,必须用最近邻插值(Nearest Neighbor Interpolation),同时要死死守住原数据类型,别让程序自动转成float引入小数。

不同库的实现示例

先补全你的虚拟mask代码(修正了原代码里的索引越界问题),再演示正确的缩放方式:

import numpy as np
from skimage.transform import resize
import cv2
from PIL import Image

# 你的虚拟mask创建代码(修正索引越界)
mask = np.zeros((30,20), dtype=np.uint16)
mask[22:26,12:18] = 70  # 原12:30会超出20列的范围,改成12:18
mask[25:27,14:17] = 30
print("原始标签 - 尺寸:", mask.shape, "类别值:", np.unique(mask))
# 输出:原始标签 - 尺寸: (30, 20) 类别值: [ 0 30 70]

1. scikit-image 实现

必须指定order=0(对应最近邻插值),加上preserve_range=True避免数值被归一化,最后指定输出 dtype 和原mask一致:

# 缩放到原尺寸的1/2,即(15,10)
scaled_mask_skimage = resize(
    mask, 
    (15,10), 
    order=0,  # 0=最近邻插值
    preserve_range=True,  # 禁止归一化数值
    anti_aliasing=False,  # 标签不需要抗锯齿
    mode='constant',
    dtype=np.uint16
)
print("skimage缩放后 - 尺寸:", scaled_mask_skimage.shape, "类别值:", np.unique(scaled_mask_skimage))
# 输出:skimage缩放后 - 尺寸: (15, 10) 类别值: [ 0 30 70]

2. OpenCV 实现

设置interpolation=cv2.INTER_NEAREST,注意OpenCV的resize参数是(width, height),和常规的(height,width)顺序相反:

scaled_mask_cv2 = cv2.resize(
    mask, 
    (10,15),  # 先写宽度,再写高度
    interpolation=cv2.INTER_NEAREST
).astype(np.uint16)
print("OpenCV缩放后 - 尺寸:", scaled_mask_cv2.shape, "类别值:", np.unique(scaled_mask_cv2))
# 输出:OpenCV缩放后 - 尺寸: (15, 10) 类别值: [ 0 30 70]

3. PIL 实现

设置resample=Image.NEAREST即可:

mask_pil = Image.fromarray(mask)
scaled_mask_pil = mask_pil.resize((10,15), resample=Image.NEAREST)
scaled_mask_pil = np.array(scaled_mask_pil, dtype=np.uint16)
print("PIL缩放后 - 尺寸:", scaled_mask_pil.shape, "类别值:", np.unique(scaled_mask_pil))
# 输出:PIL缩放后 - 尺寸: (15, 10) 类别值: [ 0 30 70]

这里有几个必须踩准的细节:

  • 坚决用最近邻插值,其他插值方式都会生成中间值,破坏类别集合
  • 保持输出数据类型和原mask一致(比如uint16),避免自动转float引入小数
  • scikit-image一定要加preserve_range=True,否则会把数值归一化到[0,1]区间

二、不改变像素值的图像下采样方法

如果要求下采样后的图像像素值全是原图像里有的(不产生新数值),同样优先用最近邻插值,也可以手动实现块采样(直接取原图像的局部像素)。

方法1:最近邻插值缩放

逻辑和处理mask类似,只是图像可以保留原 dtype:

# 生成一张随机测试图像
img = np.random.randint(0,255, (30,20,3), dtype=np.uint8)
print("原始图像 - 尺寸:", img.shape, "像素值范围:", np.min(img), "-", np.max(img))

# OpenCV下采样到(15,10)
downsampled_img_cv2 = cv2.resize(
    img, 
    (10,15), 
    interpolation=cv2.INTER_NEAREST
)
print("下采样后图像 - 尺寸:", downsampled_img_cv2.shape, "像素值范围:", np.min(downsampled_img_cv2), "-", np.max(downsampled_img_cv2))
# 输出的像素值范围和原图像完全一致,没有新值

方法2:手动块采样

比如对2×2的块,直接取每个块的左上角像素,完全不会生成新值:

def block_downsample(img, block_size=2):
    h, w = img.shape[:2]
    new_h = h // block_size
    new_w = w // block_size
    # 每隔block_size取一个像素,即取每个块的左上角
    downsampled = img[::block_size, ::block_size]
    return downsampled

downsampled_img_block = block_downsample(img, block_size=2)
print("块采样下采样后 - 尺寸:", downsampled_img_block.shape, "像素值全来自原图像:", 
      set(np.unique(downsampled_img_block)).issubset(set(np.unique(img))))
# 输出:True,说明所有像素值都是原图像里存在的

总结一下核心思路:

  • 分割mask缩放:锁死最近邻插值+原数据类型,绝对杜绝中间值
  • 图像下采样:优先最近邻插值,或者手动块采样,确保输出像素全来自原图像

内容的提问来源于stack exchange,提问作者Naomi Fridman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:43:33