前言
最近多模态大模型层出不穷,笔者关注到了很多大模型中都采用了crop进行图像增强,如:llava、Intern-VL1.5等,以Intern-VL1.5中的瓷砖crop方法为例,
看看在图像处理过程中如何通过动态匹配最佳的宽高比(aspect ratio)来保持自然的图像比例。
-
动态宽高比匹配(Dynamic Aspect Ratio Matching)
在处理图像时,为了确保图像的自然宽高比保持不变,需要动态地选择最合适的宽高比。这意味着根据图像的实际宽高比从预定义的宽高比集合中选择一个最匹配的比率。
-
预定义的宽高比集合
为了在训练过程中保持计算资源的限制,系统预定义了一组宽高比。这里提到的最大数量是12个图像块,因此预定义的宽高比集合包括了由1到12个图像块形成的35种可能的宽高比组合。例如,{1:1, 1:2, 2:1, 3:1, …, 2:6} 表示从1x1的块到2x6的块的各种组合。
-
匹配过程
- 计算图像的宽高比:对于每个输入图像,计算其实际的宽高比。
- 比较宽高比:将输入图像的宽高比与预定义的35种宽高比进行比较。比较的标准是计算绝对差异,即找到最接近输入图像宽高比的预定义宽高比。
- 处理多个匹配:如果有多个预定义的宽高比与输入图像的宽高比相匹配(例如,1:1 和 2:2),系统会优先选择一个宽高比,该比率不会导致图像面积扩大超过输入图像面积的两倍。这是为了避免对低分辨率图像进行过度放大。
-
防止过度放大
优先选择面积不超过输入图像面积两倍的宽高比可以防止图像被过度放大。这样可以避免低分辨率图像在处理过程中因为过度放大而失去细节或出现失真。
代码实现及注释
直接看代码实现及注释
def find_closest_aspect_ratio(aspect_ratio, target_ratios, width, height, image_size):
best_ratio_diff = float('inf')
best_ratio = (1, 1)
area = width * height
for ratio in target_ratios:
target_aspect_ratio = ratio[0] / ratio[1]
ratio_diff = abs(aspect_ratio - target_aspect_ratio)
if ratio_diff < best_ratio_diff:
best_ratio_diff = ratio_diff
best_ratio = ratio
elif ratio_diff == best_ratio_diff:
if area > 0.5 * image_size * image_size * ratio[0] * ratio[1]:
best_ratio = ratio
# print(f'width: {width}, height: {height}, best_ratio: {best_ratio}')
return best_ratio
def dynamic_preprocess(image, min_num=1, max_num=6, image_size=1024, use_thumbnail=True):
'''
:param image: 输入的图像对象
:param min_num: 用于计算目标宽高比的最小乘积
:param max_num: 用于计算目标宽高比的最大乘积
:param image_size: 目标图像块的大小
:param use_thumbnail: 是否在最后生成一个缩略图并附加到结果图像列表中
:return:
'''
orig_width, orig_height = image.size
# 计算输入图像的宽度、高度,并得出图像的宽高比
aspect_ratio = orig_width / orig_height
# target_ratios 计算并存储了可能的目标宽高比,通过双重循环和条件筛选生成一系列可能的目标宽高比,
# 这些宽高比的乘积在min_num和max_num之间。对这些宽高比进行排序,以便后续选择最接近原始图像宽高比的目标宽高比。
target_ratios = set(
(i, j) for n in range(min_num, max_num + 1) for i in range(1, n + 1) for j in range(1, n + 1) if
i * j <= max_num and i * j >= min_num)
# print(target_ratios)
target_ratios = sorted(target_ratios, key=lambda x: x[0] * x[1])
# find_closest_aspect_ratio 用于在计算出的target_ratios中找到最接近输入图像的宽高比
target_aspect_ratio = find_closest_aspect_ratio(
aspect_ratio, target_ratios, orig_width, orig_height, image_size)
# print(target_aspect_ratio)
# 根据找到的目标宽高比,计算出目标图像的宽度和高度。blocks 表示将图像分割的块数。
target_width = image_size * target_aspect_ratio[0]
target_height = image_size * target_aspect_ratio[1]
blocks = target_aspect_ratio[0] * target_aspect_ratio[1]
# 将图像调整为目标宽度和高度。然后,根据计算出的块数,将图像逐块裁剪并保存到processed_images列表中
resized_img = image.resize((target_width, target_height))
processed_images = []
for i in range(blocks):
box = (
(i % (target_width // image_size)) * image_size,
(i // (target_width // image_size)) * image_size,
((i % (target_width // image_size)) + 1) * image_size,
((i // (target_width // image_size)) + 1) * image_size
)
# split the image
split_img = resized_img.crop(box)
processed_images.append(split_img)
assert len(processed_images) == blocks
# 如果use_thumbnail为True,并且图像块数大于1,则生成一个缩略图(将原始图像调整为 image_size x image_size),
# 并将其附加到processed_images列表中。
if use_thumbnail and len(processed_images) != 1:
thumbnail_img = image.resize((image_size, image_size))
processed_images.append(thumbnail_img)
return processed_images
if __name__ == '__main__':
image = Image.open('image.png')
processed_images = dynamic_preprocess(image)
for idx, img in enumerate(processed_images):
img.show()
img.save(f'output/processed_image_{idx}.jpg')
总结
本文记录了常见在多模态任务中的数据增强方法crop实现,在具体应用中,可以根据一些特定数据集的特点修改crop的方式。