使用tf.image.sample_distorted_bounding_box时边界框与裁剪图不匹配问题排查
我之前也踩过这个坑!用tf.image.sample_distorted_bounding_box生成的框和裁剪图不匹配,大概率是这几个原因导致的,咱们一步步排查:
1. 最常见的问题:resize后的图像尺寸没同步给框生成函数
sample_distorted_bounding_box需要知道当前要处理的图像的实际尺寸(height, width),如果你先resize了图像,但还是用原图像的尺寸去生成框,那框的比例坐标是基于原图像的,放到resize后的小图上肯定会错位。
比如你原图像是(500,500),resize到(200,200),但生成框时传入的是原图像的shape,那生成的框坐标比例是对应500x500的,放到200x200的图上,位置就完全不对了。
修正要点:生成框时必须传入resize后图像的shape,而不是原图像的:
# 先resize图像 image_small = tf.image.resize(image_float, (224, 224)) # 获取resize后的图像尺寸(height, width) img_shape = tf.shape(image_small)[0:2] # 把这个shape传给sample_distorted_bounding_box的第一个参数 begin, size, bboxes = tf.image.sample_distorted_bounding_box( img_shape, bounding_boxes=boxes, # 其他参数... )
2. 确认bounding box的坐标格式是否正确
TensorFlow里的bounding box格式是**[y_min, x_min, y_max, x_max]**,而且是相对于图像尺寸的比例值(范围0~1)。如果你不小心写成了[x_min, y_min, ...]的顺序,生成的框就会上下左右颠倒,自然和裁剪图不匹配。
你代码里的boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], ...]]),要确认第一个0.05是y方向的比例,第二个0.05是x方向的比例,别搞反了。
3. 裁剪时要用和生成框同尺寸的图像
如果生成框是基于resize后的image_small,那裁剪时必须用image_small,而不是原图像img_data。要是搞混了,裁剪的图像尺寸和框的比例不对应,结果肯定不对。
正确的裁剪代码:
# 用生成的begin和size裁剪resize后的图像 cropped_image = tf.slice(image_small, begin, size)
4. 检查sample_distorted_bounding_box的参数设置
这个函数是用来生成扭曲后的边界框的(比如随机缩放、偏移),如果你的参数设置太严格,可能会导致框的变形超出预期:
min_object_covered:如果设置得太高(比如0.9),可能会强制框覆盖大部分原始目标,导致扭曲过度aspect_ratio_range:如果设置的范围太窄(比如(1.0,1.0)),只能生成正方形框,可能和你预期的框形状不符- 如果不需要扭曲,只是想基于原始框裁剪,建议用
tf.image.crop_to_bounding_box(需要像素坐标)或者tf.image.crop_and_resize(支持比例坐标)
完整的修正代码示例
import tensorflow as tf import numpy as np from PIL import Image # 加载原始图像(替换成你的图像路径) img_data = np.array(Image.open("your_image.jpg")) with tf.Session() as sess: # 定义原始边界框:格式是[y_min, x_min, y_max, x_max],比例值 boxes = tf.constant([[[0.05, 0.05, 0.9, 0.7], [0.35, 0.47, 0.5, 0.56]]]) # 转成float32格式 image_float = tf.image.convert_image_dtype(img_data, tf.float32) # resize到目标尺寸 target_size = (224, 224) image_small = tf.image.resize(image_float, target_size) # 获取resize后的图像尺寸(height, width) img_shape = tf.shape(image_small)[0:2] # 正确调用sample_distorted_bounding_box begin, size, distorted_boxes = tf.image.sample_distorted_bounding_box( img_shape, bounding_boxes=boxes, min_object_covered=0.1, aspect_ratio_range=(0.75, 1.33), area_range=(0.05, 1.0), max_attempts=100, use_image_if_no_bounding_boxes=True ) # 裁剪resize后的图像 cropped_image = tf.slice(image_small, begin, size) # 运行会话获取结果 cropped_img, final_box, resized_img = sess.run([cropped_image, distorted_boxes, image_small]) # 可选:验证框和裁剪图是否匹配(把框画到resized_img上) # from PIL import ImageDraw # draw = ImageDraw.Draw(Image.fromarray((resized_img*255).astype(np.uint8))) # y_min, x_min, y_max, x_max = final_box[0][0] # draw.rectangle([x_min*224, y_min*224, x_max*224, y_max*224], outline="red", width=2)
内容的提问来源于stack exchange,提问作者huangjs

