ARB桶很大程度上省略了手动裁切的步骤,但自动裁剪也并不总是那么可靠。
比如我有一张图片如下,像素为——宽810*高1683:
设置的训练分辨率和ARB桶规格如下:
resolution = "512,768"
enable_bucket = true
min_bucket_reso = 512
max_bucket_reso = 1536
bucket_reso_steps = 64
那么这张图就会被分到最接近的一个比例(512,768)中:接着,程序先将图片等比缩小,宽度缩小至512,高度则缩减至512/810*1683=1064;
然后,会将缩小后的图片(512,1064)裁切为(512,768)的图片。
关于裁切,目前的代码是这样的:
if image_width > reso[0]:
trim_size = image_width - reso[0]
p = trim_size // 2 if not random_crop else random.randint(0, trim_size)
# logger.info(f"w {trim_size} {p}")
image = image[:, p : p + reso[0]]
if image_height > reso[1]:
trim_size = image_height - reso[1]
p = trim_size // 2 if not random_crop else random.randint(0, trim_size)
logger.info(f"h {trim_size} {p}")
image = image[p : p + reso[1]]
reso[0]即为指定宽512,reso[1]即为指定高768,
很明显,高度需要裁切,且裁剪的大小为1064-768=296px;
程序目前的做法是,将裁剪的大小除以2,然后上下各裁一段,类似centerCrop;
针对上面的图片,就是上面裁148px,下面裁148px,如下图红色区域即为裁切部分:
问题就在这里了。
一般选的图肯定是头部在上,而lora训练尤其是真人训练,头部是非常重要的,但因为要裁切的原因,直接把头部裁了一半......
我肯定是希望保留上半,而多裁下半,如下图:
那么解决办法也非常简单,代码稍微改动一下就行,即在高度裁切中,把image = image[p : p + reso[1]]替换为image = image[0 : reso[1]]即可:
if image_height > reso[1]:
trim_size = image_height - reso[1]
p = trim_size // 2 if not random_crop else random.randint(0, trim_size)
logger.info(f"h {trim_size} {p}")
image = image[0 : reso[1]]
当然,像上面这样改动裁切,如果遇上比例更为极端的图片,有可能把图片下半的很多tag直接裁没了,也是一个问题,虽然原来的代码也会存在这个问题就是了。
所以有时候久练不像并不是参数的原因,还是得归结于图片素材的选取;
比例越是与训练图片的分辨率接近,裁切的部分就越小,理论上就越容易获得好结果;
反之,比例差距越是悬殊,哪怕图片选择的再高清再好看,最终被程序裁切了主体,也是白费功夫。
从这个角度而言,手动裁剪确实比较稳定,至少可以在久练不像时优先排除图片素材的原因:-)
也可以像我一样,加一行输出裁切后的图片来检查最终训练时用的图片是否都正常可用:
for info in image_infos:
image = load_image(info.absolute_path) if info.image is None else np.array(info.image, np.uint8)
# TODO 画像のメタデータが壊れていて、メタデータから割り当てたbucketと実際の画像サイズが一致しない場合があるのでチェック追加要
image, original_size, crop_ltrb = trim_and_resize_if_required(random_crop, image, info.bucket_reso, info.resized_size)
logger.info(f"reso={info.resized_size} crop_ltrb={crop_ltrb} {os.path.splitext(info.absolute_path)[0]}")
cv2.imwrite("本地目录"+os.path.splitext(os.path.basename(info.absolute_path))[0]+"_crop.png", image)
image = IMAGE_TRANSFORMS(image)
images.append(image)
info.latents_original_size = original_size
info.latents_crop_ltrb = crop_ltrb
肉眼就能看到裁切的结果: