基于diffusers库 简单实现图像的outpaint(图像外扩)

距离上次更新已经过去两年,实在是比较懒。最近工作中需要批量进行图像的outpaint操作,笔者对文生图、图生图、图生视频的了解学习是不够充分的。只想基于diffusers库快速复现出outpaint的功能。huggingface上基于diffusers库有很多模型做inpaint的实现,例如SD1.5 、SD2、SD3、SDXL、flux等等。本质上outpaint是一种特殊的inpaint,但是找遍huggingface也没有直接实现的,开源的很多都是基于comfyUI或者powerpaint等方式,感觉还是比较复杂,不是傻瓜式的 几十行代码快速复现。用inpaint模型自己尝试的时候发现有些模型有坑,怎么都做不出outpaint的操作,给自己造成了困扰,特此记录一下。

目录

一、选inpaint的模型

二、扩充图像和重绘mask

三、outpaint的推理


本篇宗旨,无需特别了解文生图、图生图的原理,直接实现扩图。话不多说,开始正文。

一、选inpaint的模型

        outpaint的本质上也是inpaint,只不过从图像内部的局部重绘变成了,外扩部分的重绘,都参考了整幅图像的语义信息。

        笔者用了两个inpaint模型:diffusers/stable-diffusion-xl-1.0-inpainting-0.1,black-forest-labs/FLUX.1-Fill-dev。其中sdxl模型有坑,他只能上下或者左右进行扩图。不能够上下左右同时,并且expand image时候要用127像素,笔者之前用0 或255填充,怎么都做不出outpaint的操作,而flux.1-fill就无所谓了,官方说此模型也可以做outpaint,实测下来确实如此。下面代码是两个模型,具体用哪个注释掉另一个就行。关于huggingface上的模型怎么下载,就不多言了。

from diffusers import AutoPipelineForInpainting
from diffusers import FluxFillPipeline
from diffusers.utils import load_image
import torch
import os
import numpy as np
import cv2
import random
from PIL import Image

"""
sdxl

"""
pipe = AutoPipelineForInpainting.from_pretrained("/data/caidou/diffusers/stable-diffusion-xl-1.0-inpainting-0.1", torch_dtype=torch.float16, variant="fp16").to("cuda")
generator = torch.Generator(device="cuda").manual_seed(0)


"""
flux.1fill-dev
"""

pipe = FluxFillPipeline.from_pretrained("/data/caidou/black-forest-labs/FLUX.1-Fill-dev", torch_dtype=torch.bfloat16).to("cuda")

二、扩充图像和重绘mask

expand image 参考PowerPaint的做法。很多方法的expand image应该都是经过了一个模型,经过语义理解,重新的到一张待重绘的带噪声图,然后在用inpaint模型进行重绘。笔者直接使用127像素值填充待原图的待扩充区域。

def get_expand_img_mask(input_image,vertical_expansion_ratio,horizontal_expansion_ratio):

    if vertical_expansion_ratio is not None and horizontal_expansion_ratio is not None:
        # o_W, o_H = input_image["image"].convert("RGB").size
        o_W, o_H = input_image.convert("RGB").size
        c_W = int(horizontal_expansion_ratio * o_W)
        c_H = int(vertical_expansion_ratio * o_H)

        expand_img = np.ones((c_H, c_W, 3), dtype=np.uint8) * 127
        original_img = np.array(input_image)
        expand_img[
            int((c_H - o_H) / 2.0) : int((c_H - o_H) / 2.0) + o_H,
            int((c_W - o_W) / 2.0) : int((c_W - o_W) / 2.0) + o_W,
            :,
        ] = original_img

        blurry_gap = 0

        expand_mask = np.ones((c_H, c_W, 3), dtype=np.uint8) * 255
        if vertical_expansion_ratio == 1 and horizontal_expansion_ratio != 1:
            expand_mask[
                int((c_H - o_H) / 2.0) : int((c_H - o_H) / 2.0) + o_H,
                int((c_W - o_W) / 2.0) + blurry_gap : int((c_W - o_W) / 2.0) + o_W - blurry_gap,
                :,
            ] = 0
        elif vertical_expansion_ratio != 1 and horizontal_expansion_ratio != 1:
            expand_mask[
                int((c_H - o_H) / 2.0) + blurry_gap : int((c_H - o_H) / 2.0) + o_H - blurry_gap,
                int((c_W - o_W) / 2.0) + blurry_gap : int((c_W - o_W) / 2.0) + o_W - blurry_gap,
                :,
            ] = 0
        elif vertical_expansion_ratio != 1 and horizontal_expansion_ratio == 1:
            expand_mask[
                int((c_H - o_H) / 2.0) + blurry_gap : int((c_H - o_H) / 2.0) + o_H - blurry_gap,
                int((c_W - o_W) / 2.0) : int((c_W - o_W) / 2.0) + o_W,
                :,
            ] = 0

        expand_img = Image.fromarray(expand_img)
        expand_mask = Image.fromarray(expand_mask)

        return expand_img,expand_mask

三、outpaint的推理

扩图的比例合适就行。其它参数也可以调一调。prompt空就可以,啥也不用写就能outpaint。

1. sdxl-inpaint

if __name__=="__main__":
    image = load_image(image_path)
    expandimg,expandmask = get_expand_img_mask(image,vertical_expansion_ratio=1,horizontal_expansion_ratio=1.2)
    W = int(np.shape(expandimg)[0] - np.shape(expandimg)[0] % 8)
    H = int(np.shape(expandimg)[1] - np.shape(expandimg)[1] % 8)# sdxl的size必须要被8整除
    expandimg = expandimg.resize((H, W))
    expandmask = expandmask.resize((H, W))
    w,h = expandimg.size
    print(expandimg.size,expandmask.size)
    generator = torch.Generator(device="cuda").manual_seed(0)
    image = pipe(
    prompt="",
    height=h,
    width=w,
    image=expandimg,
    mask_image=expandmask,
    guidance_scale=8.0,
    num_inference_steps=20,  # steps between 15 and 30 work well for us
    strength=0.99,  # make sure to use `strength` below 1.0
    generator=generator,
    ).images[0]
    image.save("./sdxl-outpaint.jpg")

原图部分也是变模糊了,扩图效果这张不好,其实好多张还是可以的。

2. flux.1fill-dev

if __name__=="__main__":    
    image = load_image(image_path)
    expandimg,expandmask = get_expand_img_mask(image,vertical_expansion_ratio=1.2,horizontal_expansion_ratio=1.0)
    w,h = expandimg.size
    print(expandimg.size,expandmask.size)
    image = pipe(
        prompt="",
        image=expandimg,
        mask_image=expandmask,
        height=h,
        width=w,
        guidance_scale=30,
        num_inference_steps=50,
        max_sequence_length=512,
        generator=torch.Generator("cpu").manual_seed(0)
    ).images[0]
    image.save("./flux.1fill-dev_outpaint.jpg")

结果看起来原图保持的其实一般,扩图之后的原图部分会比较模糊,但是flux的扩图的效果还是比较好的。

发现很多博主涉及到outpainting都开收费,很是无语...特此简单记录下,如果对大家有帮助 欢迎点赞

### Hugging Face Diffusers 使用指南 #### 一、简介 Hugging Face Diffusers 是一个用于实现散模型的深度学习,能够帮助开发者轻松创建高质量的图像和音频内容。通过该可以快速上手并掌握如何构建基于散模型的应用程序[^1]。 #### 二、安装与配置 要开始使用Diffusers,需先完成其安装过程: 对于Python环境而言,推荐采用pip工具来进行包管理;打开命令行终端执行如下指令即可下载最新版本的Diffusers: ```bash pip install diffusers ``` 如果希望获得更稳定的体验,则可以选择指定特定版本号的方式进行安装: ```bash pip install "diffusers==0.9.0" ``` 另,在某些情况下可能还需要额安装其他依赖项,比如transformers等辅助来增强功能支持。此时可以通过下面这条语句一次性搞定所有必要的组件: ```bash pip install "diffusers[torch]" ``` #### 三、基础操作实例 一旦成功设置了工作空间之后就可以着手尝试一些简单的例子啦! 这里给出一段用来加载预训练好的稳定散模型(Stable Diffusion Model)并将随机噪声逐步转化为逼真图片的小脚本作为入门级练习: ```python from diffusers import StableDiffusionPipeline import torch model_id = "CompVis/stable-diffusion-v1-4" device = "cuda" if torch.cuda.is_available() else "cpu" pipe = StableDiffusionPipeline.from_pretrained(model_id).to(device) prompt = "a photograph of an astronaut riding a horse" image = pipe(prompt)["sample"][0] image.save("astronaut_rides_horse.png") ``` 上述代码片段展示了如何定义目标描述文本(`prompt`)并通过调用`pipe()`方法传入参数触发生成流程最终得到一张保存于本地磁盘上的PNG格式文件。 #### 四、深入探究 当掌握了基础知识以后不妨继续挖掘更多有趣的话题吧!例如探索不同类型的散算法差异所在或是研究怎样优化超参设置提高产出效率等等。官方文档提供了详尽的技术细节说明以及丰富的实践案例供参考学习[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值