如何在TensorFlow中读取PascalVOC索引彩色标签图适配FCN语义分割?
这个问题我之前在做FCN语义分割时也遇到过,核心原因是Pascal VOC的标注PNG是调色板(Palette)模式存储的,PIL和TensorFlow对这类图像的解码逻辑不一样:
- PIL打开调色板图像时,默认会返回索引模式(
mode='P'),转成numpy数组后直接得到0-20的类别索引; - 而TensorFlow的
tf.image.decode_png默认会把调色板图像解码成RGB格式(3通道),所以你拿到的是0-255的RGB像素值,不是我们需要的类别ID。
下面给两种解决方案,你可以根据标注图的实际格式选择:
方案1:直接解码调色板图像为索引(适用于原始Pascal VOC标注图)
如果你的标注图是Pascal VOC原始的调色板PNG(用PIL打开后img.mode为'P'),可以直接指定channels=1解码,就能得到和PIL完全一致的0-20索引:
def load_voc_label_tf(image_path): # 读取标注文件 file = tf.io.read_file(image_path) # 解码为单通道索引图像,指定dtype保持和PIL输出一致 label = tf.image.decode_png(file, channels=1, dtype=tf.uint8) # 去掉多余的通道维度,转为[H, W]形状的张量 label = tf.squeeze(label, axis=-1) # 可选:转为int32类型方便后续模型计算 label = tf.cast(label, tf.int32) return label
方案2:通过颜色映射转换为索引(适用于已转为RGB的标注图)
如果你的标注图已经被转换成了RGB格式(PIL打开后mode='RGB'),就需要借助Pascal VOC固定的颜色-类别映射表来转换。我们可以构建颜色到索引的映射,再用TensorFlow的查找表操作完成转换:
# Pascal VOC官方的类别颜色映射(索引 -> (R, G, B)) VOC_COLOR_MAP = { 0: (0, 0, 0), # 背景 1: (128, 0, 0), # aeroplane 2: (0, 128, 0), # bicycle 3: (128, 128, 0), # bird 4: (0, 0, 128), # boat 5: (128, 0, 128), # bottle 6: (0, 128, 128), # bus 7: (128, 128, 128), # car 8: (64, 0, 0), # cat 9: (192, 0, 0), # chair 10: (64, 128, 0), # cow 11: (192, 128, 0), # diningtable 12: (64, 0, 128), # dog 13: (192, 0, 128), # horse 14: (64, 128, 128), # motorbike 15: (192, 128, 128), # person 16: (0, 64, 0), # pottedplant 17: (128, 64, 0), # sheep 18: (0, 192, 0), # sofa 19: (128, 192, 0), # train 20: (0, 64, 128) # tvmonitor } # 反转映射:颜色 -> 类别索引 color_to_idx = {v: k for k, v in VOC_COLOR_MAP.items()} def rgb_to_label_idx(label_rgb): # 将RGB值转换为唯一整数(方便哈希查找) rgb_flat = tf.reduce_sum(label_rgb * tf.constant([256*256, 256, 1], dtype=tf.uint8), axis=-1) # 构建查找表的键值对 keys = tf.constant([r*256*256 + g*256 + b for r, g, b in color_to_idx.keys()], dtype=tf.uint32) values = tf.constant(list(color_to_idx.values()), dtype=tf.int32) # 创建静态哈希表 lookup_table = tf.lookup.StaticHashTable( tf.lookup.KeyValueTensorInitializer(keys, values), default_value=0 # 默认映射到背景类 ) # 查找得到类别索引 label_idx = lookup_table.lookup(tf.cast(rgb_flat, tf.uint32)) return label_idx def load_voc_label_tf_rgb(image_path): file = tf.io.read_file(image_path) # 解码为RGB图像 label_rgb = tf.image.decode_png(file, channels=3, dtype=tf.uint8) # 转换为类别索引 label_idx = rgb_to_label_idx(label_rgb) return label_idx
验证方法
你可以拿一张标注图测试两种方法的结果是否和PIL一致:
import numpy as np from PIL import Image # PIL的结果 with Image.open(image_path) as img: pil_label = np.array(img) # TensorFlow的结果 tf_label = load_voc_label_tf(image_path).numpy() # 检查是否完全相等 print(np.array_equal(pil_label, tf_label)) # 正确情况下会输出True
内容的提问来源于stack exchange,提问作者Kousuke Kuzuoka
相关产品推荐
相关产品推荐

