Keras Stable Diffusion : インペインティングの簡易アプリケーション (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 12/30/2022
* 本ページは、github の divamgupta/stable-diffusion-tensorflow レポジトリの以下のドキュメント内の Colab ノートブックを翻訳した上でまとめ直したものです。一部は修正しています:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- 人工知能研究開発支援
- 人工知能研修サービス(経営者層向けオンサイト研修)
- テクニカルコンサルティングサービス
- 実証実験(プロトタイプ構築)
- アプリケーションへの実装
- 人工知能研修サービス
- PoC(概念実証)を失敗させないための支援
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
- 株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
- sales-info@classcat.com ; Web: www.classcat.com ; ClassCatJP
Keras Stable Diffusion : インペインティングの簡易アプリケーション
このノートブックでは Stable Diffusion のインペインティングを実演しますが、最初に基本的なテキスト-to-画像変換と画像-to-画像変換を動作確認しておきます。
GPU 要件のインストール
!pip install git+https://github.com/divamgupta/stable-diffusion-tensorflow --upgrade --quiet
!pip install tensorflow tensorflow_addons ftfy --upgrade --quiet
from tensorflow import keras
import requests
from PIL import Image, ImageOps, ImageFilter
from stable_diffusion_tf.stable_diffusion import StableDiffusion
import io
from scipy.ndimage import gaussian_filter
import numpy as np
StableDiffusion を混合精度で利用します :
keras.mixed_precision.set_global_policy("mixed_float16")
generator = StableDiffusion(
img_height=512,
img_width=512,
jit_compile=False,
)
Text2Image
最初に、テキスト-to-画像変換を試しておきます :
img = generator.generate(
"A dog with sunglasses, wearing comfy hat, looking at camera, highly detailed, ultra sharp, cinematic, 100mm lens, 8k resolution.",
num_steps=50,
unconditional_guidance_scale=8,
temperature=1,
batch_size=1,
input_image_strength=0.9
)
display(Image.fromarray(img[0]))
Image2Image
次に画像-to-画像変換です。まず初期画像を取得します :
# load from URL...
image_url = 'https://hips.hearstapps.com/ghk.h-cdn.co/assets/17/30/pembroke-welsh-corgi.jpg?crop=1xw:0.9997114829774957xh;center,top&resize=980:*'
input_image = Image.open(requests.get(image_url, stream=True).raw)
w, h = input_image.size
input_image = input_image.crop((0, 150, w-0, h-150)).resize((512, 512))
display(input_image)
input_image.convert("RGB").save("init_image.png", "PNG")
初期画像をベースに画像生成します :
img = generator.generate(
"A dog with sunglasses, wearing comfy hat, looking at camera, highly detailed, ultra sharp, cinematic, 100mm lens, 8k resolution.",
num_steps=50,
unconditional_guidance_scale=8,
temperature=1,
batch_size=1,
input_image='init_image.png',
input_image_strength=0.9
)
display(Image.fromarray(img[0]))
3. インペインティング
次にインペインティングを実演する簡単なデモを作成します。
最初にマスク描画用の canvas 関数を定義します :
#@title Define canvas functions for mask drawing
from google.colab.output import eval_js
from base64 import b64decode, b64encode
from IPython.display import display as ip_display, HTML
mask_drawn = False
def to_raw_img(imgin) :
outputi = io.BytesIO()
imgin.save(outputi, format='PNG')
encoded_string = b64encode(outputi.getvalue()).decode()
return "data:image/png;base64,{}".format(encoded_string)
def mask_image(im_path, w, h, blur_size=0) :
bicfn = lambda x : 0 if x > 0 else 255
mask_img = Image.open(im_path).convert("RGBA")
final_mask_img = Image.new("RGBA", (w,h), color=(255,255,255))
final_mask_img = Image.alpha_composite(final_mask_img, mask_img)
final_mask_img = final_mask_img.convert("L").point(bicfn, mode='1').convert("L")
final_mask_img = final_mask_img.filter(ImageFilter.GaussianBlur(radius=blur_size))
final_mask_img.save(im_path)
# ip_display(final_mask_img)
return final_mask_img
canvas_html = """
<h2> Erase the part of the image you want to inpaint, then click "Save" </h2>
<canvas width=%d height=%d style="background: url('%s');"></canvas>
<button id="save">Save</button>
<button id="reset">Reset</button>
<div class="slidecontainer">
<input type="range" min="1" max="100" value="25" class="slider" id="range">
</div>
<script>
var canvas = document.querySelector('canvas')
var ctx = canvas.getContext('2d')
ctx.strokeStyle = 'rgba(255, 0, 0, 0.7)';
var slider = document.getElementById("range")
var save_button = document.querySelector('#save')
var reset_button = document.querySelector('#reset')
var mouse = {x: 0, y: 0}
canvas.addEventListener('mousemove', function(e) {
mouse.x = e.pageX - this.offsetLeft
mouse.y = e.pageY - this.offsetTop
})
canvas.onmousedown = ()=>{
ctx.beginPath()
ctx.moveTo(mouse.x, mouse.y)
canvas.addEventListener('mousemove', onPaint)
}
canvas.onmouseup = ()=>{
canvas.removeEventListener('mousemove', onPaint)
}
reset_button.onclick = ()=>{{
ctx.clearRect(0, 0, canvas.width, canvas.height);
// ctx.fillRect(0, 0, canvas.width, canvas.height);
}}
var onPaint = ()=>{
ctx.beginPath()
ctx.arc(mouse.x, mouse.y, slider.value, 0, Math.PI * 2, 0)
ctx.fill()
// ctx.lineTo(mouse.x, mouse.y)
// ctx.stroke()
}
var data = new Promise(resolve=>{
save_button.onclick = ()=>{
save_button.style.display = 'none'
reset_button.style.display = 'none'
slider.style.display = 'none'
resolve(canvas.toDataURL('image/png'))
}
})
"""
def draw_on_img(url, filename, w, h, blur_size):
ip_display(HTML(canvas_html % (w, h, url)))
data = eval_js("data")
binary = b64decode(data.split(',')[1])
with open(filename, 'wb') as f:
f.write(binary)
return mask_image(filename, w, h, blur_size)
先に使用したものと同じ初期画像をダウンロードします :
# load from URL...
image_url = 'https://hips.hearstapps.com/ghk.h-cdn.co/assets/17/30/pembroke-welsh-corgi.jpg?crop=1xw:0.9997114829774957xh;center,top&resize=980:*'
input_image = Image.open(requests.get(image_url, stream=True).raw)
w, h = input_image.size
input_image = input_image.crop((0, 150, w-0, h-150)).resize((512, 512))
display(input_image)
input_image.convert("RGB").save("init_image.png", "PNG")
マスク画像を編集します。
input_image = Image.open("init_image.png")
html_raw_img = to_raw_img(input_image)
mask_path = 'mask.png'
final_mask_img = draw_on_img(html_raw_img, mask_path, input_image.size[0], input_image.size[1], blur_size=3)
m = (np.array(final_mask_img) > 0).astype(float)
m2 = gaussian_filter(m, sigma=5)
m = np.minimum(gaussian_filter(m*200+m2*200, sigma=9), 255)
image = Image.fromarray(m.astype(np.uint8))
i = np.array(input_image)
i[m>=100] = 0
display(Image.fromarray(i))
display(image)
image.save('mask.png')
そして編集が完了したら “save” ボタンをクリックすれば、マスク画像が作成されます :
初期画像とマスク画像を使用してインペインティングを実行します :
img = generator.generate(
"A dog with sunglasses, wearing comfy hat, looking at camera, highly detailed, ultra sharp, cinematic, 100mm lens, 8k resolution.",
num_steps=50,
unconditional_guidance_scale=8,
temperature=1,
batch_size=1,
input_image='init_image.png',
input_mask='mask.png',
input_image_strength=0.9
)
display(Image.fromarray(img[0]))
以上