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

自定义数据增强生成图片数量未达预期问题排查

数据增强代码生成图片数量不符问题排查

问题描述

期望通过自定义数据增强代码生成5700张图片(原始图片数量的10倍),但实际仅生成了1140张(仅为原始数量的2倍),需排查代码问题。

原始代码

# Custom augmentation functions
def random_translate(image, max_translate):
    shift = np.random.uniform(-max_translate, max_translate, 3)
    translated_image = scipy.ndimage.shift(image, shift, mode='nearest')
    return translated_image

def random_rotate(image, max_angle):
    angles = np.random.uniform(-max_angle, max_angle, 3)
    rotated_image = scipy.ndimage.rotate(image, angles[0], axes=(1, 2), reshape=False)
    rotated_image = scipy.ndimage.rotate(rotated_image, angles[1], axes=(0, 2), reshape=False)
    rotated_image = scipy.ndimage.rotate(rotated_image, angles[2], axes=(0, 1), reshape=False)
    return rotated_image

def random_flip(image):
    if random.random() > 0.5:
        image = np.flip(image, axis=0)
    if random.random() > 0.5:
        image = np.flip(image, axis=1)
    if random.random() > 0.5:
        image = np.flip(image, axis=2)
    return image

def random_noise(image, noise_level=0.01):
    noise = np.random.normal(0, noise_level, image.shape)
    noisy_image = image + noise
    return noisy_image

def random_brightness(image, max_delta=0.2):
    delta = np.random.uniform(-max_delta, max_delta)
    bright_image = np.clip(image + delta, 0, 1)  # Clip to maintain valid pixel range
    return bright_image

def random_contrast(image, lower=0.8, upper=1.2):
    factor = np.random.uniform(lower, upper)
    mean = np.mean(image, axis=(0, 1, 2), keepdims=True)
    contrast_image = np.clip((image - mean) * factor + mean, 0, 1)
    return contrast_image

def random_scale(image, min_scale=0.9, max_scale=1.1):
    scale = np.random.uniform(min_scale, max_scale)
    height, width = image.shape[:2]
    scaled_image = scipy.ndimage.zoom(image, (scale, scale, 1), order=1)
    if scale < 1.0:
        pad_height = (height - scaled_image.shape[0]) // 2
        pad_width = (width - scaled_image.shape[1]) // 2
        scaled_image = np.pad(scaled_image, ((pad_height, pad_height), (pad_width, pad_width), (0, 0)), mode='constant')
    else:
        start_height = (scaled_image.shape[0] - height) // 2
        start_width = (scaled_image.shape[1] - width) // 2
        scaled_image = scaled_image[start_height:start_height + height, start_width:start_width + width]
    return scaled_image

def random_shear(image, max_shear=0.2):
    shear = np.random.uniform(-max_shear, max_shear)
    afine_tf = tf.keras.preprocessing.image.random_shear(shear)
    shear_image = tf.keras.preprocessing.image.apply_affine_transform(image, shear=afine_tf)
    return shear_image

def elastic_transform(image, alpha=1000, sigma=30):
    random_state = np.random.RandomState(None)
    shape = image.shape
    dx = scipy.ndimage.gaussian_filter((random_state.rand(*shape) * 2 - 1), sigma, mode="constant", cval=0) * alpha
    dy = scipy.ndimage.gaussian_filter((random_state.rand(*shape) * 2 - 1), sigma, mode="constant", cval=0) * alpha
    dz = scipy.ndimage.gaussian_filter((random_state.rand(*shape) * 2 - 1), sigma, mode="constant", cval=0) * alpha
    x, y, z = np.meshgrid(np.arange(shape[0]), np.arange(shape[1]), np.arange(shape[2]), indexing='ij')
    indices = np.reshape(x + dx, (-1, 1)), np.reshape(y + dy, (-1, 1)), np.reshape(z + dz, (-1, 1))
    distored_image = scipy.ndimage.map_coordinates(image, indices, order=1, mode='reflect')
    return distored_image.reshape(image.shape)


