【第7週】画像の生成と変換
講義目録:即戦力化 ディープラーニング実習
はじめに
これまでの実習では、主に テキスト(txt2txt) 系タスクを扱い、
「生成 → 評価 → プロンプト改善」という一連の流れを体験してきました。
第7週では舞台を 画像分野 に移し、
1 行のコード変更で動かせる Stable Diffusion 系モデルを用いて
- text-to-image:文章 → 画像をゼロから生成
- image-to-image:既存画像を別スタイルに変換
を一気に体験します。
体験する内容は、次のgoogle colabで確認できます。
Google Colabへのリンク
1. 実習の目的
-
画像生成(text-to-image)の基本操作を習得する
-
画像変換(image-to-image / style-transfer)の代表的手法を比較する
- Img2Img : 画像を丸ごと別の画風へ変換する
- Instruct-Pix2Pix : 自然言語の指示文で部分編集を行う
- Img2Imgと Pix2Pixの違いを理解する
- ControlNet (Canny) : 線画に着色する
2. CNN とは
今週から畳み込みニューラルネットワーク(Convolutional Neural Network; CNN) を用いた実習が始まります。
CNNは、画像のような “空間的に隣接するピクセル同士の関連” を捉えるのが得意なネットワークです。
畳み込み層は 小さなフィルタ(カーネル)を画像全体に滑らせる ことで、エッジ・テクスチャ・形状といった局所パターンを抽出します。
それを多層化(深層化)して、頑張って計算してくれるのがCNNです。
CNNの詳しい説明は、webでも書籍であちこちに存在しますので、ここでは詳しく説明しないことにします。
RNN とのちがい
先週まで扱った RNN(再帰型ネットワーク) は「時系列」や「文の単語列」のような 一次元の“順序依存” をモデリングするのに特化しています。RNN は「時間的文脈」, CNN は「空間的文脈」 を捉えるアーキテクチャだという点が最大の違いです。
3-1 共通の準備
最初に、共通のライブラリをpipでinstallします。
google colab で動かそうとすると、いろいろと面倒でした。
まずは、深く考えず、お試し実行を優先することにしましょう。
!pip uninstall -y diffusers transformers huggingface_hub peft sentence-transformers -q || true
# 衝突しない組み合わせをクリーンインストール
!pip install -q --no-cache-dir \
"huggingface_hub==0.24.1" \
"transformers==4.25.1" \
"diffusers==0.26.3" \
accelerate safetensors xformers
Google Colab上で実習する場合、上記セルを実行後、
「ランタイム ▸ 再起動」 を行ってください。そうしないと、いろいろとエラーがでてきます。
Colab ノートブック例
セルの配置やランタイム再起動の手順が不安な場合は、
▶︎ 画像生成と変換の Colab サンプル を使ってください。
3-2 画像生成(text-to-image)
Stable Diffusion v1-5 を使い、テキストから 512×512 px の画像をゼロから生成しましょう。
3-2-1 パイプラインのロード
from diffusers import StableDiffusionPipeline
import torch, datetime
pipe = StableDiffusionPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=torch.float16,
).to("cuda")
初回は 4.3 GB の重みをダウンロードするため 1〜2 分かかります。
3-2-2 生成してみる
prompt = "A majestic tiger standing on a cliff at sunset, ultra-realistic, 8K"
#夕暮れの崖の上に立つ雄大な虎、超リアル、8K
image = pipe(prompt, num_inference_steps=30, guidance_scale=7.5).images[0]
display(image)
私の場合、きちんとトラが生成されました。みなさんは、いかがだったでしょうか。
なお、トラのimageは、後続の実習で再利用します。
では、prompt を変更し、みなさんも試してみてください。
えっ、日本語のプロンプトも試してみたいですって?
どうぞどうぞ。ぜひお試しあれ。
3-2-2 日本語プロンプトのお試し。
では早速、試してみましょう。
日本語と英語、それぞれ同じ内容のプロンプトを準備しました。
言語 | プロンプト |
---|---|
英語 | A black cat meows next to a hydrangea flower. |
日本語 | 黒猫がアジサイの花の横でにゃーんと鳴いている。 |
では実行してみましょう。
prompt_en = "A black cat meows next to a hydrangea flower."
prompt_jp = "黒猫がアジサイの花の横でにゃーんと鳴いている。"
image_en = pipe(prompt_en, num_inference_steps=30, guidance_scale=7.5).images[0]
image_en.save("cat_en.png")
display(image_en)
image_jp = pipe(prompt_jp, num_inference_steps=30, guidance_scale=7.5).images[0]
image_jp.save("cat_jp.png")
display(image_jp)
おお?猫に羽が生えている!!
どうやらこのモデルは日本語が苦手なようですね。
私が試したところ、指示を日本語で与えたら、背中に羽の生えた猫?鳥?みたいなへんてこな画像が生成されました。
みなさんも、同じような状況だったのではないでしょうか。
その理由ですが、今回用いたモデルが日本語を苦手としているからなのです。ではどうすればいい?
日本語のプロンプトを英語に翻訳してあげればいいのです。
ではその翻訳は誰がするのかって?
そりゃもちろん、深層学習のモデルに行ってもらえばいいのです。
即戦力化 ディープラーニング実習の「第3週 翻訳と要約」で学んだ成果をここで生かす時が来ましたよ。
3-3 画像変換(image-to-image / style-transfer)
ここからは、先ほど生成した 虎の画像 を init_image
として使い、
3 種類の変換手法(Img2Img / Instruct-Pix2Pix / ControlNet)を比較します。
3-3-1 共通コード
では最初に、3種類の変換手法に共通する部分です。
# 先ほど生成したトラの image を実習用のイメージに転用します
init_image = image.convert("RGB").resize((512, 512))
# 共通セットアップ
from diffusers import (
StableDiffusionImg2ImgPipeline,
StableDiffusionInstructPix2PixPipeline,
ControlNetModel,
StableDiffusionControlNetPipeline,
)
from diffusers.utils import load_image
import torch, requests, io, PIL.Image as Image
device = "cuda" # GPU
dtype = torch.float16 # VRAM 節約
ポイント
init_image
は 512×512 px・RGB に統一すると Stable Diffusion 1.5 系で最も安定します。- 以降の各パイプラインはすべて
device
とdtype
を共通化し、メモリ効率を最大化します。この準備ができたら、次節以降で
- Img2Img(画風変換)
- Instruct-Pix2Pix(指示文による部分編集)
- ControlNet (Canny)(線画固定+着彩)
を順に実行していきます。
3-3-2 Img2Img ─ 画風を「水彩画」へ変換
では、img2Img から始めましょう。先ほどのトラの絵を水彩画風に変換します。
pipe_img2img = StableDiffusionImg2ImgPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
torch_dtype=dtype,
).to(device)
prompt = "A watercolor painting of the same scene, A majestic tiger standing on a cliff at sunset, ultra-realistic, 8K"
#同じシーンを描いた水彩画, そのあとに、生成に利用したプロンプトをコピペしています
strength = 0.75 # 0=original維持, 1=完全生成
guidance = 7.5
image_out = pipe_img2img(
prompt = prompt,
image = init_image,
strength = strength,
guidance_scale = guidance,
num_inference_steps = 30,
).images[0]
image_out
背景が良くなりましたが、しっぽが2本になりました。
まぁ、これなら良いほうでしょうか。
運が良ければなんとか原型をとどめているかと思います。
3-3-3 Instruct-Pix2Pix ─ 指示文で「夜景+星空」に編集
次は違う仕組みを使って、背景を編集してみます。
pipe_p2p = StableDiffusionInstructPix2PixPipeline.from_pretrained(
"timbrooks/instruct-pix2pix",
torch_dtype=dtype,
safety_checker=None, # NSFW フィルタ不要なら外しても
).to(device)
pipe_p2p.enable_attention_slicing()
instruction = "Make it a night scene with a starry sky"
#星空のある夜景にする
image_edit = pipe_p2p(
prompt = instruction,
image = init_image,
num_inference_steps = 30,
guidance_scale = 7.5,
image_guidance_scale= 1.5, # 入力画像をどれだけ信頼するか
).images[0]
image_edit
プロンプト通り、夜の景色になりましたね。
皆さんもいろいろ試してみてください。
3-3-4 Img2Img2 とpix2pix の違い
子の実習では、最初にimg2img で水彩画に、続いてInstruct-Pix2Pix(pix2pix)で夜景に変換しました。
似たような変換ですが、手法としては何が違うのでしょうか?
Img2Img は「元画像にノイズを混ぜてから prompt に従って描き直す」方式で、絵全体を新しいスタイルにリライトするのが得意です。
一方、 Instruct-Pix2Pix は「元画像・編集後画像・自然言語指示」のペアで追加学習されており、instruction を受けて 必要な部分だけを改変 するよう振る舞います。
3-3-4 ControlNet (Canny) ─ 線画を固定してアニメ調に着彩
続いて3番目の画風変換です。
線画に色を塗ってみましょう。
とはいえ最初の画像は普通のカラー画像のトラさんです。
そこで、
- エッジ抽出により線画を生成し、
- 続いて色を塗る
という二段階の操作を行います。
# 1. ControlNet 重み(Canny)ロード
controlnet = ControlNetModel.from_pretrained(
"lllyasviel/sd-controlnet-canny",
torch_dtype=dtype,
)
pipe_canny = StableDiffusionControlNetPipeline.from_pretrained(
"runwayml/stable-diffusion-v1-5",
controlnet=controlnet,
torch_dtype=dtype,
).to(device)
pipe_canny.enable_xformers_memory_efficient_attention()
# 2. Canny エッジを抽出
import cv2, numpy as np
np_img = np.array(init_image)
edges = cv2.Canny(np_img, 100, 200)
edge_img = Image.fromarray(edges).convert("RGB").resize((512, 512))
display(edge_img)
# 3. 生成
prompt = "A vibrant anime style illustration"
#躍動感あふれるアニメ風イラスト
image_cn = pipe_canny(
prompt = prompt,
image = edge_img,
num_inference_steps = 30,
guidance_scale = 9.0,
controlnet_conditioning_scale = 1.0,
).images[0]
display(image_cn)
線画
おおって、なんか線画ってかっこよさげですね。
それなのに...
着色
色を塗ると、なんか幻滅。でも、色が塗れたのでよしとしましょう。
最初なら、こんなもんです。
こちらもプロンプトを変更して、どう変わるかを試してみてくださいね。
これで Img2Img → Pix2Pix → ControlNet の 3 手法を一通り体験できました。
🔚 第7週のまとめ
今週は、画像生成(text-to-image)と画像変換(image-to-image / style-transfer) を実習しました。
特に、image-to-image では3手法にチャレンジしました。
- Img2Img
- Instruct-Pix2Pix
- ControlNet
✅ 今週のポイント
- CNN とは何か、畳み込み層が “空間的文脈” を捉える原理と、RNN の “時間的文脈” との対比を学びました。
-
Stable Diffusion v1-5 で 512×512px の画像を生成し、
prompt / guidance_scale / seed
の3パラメータ調整を体験しました。 -
Img2Img で
strength
を変えながら「画風(水彩画)変換」の自由度を検証しました。 - Instruct-Pix2Pix で “Make it a night scene with a starry sky” のような自然言語指示により、局所編集を実行しました。
- ControlNet (Canny) で線画に色を塗ってみました。
🔭 次回予告:「第8週 画像の特徴抽出」
次回は 「画像の特徴抽出 ― 画像からキーワードや特徴ベクトルを得る」 をテーマに進めます
- Vision Transformer / CLIP などの 事前学習モデルを転用し、
- 画像を入力すると 埋め込みベクトル(feature vector) と 自動キーワード(タグ) を取得。
- 得られた特徴を使って 画像検索・クラスタリング・類似度計算 を体験します。
- さらに、今週生成した画像をベンチマークに用い、
「生成画像は検索システムでどのように表現されるか」 を実験的に確認します。
「生成→特徴抽出→検索」というワークフローは、
実サービスの“画像検索エンジン”や“レコメンドシステム”にも直結する重要ステップです。
どうぞお楽しみに!