/sd-webui-nudenet-nsfw-censor

NSFW regions censoring using NudeNet for Stable Diffusion Web UI

Primary LanguagePythonGNU Affero General Public License v3.0AGPL-3.0

Stable Diffusion Web UI - NudeNet NSFW Censor

This is a Stable Diffusion web UI extension that uses NudeNet to detect NSFW regions in an image, allowing for partial censoring of the image, useful when sharing images NSFW images on social media.

Changelog

  • 20240109: fix issue where the detected region is not scaled correctly
    • this fix cause old horizontal and vertical multiplier to be incorrect, as such if you are upgrading from a previous version the multiplier will be reset new default of 1.0

Features

  • Image generation and Live preview
  • Auto and manual censor Extras tab tab
  • Post-processing via Extras tab
  • Partial censoring of the image
  • Customizable censoring categories
  • Multiple censoring methods
  • API

Filter types

  • Gaussian Blur: Blur the image control with Blur strength
  • Variable blur: Applies varying strength blur strength based of the grayscale value of the mask, making the censor border less eye jarring. Warning CPU intensive.
  • Pixelate: Pixelated Mosaic effect.
  • Fill color: Fill a specified color
  • No censor: No filter is applied, but the NudeNet detection mask is still generated, use when you only want the mask but don’t want to censor the image.
  • Disable: Disable this extension for this workflow

Settings

Settings can be found under Settings > NudeNet NSFW Censor

  • Enable Region censor for image generation: enables/disables NudeNet censor for image generation
  • Save a copy of the image before applying censor: Save a copy of the image before applying censor
  • Save censor mask: Save the mask generated by NudeNet
  • Generation censor filter: censor filter used for the final generated image
  • Live preview censor filter: censor filter used during Live preview
  • Extras censor filter: the default censor filter used for Extras tab
  • Mask shape: the mask shape applied on the detected regions
  • Rectangle round radius: controls the corner rounding radius when Mask shape is Rounded rectangle
    • different ranges of value have different meaning
    • [0, 1]: round ratio, 0.5 will round 50%, round radius will scale with the mask size
    • (0, inf): round unit, round radius will scale with the image size
    • (-inf, 0): absolute pixel, round radius will absolute value in pixels
    • note: due to a bug with Pillow 9.5.0 Rounded rectangle can fail to draw the mask

Filter parameters

  • Blur strength: Controls the blur strength for Gaussian Blur and Variable blur.

  • Mask blend strength: applies to Gaussian Blur, Pixelate, Fill color
    apply blur to the detected mask, softening the censor border, if the value is too high the censor area might be transparent.

  • Mask blend strength - Variable blur: controls the size of the censor transition border Apply blur to the detected mask, grayscale value is used to control the strength blur.

  • Blur strength curve - Variable blur: Controls the blur strength gradient.

    • Value range [0, 6]
    • 0: No blur
    • 3: linear strength
    • 4: linear with mask grayscale value
    • 6: Entire image Max blur
  • Pixelation factor: Controls pixelation strength

  • Fill color: Fill the detected region with this color

Detection configuration

  • Categories to be censored: NudeNet has 18 categories of body parts
    Select which you wish to censor, each type has three values that you can adjust threshold horizontal multiplier and vertical multiplier

  • threshold: confidence threshold of when the detected region is used
    typically a threshold of 0.25~0.5 should be a good range to reliably detect a category,
    setting this to a high value means that it will only trigger censoring when it's more confident, setting it to 1 is the same as disabling a category, on the other hand setting a low value would make censoring easier to trigger but also more chance of erroneously detecting regions

  • horizontal multiplier and vertical multiplier: to compensate NudeNet's detection often is off-centered or does not cover the entire body part, the origin NudeNet detected region can be enlarged by horizontal multiplier and vertical multiplier expanding the masked area covering a larger region.

List of NudeNet categories
  • Anus covered
  • Anus exposed
  • Armpits covered
  • Armpits exposed
  • Belly covered
  • Belly exposed
  • Buttocks covered
  • Buttocks exposed
  • Face female
  • Face male
  • Feet covered
  • Feet exposed
  • Female breast covered
  • Female breast exposed
  • Female genitalia covered
  • Female genitalia exposed
  • Male breast exposed
  • Male genitalia exposed
  • Non-Maximum Suppression threshold: Discard overlapping detected regions,

    • set to 1 disables NMS note that NMS is performed prior to the adjustment of the detected regions
  • Print detection info in terminal: Print info about detected mask regions, used for tweaking values, does not work when mask shape is set to Entire image

API example

/nudenet/censor

  • all parameters are optional, if not specified it will use the parameter configured in the settings, if they configuration already configured in the settings then the payload can be just the input_image alone
  • enable_nudenet is enabled by default
  • if no censoring is apply to the image then image will return None
from PIL import Image
import requests
import base64
import io

image_path = r"input_image.png"
with open(image_path, 'rb') as image_file:
    base64_image = base64.b64encode(image_file.read()).decode('utf-8')

image_path_mask = r"input_custom_mask.png"
with open(image_path_mask, 'rb') as image_file:
    base64_image_mask = base64.b64encode(image_file.read()).decode('utf-8')

payload = {
    "input_image": base64_image,  # the image you wish to censor
    "input_mask": base64_image_mask,  # optional, if used will be combined with the NudeNet mask
    "enable_nudenet": True,  # enable NudeNet detect and generate the censor mask
    "output_mask": True,  # return the generated mask
    "filter_type": "Variable blur",  # the type of filter that will be used for censoring the image
    "blur_radius": 10,  # control the strength of gaussian blur when using "Variable blur" or "Gaussian blur"
    "blur_strength_curve": 3,  # control the blur strength gradient for "Variable blur"
    "pixelation_factor": 5,  # the pixelation factor when using "Pixelate"
    "fill_color": "#000000",  # the fill color when using "Fill color"
    "mask_shape": "Ellipse",  # the shape of the masked NudeNet regions
    "mask_blend_radius": 10,  # the blurring of the combined NudeNet and input_mask before censoring is applied to the input_image
    "rectangle_round_radius": 0,  # controls corner the rounding radius when mask_shape is "Rounded rectangle"
    "nms_threshold": 0.5,  # Non-Maximum Suppression threshold of the NudeNet detected regions
    # the following three list of 18 float configures which category is censored
    # the confidence threshold of each category for it to be censored
    # and the amount that each detected regions will be expanded in horizontal and vertical direction
    # each element in the list correspond to one NudeNet label, the order can be found below in Default category configuration or on webui's api "/docs" page
    # confidence thresholds float [0, 1] when set to 1 disables this category
    # this example below censors [Female_breast_exposed, Female_genitalia_exposed, Anus_exposed, Male_genitalia_exposed]
    "thresholds":           [1, 1, 1, 0.25, 0.25, 1, 0.25, 1.0, 1.0, 1, 1, 1, 1, 1, 0.25, 1.0, 1.0, 1],
    # expand horizontal / vertical, float [0, inf]
    "expand_horizontal":    [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    "expand_vertical":      [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
}

response = requests.post(url=f'http://localhost:7860/nudenet/censor', json=payload)
if response.status_code == 200:
    response = response.json()
    if response['image']:
        Image.open(io.BytesIO(base64.b64decode(response['image']))).show()
    if response['mask']:
        Image.open(io.BytesIO(base64.b64decode(response['mask']))).show()

Default category configuration

nudenet_labels_dict = {
    # Category_name: [threshold, horizontal_multiplier, vertical_multiplier ]
    'Female_genitalia_covered': [0.25, 1.0, 1.0],
    'Face_female': [0.25, 1.0, 1.0],
    'Buttocks_exposed': [0.25, 1.0, 1.0],
    'Female_breast_exposed': [0.25, 1.0, 1.0],
    'Female_genitalia_exposed': [0.25, 1.0, 1.0],
    'Male_breast_exposed': [0.25, 1.0, 1.0],
    'Anus_exposed': [0.25, 1.0, 1.0],
    'Feet_exposed': [0.25, 1.0, 1.0],
    'Belly_covered': [0.25, 1.0, 1.0],
    'Feet_covered': [0.25, 1.0, 1.0],
    'Armpits_covered': [0.25, 1.0, 1.0],
    'Armpits_exposed': [0.25, 1.0, 1.0],
    'Face_male': [0.25, 1.0, 1.0],
    'Belly_exposed': [0.25, 1.0, 1.0],
    'Male_genitalia_exposed': [0.25, 1.0, 1.0],
    'Anus_covered': [0.25, 1.0, 1.0],
    'Female_breast_covered': [0.25, 1.0, 1.0],
    'Buttocks_covered': [0.25, 1.0, 1.0],
}