如何对掩码(Mask)进行尺寸调整/重采样?
掩码重采样的正确姿势:基于类别投票的分块法
嘿,这个问题抓得特别准——掩码作为像素级分类的真值,本质是离散的类别标签集合,普通图像插值(最近邻、双线性那套)要么会丢失区域的主导类别(比如最近邻刚好取到块里的少数派像素),要么会产生非整数的无效值,完全不适用。最靠谱的方法是分块投票法,核心逻辑就是用每个区域的"多数类别"代表重采样后的像素值,完美契合掩码的属性。
具体思路拆解
把原掩码划分成与目标尺寸一一对应的网格块,对每个块统计各类别像素的数量,票数最多的类别就是这个块重采样后的像素值。如果遇到平票(比如块里两类像素数量相同),可以自定义规则解决(比如优先背景、优先某类目标,或者取块中心像素值)。
用你的例子实际推演
原4×4掩码:
[[1, 1, 0, 0], [0, 1, 0, 0], [1, 1, 1, 2], [2, 2, 2, 2]]
要转成2×2,就把原掩码分成4个2×2的块:
- 左上块:
[[1,1],[0,1]]→ 1出现3次,0出现1次 → 投票选1 - 右上块:
[[0,0],[0,0]]→ 全0 → 选0 - 左下块:
[[1,1],[2,2]]→ 1和2各2次(平票),假设我们规则是"优先类别号大的目标",就选2 - 右下块:
[[1,2],[2,2]]→ 2出现3次,1出现1次 → 选2
最终得到的2×2掩码就是:
[[1, 0], [2, 2]]
代码实现(Python + NumPy)
这里用最直观的方式实现分块投票,你可以直接复用:
import numpy as np # 原掩码数组 original_mask = np.array([[1, 1, 0, 0], [0, 1, 0, 0], [1, 1, 1, 2], [2, 2, 2, 2]]) # 目标尺寸 target_height, target_width = 2, 2 # 计算每个分块的大小(确保原尺寸是目标的整数倍,否则先裁剪/补边) block_h = original_mask.shape[0] // target_height block_w = original_mask.shape[1] // target_width # 初始化重采样后的掩码 resampled_mask = np.zeros((target_height, target_width), dtype=original_mask.dtype) # 遍历每个目标块 for i in range(target_height): for j in range(target_width): # 提取当前块的所有像素 current_block = original_mask[i*block_h : (i+1)*block_h, j*block_w : (j+1)*block_w].flatten() # 统计每个类别的出现次数 category_counts = np.bincount(current_block) # 取票数最多的类别(平票时默认取索引最大的类别,可自定义规则) resampled_mask[i, j] = np.argmax(category_counts) print(resampled_mask)
补充说明
- 非整数倍尺寸处理:如果原掩码尺寸不是目标尺寸的整数倍(比如5×5转2×2),可以先把原掩码裁剪到最近的可整除尺寸,或者在边缘补背景(0)后再分块。
- 平票规则自定义:如果不想默认选类别号大的,也可以改成优先背景(0),或者取块中心的像素值,甚至随机选择,完全看你的任务需求。
- 高效实现优化:如果处理大尺寸掩码,用循环会慢,可以用NumPy的广播和聚合操作优化,比如用
reshape结合矢量化的统计函数替代循环,不过上面的循环版本已经足够清晰,小尺寸场景用完全没问题。
这种方法既能保证输出是合法的整数类别,又能最大程度保留原掩码的区域语义,绝对是掩码重采样的标准操作。
内容的提问来源于stack exchange,提问作者yujuezhao
相关产品推荐
相关产品推荐