def custom_data_generator(images, labels, batch_size,
                          max_translate=10, max_angle=15, noise_level=0.01,
                          max_delta=0.2, lower_contrast=0.8, upper_contrast=1.2,
                          min_scale=0.9, max_scale=1.1, max_shear=0.2, alpha=1000, sigma=30):
    num_images = images.shape[0]
    while True:
        batch_indices = np.random.choice(num_images, batch_size)
        batch_images = []
        batch_labels = []
        for idx in batch_indices:
            image = images[idx]
            label = labels[idx]

            # Apply all augmentation functions
            image = random_translate(image, max_translate)
            image = random_rotate(image, max_angle)
            image = random_flip(image)
            image = random_noise(image, noise_level)
            image = random_brightness(image, max_delta)
            image = random_contrast(image, lower_contrast, upper_contrast)
            image = random_scale(image, min_scale, max_scale)
            image = tf.keras.preprocessing.image.random_shear(image, max_shear)
            image = elastic_transform(image, alpha, sigma)

            batch_images.append(image)
            batch_labels.append(label)

        batch_images = np.array(batch_images)
        batch_labels = np.array(batch_labels)
        yield batch_images, batch_labels


# Directory to save the images
save_dir = 'mri_augmented_images'
os.makedirs(save_dir, exist_ok=True)

for i in range(mri_resized.shape[0]):
    for j in range(mri_resized.shape[3]):  # Loop over channels
        img_array = mri_resized[i, :, :, j]
        img = Image.fromarray((img_array * 255).astype('uint8'))  # Scale to [0, 255] and convert to uint8
        img.save(os.path.join(save_dir, f'image_{i}_channel_{j}.png'))

# Using the custom data generator to augment images and retain labels
target_num_images = 5700
batch_size = 3  # Define your batch size
num_batches_needed = (target_num_images + batch_size - 1) // batch_size
data_gen = custom_data_generator(mri_resized, labels, batch_size=batch_size)

# Generate and save augmented images with labels
for batch_images, batch_labels in data_gen:  # Directly iterate over the generator
    for j, img_array in enumerate(batch_images):
        img = Image.fromarray((img_array[:, :, 0] * 255).astype('uint8'))  # Save only the first channel
        img_index = i * batch_size + j
        img.save(os.path.join(save_dir, f'aug_image_{img_index}_label_{batch_labels[j]}.png'))

print(f"Generated and saved {target_num_images} augmented images.")

问题排查与修正

1. 生成循环无终止条件+索引计算错误

原始代码直接遍历无限生成器data_gen,且使用保存原始图循环的残留变量i计算img_index,导致所有增强图索引重复,文件被不断覆盖,最终仅保留少量图片。

修正方案:跟踪已生成图片数量,达到目标后终止循环,用独立计数器作为索引:

# 修正后的增强图生成与保存代码
generated_count = 0
data_gen = custom_data_generator(mri_resized, labels, batch_size=batch_size)

while generated_count < target_num_images:
    batch_images, batch_labels = next(data_gen)
    # 取当前batch中需要的数量(避免最后一个batch超出目标)
    take = min(len(batch_images), target_num_images - generated_count)
    for j in range(take):
        img_array = batch_images[j]
        img = Image.fromarray((img_array[:, :, 0] * 255).astype('uint8'))
        img.save(os.path.join(save_dir, f'aug_image_{generated_count}_label_{batch_labels[j]}.png'))
        generated_count += 1

print(f"Generated and saved {generated_count} augmented images.")

2. random_shear函数用法错误

原函数中tf.keras.preprocessing.image.random_shear调用参数错误,该函数第一个参数应为图像而非shear值,会导致运行报错中断生成。

修正方案:

def random_shear(image, max_shear=0.2):
    shear = np.random.uniform(-max_shear, max_shear)
    # 直接调用random_shear处理图像,intensity参数传入剪切角度
    shear_image = tf.keras.preprocessing.image.random_shear(image, intensity=shear)
    return shear_image

3. 2D图像误用3D变换逻辑

random_translate、random_rotate、elastic_transform函数针对3D图像设计了三维变换(如shift取3个值、三维旋转、dz分量),但你的图像是2D+通道结构(shape为(height, width, channels)),通道维度不需要变换,会导致无效操作甚至图像变形。

修正示例(以random_translate为例):

def random_translate(image, max_translate):
    # 仅对height和width维度做平移,通道维度不变
    shift = np.random.uniform(-max_translate, max_translate, 2)
    # 补全通道维度的平移量为0
    shift = np.append(shift, 0)
    translated_image = scipy.ndimage.shift(image, shift, mode='nearest')
    return translated_image

4. 原始图与增强图数量混淆

原始代码先保存了所有原始图像的通道图,这部分会被计入总文件数,但你需要的是5700张增强图,需确保增强图的生成计数独立于原始图。

总结

核心问题是增强图生成循环无终止逻辑且索引重复导致文件覆盖,同时存在变换函数的参数错误。修正上述问题后,即可生成目标数量的增强图片。

内容的提问来源于stack exchange,提问作者dhruv Gangwani

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 02:19:52