はじめに
Stable Diffusionで画像を生成する際に時々18禁など不適切な画像が生成されますね。これは一般に「NSFW」(Not safe for workの略)と呼ばれています。
diffusersで生成する場合は自動的に(勝手に!)NSFW検出が行われて真っ暗な画像になって困ることもあります。
逆にComfyUIを使う場合は普段そういう処理が一切行われないので、突然画像が真っ暗になる心配はないのですが、NSFWが出てきて不愉快になることもありますね。
これが自分で使う時ならまだしも、ComfyUIを核心としてウェブアプリに実装して人に使わせたら大変な場合もあるでしょう。
こういう時NSFWみたいに検出が必要となる場合が多少あるので、今回はComfyUIにおいてNSFWを検出するノードの実装をする方法を紹介したいと思います。
使用可能パッケージ
実は検索してみたらComfyUIにおいてNSFWの処理をするためのパッケージが沢山あるようです。ここで一部表示します。
名前 | githubリンク |
ComfyUI-NSFW-Detection | https://github.com/trumanwong/ComfyUI-NSFW-Detection |
ComfyUI YetAnotherSafetyChecker | https://github.com/BetaDoggo/ComfyUI-YetAnotherSafetyChecker |
ComfyUI-safety-checker | https://github.com/42lux/ComfyUI-safety-checker |
ComfyUI Nodes for External Tooling | https://github.com/Acly/comfyui-tooling-nodes |
どれも似ているものですが、その中で今回「ComfyUI-NSFW-Detection」を紹介したいです。
他のパッケージではNSFWを検出すると真っ暗な画像に入れ替えられますが、ComfyUI-NSFW-Detectionでは入れ替える画像を自分で準備することができるので便利です。
ComfyUI-NSFW-Detectionの使い方
まずはインストールです。簡単にComfyUI Managerでインストールできます。似ている名前のパッケージがあるので間違えないように注意です。
インストールが終わった後、次は実際に使ってみましょう。
ComfyUI-NSFW-Detectionを組み込んだ簡単なtxt2imgのワークフローはこんな感じとなります。
このワークフローでは私が以前の記事で紹介した自動翻訳DeepTranslatorCLIPTextEncodeNode
ノードとjpgで保存できるSaveImagePlus
ノードも含まれますが、勿論これを必要でない場合はデフォルトの翻訳無しのCLIPTextEncode
とpng保存のSaveImage
を使っても構いません。
ここで一番重要なところはNSFWDetection
ノードです。
score
はどれくらい厳しく判断すると決める閾値です。0の場合はいつも通れることになりますが、1なら全部通れなくなります。普段は0.5くらいでいいですが、厳しくしたい場合は1に近い値にしてもいいです。
入力は2つ
-
image
: NSFWを審査したい画像 -
alternative_image
: NSFWだと判断された場合代わりに使う画像
代わりの画像はどの画像でもいいのですが、ここではモザイク処理が行われた画像を使うことにします。作り方は簡単です。組み込みのImageScale
ノードで16×16ピクセルに縮小させて、nearest-exact
方法でもう一度拡大するのです。
こうすることで生成する時にもしNSFWが検出されたら自動的にモザイク処理されるが、普通に害のない画像だったらそのまま出力されます。
文字で伝える
ウェブアプリに使う時などただモザイク処理だけではどうしてこうなるかわからない場合もあるでしょう。ちょっと説明文を入れられたらいいと思って、おまけに文字を入れる方法も紹介しておきたいです。
そのためにText Overlayというノードを使うと便利です。これもまずインストールする必要があります。
こんな感じでImageScale
とNSFWDetection
ノードの間に挿入します。
尚、日本語などを表示するためにまず使うフォントをComfyUIのcustom_nodes/ComfyUI-TextOverlay/fonts
フォルダに入れる必要があります。ここではmeiryoub.ttc
を使っています。
ワークフロー
最後に以上の画像のワークフローです。これをコピーして.jsonに保存してComfyUIの中にロードして使ってみてください。
{
"1": {
"inputs": {
"ckpt_name": "meinamix_meinaV11.safetensors"
},
"class_type": "CheckpointLoaderSimple"
},
"2": {
"inputs": {
"width": 512,
"height": 512,
"batch_size": 1
},
"class_type": "EmptyLatentImage"
},
"3": {
"inputs": {
"from_translate": "japanese",
"to_translate": "english",
"add_proxies": false,
"proxies": "",
"auth_data": "",
"service": "GoogleTranslator [free]",
"text": "裸の猫耳少女",
"Show proxy": "proxy_hide",
"Show authorization": "authorization_hide",
"clip": ["1", 1]
},
"class_type": "DeepTranslatorCLIPTextEncodeNode"
},
"4": {
"inputs": {
"from_translate": "japanese",
"to_translate": "english",
"add_proxies": false,
"proxies": "",
"auth_data": "",
"service": "GoogleTranslator [free]",
"text": "低品質",
"Show proxy": "proxy_hide",
"Show authorization": "authorization_hide",
"clip": ["1", 1]
},
"class_type": "DeepTranslatorCLIPTextEncodeNode"
},
"5": {
"inputs": {
"seed": 696759150096688,
"steps": 20,
"cfg": 7,
"sampler_name": "ddim",
"scheduler": "ddim_uniform",
"denoise": 1,
"model": ["1", 0],
"positive": ["3", 0],
"negative": ["4", 0],
"latent_image": ["2", 0]
},
"class_type": "KSampler"
},
"6": {
"inputs": {
"samples": ["5", 0],
"vae": ["1", 2]
},
"class_type": "VAEDecode"
},
"7": {
"inputs": {
"upscale_method": "nearest-exact",
"width": 16,
"height": 16,
"crop": "disabled",
"image": ["6", 0]
},
"class_type": "ImageScale"
},
"8": {
"inputs": {
"upscale_method": "nearest-exact",
"width": 512,
"height": 512,
"crop": "disabled",
"image": ["7", 0]
},
"class_type": "ImageScale"
},
"9": {
"inputs": {
"text": "不適切な内容が\n検出されたため\nモザイク処理\nさせていただきます",
"font_size": 50,
"font": "meiryoub.ttc",
"fill_color_hex": "#FFFFFF",
"stroke_color_hex": "#FF0000",
"stroke_thickness": 0.2,
"padding": 16,
"horizontal_alignment": "center",
"vertical_alignment": "middle",
"x_shift": 0,
"y_shift": 0,
"image": ["8", 0]
},
"class_type": "Text Overlay"
},
"10": {
"inputs": {
"score": 0.5,
"image": ["6", 0],
"alternative_image": ["9", 0]
},
"class_type": "NSFWDetection"
},
"11": {
"inputs": {
"filename_prefix": "txt2img",
"file_type": "JPEG",
"remove_metadata": false,
"images": ["10", 0]
},
"class_type": "SaveImagePlus"
}
}