Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 971f5c5

Browse files
lsteinmauwii
andauthored
Configure the NSFW checker at install time with default on (#1624)
* configure the NSFW checker at install time with default on 1. Changes the --safety_checker argument to --nsfw_checker and --no-nsfw_checker. The original argument is recognized for backward compatibility. 2. The configure script asks users whether to enable the checker (default yes). Also offers users ability to select default sampler and number of generation steps. 3.Enables the pasting of the caution icon on blurred images when InvokeAI is installed into the package directory. 4. Adds documentation for the NSFW checker, including caveats about accuracy, memory requirements, and intermediate image dispaly. * use better fitting icon * NSFW defaults false for testing * set default back to nsfw active Co-authored-by: Matthias Wild <[email protected]> Co-authored-by: mauwii <[email protected]>
1 parent 2213339 commit 971f5c5

File tree

5 files changed

+165
-30
lines changed

5 files changed

+165
-30
lines changed

docs/features/NSFW.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
---
2+
title: The NSFW Checker
3+
---
4+
5+
# :material-image-off: NSFW Checker
6+
7+
## The NSFW ("Safety") Checker
8+
9+
The Stable Diffusion image generation models will produce sexual
10+
imagery if deliberately prompted, and will occasionally produce such
11+
images when this is not intended. Such images are colloquially known
12+
as "Not Safe for Work" (NSFW). This behavior is due to the nature of
13+
the training set that Stable Diffusion was trained on, which culled
14+
millions of "aesthetic" images from the Internet.
15+
16+
You may not wish to be exposed to these images, and in some
17+
jurisdictions it may be illegal to publicly distribute such imagery,
18+
including mounting a publicly-available server that provides
19+
unfiltered images to the public. Furthermore, the [Stable Diffusion
20+
weights
21+
License](https://github.com/invoke-ai/InvokeAI/blob/main/LICENSE-ModelWeights.txt)
22+
forbids the model from being used to "exploit any of the
23+
vulnerabilities of a specific group of persons."
24+
25+
For these reasons Stable Diffusion offers a "safety checker," a
26+
machine learning model trained to recognize potentially disturbing
27+
imagery. When a potentially NSFW image is detected, the checker will
28+
blur the image and paste a warning icon on top. The checker can be
29+
turned on and off on the command line using `--nsfw_checker` and
30+
`--no-nsfw_checker`.
31+
32+
At installation time, InvokeAI will ask whether the checker should be
33+
activated by default (neither argument given on the command line). The
34+
response is stored in the InvokeAI initialization file (usually
35+
`.invokeai` in your home directory). You can change the default at any
36+
time by opening this file in a text editor and commenting or
37+
uncommenting the line `--nsfw_checker`.
38+
39+
## Caveats
40+
41+
There are a number of caveats that you need to be aware of.
42+
43+
### Accuracy
44+
45+
The checker is [not perfect](https://arxiv.org/abs/2210.04610).It will
46+
occasionally flag innocuous images (false positives), and will
47+
frequently miss violent and gory imagery (false negatives). It rarely
48+
fails to flag sexual imagery, but this has been known to happen. For
49+
these reasons, the InvokeAI team prefers to refer to the software as a
50+
"NSFW Checker" rather than "safety checker."
51+
52+
### Memory Usage and Performance
53+
54+
The NSFW checker consumes an additional 1.2G of GPU VRAM on top of the
55+
3.4G of VRAM used by Stable Diffusion v1.5 (this is with
56+
half-precision arithmetic). This means that the checker will not run
57+
successfully on GPU cards with less than 6GB VRAM, and will reduce the
58+
size of the images that you can produce.
59+
60+
The checker also introduces a slight performance penalty. Images will
61+
take ~1 second longer to generate when the checker is
62+
activated. Generally this is not noticeable.
63+
64+
### Intermediate Images in the Web UI
65+
66+
The checker only operates on the final image produced by the Stable
67+
Diffusion algorithm. If you are using the Web UI and have enabled the
68+
display of intermediate images, you will briefly be exposed to a
69+
low-resolution (mosaicized) version of the final image before it is
70+
flagged by the checker and replaced by a fully blurred version. You
71+
are encouraged to turn **off** intermediate image rendering when you
72+
are using the checker. Future versions of InvokeAI will apply
73+
additional blurring to intermediate images when the checker is active.
74+
75+
### Watermarking
76+
77+
InvokeAI does not apply any sort of watermark to images it
78+
generates. However, it does write metadata into the PNG data area,
79+
including the prompt used to generate the image and relevant parameter
80+
settings. These fields can be examined using the `sd-metadata.py`
81+
script that comes with the InvokeAI package.
82+
83+
Note that several other Stable Diffusion distributions offer
84+
wavelet-based "invisible" watermarking. We have experimented with the
85+
library used to generate these watermarks and have reached the
86+
conclusion that while the watermarking library may be adding
87+
watermarks to PNG images, the currently available version is unable to
88+
retrieve them successfully. If and when a functioning version of the
89+
library becomes available, we will offer this feature as well.

ldm/invoke/args.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ def _create_arg_parser(self):
468468
action=argparse.BooleanOptionalAction,
469469
dest='safety_checker',
470470
default=False,
471-
help='Check for and blur potentially NSFW images.',
471+
help='Check for and blur potentially NSFW images. Use --no-nsfw_checker to disable.',
472472
)
473473
model_group.add_argument(
474474
'--patchmatch',

ldm/invoke/generator/base.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import numpy as np
77
import random
88
import os
9+
import os.path as osp
910
import traceback
1011
from tqdm import tqdm, trange
1112
from PIL import Image, ImageFilter, ImageChops
@@ -32,6 +33,7 @@ def __init__(self, model, precision):
3233
self.with_variations = []
3334
self.use_mps_noise = False
3435
self.free_gpu_mem = None
36+
self.caution_img = None
3537

3638
# this is going to be overridden in img2img.py, txt2img.py and inpaint.py
3739
def get_make_image(self,prompt,**kwargs):
@@ -290,13 +292,29 @@ def safety_check(self,image:Image.Image):
290292
def blur(self,input):
291293
blurry = input.filter(filter=ImageFilter.GaussianBlur(radius=32))
292294
try:
293-
caution = Image.open(CAUTION_IMG)
294-
caution = caution.resize((caution.width // 2, caution.height //2))
295-
blurry.paste(caution,(0,0),caution)
295+
caution = self.get_caution_img()
296+
if caution:
297+
blurry.paste(caution,(0,0),caution)
296298
except FileNotFoundError:
297299
pass
298300
return blurry
299301

302+
def get_caution_img(self):
303+
if self.caution_img:
304+
return self.caution_img
305+
# Find the caution image. If we are installed in the package directory it will
306+
# be six levels up. If we are in the repo directory it will be three levels up.
307+
for dots in ('../../..','../../../../../..'):
308+
caution_path = osp.join(osp.dirname(__file__),dots,CAUTION_IMG)
309+
if osp.exists(caution_path):
310+
path = caution_path
311+
break
312+
if not path:
313+
return
314+
caution = Image.open(path)
315+
self.caution_img = caution.resize((caution.width // 2, caution.height //2))
316+
return self.caution_img
317+
300318
# this is a handy routine for debugging use. Given a generated sample,
301319
# convert it into a PNG image and store it at the indicated path
302320
def save_sample(self, sample, filepath):

scripts/configure_invokeai.py

Lines changed: 52 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -70,10 +70,10 @@ def postscript():
7070
Command-line version:
7171
python scripts/invoke.py
7272
73-
Remember to activate that 'invokeai' environment before running invoke.py.
74-
75-
Or, if you used one of the automated installers, execute "invoke.sh" (Linux/Mac)
76-
or "invoke.bat" (Windows) to start the script.
73+
If you installed manually, remember to activate the 'invokeai'
74+
environment before running invoke.py. If you installed using the
75+
automated installation script, execute "invoke.sh" (Linux/Mac) or
76+
"invoke.bat" (Windows) to start InvokeAI.
7777
7878
Have fun!
7979
'''
@@ -243,10 +243,10 @@ def download_weight_datasets(models:dict, access_token:str):
243243
for mod in models.keys():
244244
repo_id = Datasets[mod]['repo_id']
245245
filename = Datasets[mod]['file']
246-
print(os.path.join(Globals.root,Model_dir,Weights_dir), file=sys.stderr)
246+
dest = os.path.join(Globals.root,Model_dir,Weights_dir)
247247
success = hf_download_with_resume(
248248
repo_id=repo_id,
249-
model_dir=os.path.join(Globals.root,Model_dir,Weights_dir),
249+
model_dir=dest,
250250
model_name=filename,
251251
access_token=access_token
252252
)
@@ -494,12 +494,12 @@ def download_clipseg():
494494

495495
#-------------------------------------
496496
def download_safety_checker():
497-
print('Installing safety model for NSFW content detection...',file=sys.stderr)
497+
print('Installing model for NSFW content detection...',file=sys.stderr)
498498
try:
499499
from diffusers.pipelines.stable_diffusion.safety_checker import StableDiffusionSafetyChecker
500500
from transformers import AutoFeatureExtractor
501501
except ModuleNotFoundError:
502-
print('Error installing safety checker model:')
502+
print('Error installing NSFW checker model:')
503503
print(traceback.format_exc())
504504
return
505505
safety_model_id = "CompVis/stable-diffusion-safety-checker"
@@ -520,6 +520,7 @@ def download_weights(opt:dict):
520520
return
521521
else:
522522
print('** Cannot download models because no Hugging Face access token could be found. Please re-run without --yes')
523+
return
523524
else:
524525
choice = user_wants_to_download_weights()
525526

@@ -584,7 +585,7 @@ def select_outputs(root:str,yes_to_all:bool=False):
584585

585586
#-------------------------------------
586587
def initialize_rootdir(root:str,yes_to_all:bool=False):
587-
assert os.path.exists('./configs'),'Run this script from within the top level of the InvokeAI source code directory, "InvokeAI"'
588+
assert os.path.exists('./configs'),'Run this script from within the InvokeAI source code directory, "InvokeAI" or the runtime directory "invokeai".'
588589

589590
print(f'** INITIALIZING INVOKEAI RUNTIME DIRECTORY **')
590591
root_selected = False
@@ -603,19 +604,50 @@ def initialize_rootdir(root:str,yes_to_all:bool=False):
603604
print(f'\nYou may change the chosen directories at any time by editing the --root and --outdir options in "{Globals.initfile}",')
604605
print(f'You may also change the runtime directory by setting the environment variable INVOKEAI_ROOT.\n')
605606

607+
enable_safety_checker = True
608+
default_sampler = 'k_heun'
609+
default_steps = '20' # deliberately a string - see test below
610+
611+
sampler_choices =['ddim','k_dpm_2_a','k_dpm_2','k_euler_a','k_euler','k_heun','k_lms','plms']
612+
613+
if not yes_to_all:
614+
print('The NSFW (not safe for work) checker blurs out images that potentially contain sexual imagery.')
615+
print('It can be selectively enabled at run time with --nsfw_checker, and disabled with --no-nsfw_checker.')
616+
print('The following option will set whether the checker is enabled by default. Like other options, you can')
617+
print(f'change this setting later by editing the file {Globals.initfile}.')
618+
enable_safety_checker = yes_or_no('Enable the NSFW checker by default?',enable_safety_checker)
619+
620+
print('\nThe next choice selects the sampler to use by default. Samplers have different speed/performance')
621+
print('tradeoffs. If you are not sure what to select, accept the default.')
622+
sampler = None
623+
while sampler not in sampler_choices:
624+
sampler = input(f'Default sampler to use? ({", ".join(sampler_choices)}) [{default_sampler}]:') or default_sampler
625+
626+
print('\nThe number of denoising steps affects both the speed and quality of the images generated.')
627+
print('Higher steps often (but not always) increases the quality of the image, but increases image')
628+
print('generation time. This can be changed at run time. Accept the default if you are unsure.')
629+
steps = ''
630+
while not steps.isnumeric():
631+
steps = input(f'Default number of steps to use during generation? [{default_steps}]:') or default_steps
632+
else:
633+
sampler = default_sampler
634+
steps = default_steps
635+
636+
safety_checker = '--nsfw_checker' if enable_safety_checker else '--no-nsfw_checker'
637+
606638
for name in ('models','configs','embeddings'):
607639
os.makedirs(os.path.join(root,name), exist_ok=True)
608640
for src in (['configs']):
609641
dest = os.path.join(root,src)
610642
if not os.path.samefile(src,dest):
611643
shutil.copytree(src,dest,dirs_exist_ok=True)
612-
os.makedirs(outputs, exist_ok=True)
644+
os.makedirs(outputs, exist_ok=True)
613645

614646
init_file = os.path.expanduser(Globals.initfile)
615-
if not os.path.exists(init_file):
616-
print(f'Creating the initialization file at "{init_file}".\n')
617-
with open(init_file,'w') as f:
618-
f.write(f'''# InvokeAI initialization file
647+
648+
print(f'Creating the initialization file at "{init_file}".\n')
649+
with open(init_file,'w') as f:
650+
f.write(f'''# InvokeAI initialization file
619651
# This is the InvokeAI initialization file, which contains command-line default values.
620652
# Feel free to edit. If anything goes wrong, you can re-initialize this file by deleting
621653
# or renaming it and then running configure_invokeai.py again.
@@ -626,23 +658,18 @@ def initialize_rootdir(root:str,yes_to_all:bool=False):
626658
# the --outdir option controls the default location of image files.
627659
--outdir="{outputs}"
628660
661+
# generation arguments
662+
{safety_checker}
663+
--sampler={sampler}
664+
--steps={steps}
665+
629666
# You may place other frequently-used startup commands here, one or more per line.
630667
# Examples:
631668
# --web --host=0.0.0.0
632669
# --steps=20
633670
# -Ak_euler_a -C10.0
634671
#
635-
'''
636-
)
637-
else:
638-
print(f'Updating the initialization file at "{init_file}".\n')
639-
with open(init_file,'r') as infile, open(f'{init_file}.tmp','w') as outfile:
640-
for line in infile.readlines():
641-
if not line.startswith('--root') and not line.startswith('--outdir'):
642-
outfile.write(line)
643-
outfile.write(f'--root="{root}"\n')
644-
outfile.write(f'--outdir="{outputs}"\n')
645-
os.replace(f'{init_file}.tmp',init_file)
672+
''')
646673

647674
#-------------------------------------
648675
class ProgressBar():

setup.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ def list_files(directory):
8181
'scripts/preload_models.py', 'scripts/images2prompt.py','scripts/merge_embeddings.py'
8282
],
8383
data_files=[('frontend/dist',list_files('frontend/dist')),
84-
('frontend/dist/assets',list_files('frontend/dist/assets'))
84+
('frontend/dist/assets',list_files('frontend/dist/assets')),
85+
('assets',['assets/caution.png']),
8586
],
8687
)

0 commit comments

Comments
 (0)