From 7304deaffb89b8314d422a7d9eace1be6f8142e2 Mon Sep 17 00:00:00 2001 From: Vladimir Mandic Date: Tue, 5 Mar 2024 17:25:16 -0500 Subject: [PATCH] add user watermarks option --- CHANGELOG.md | 5 +++ html/logo-wm.png | Bin 0 -> 14328 bytes modules/images.py | 101 ++++++++++++++++++++++++++++------------------ modules/shared.py | 8 +++- 4 files changed, 73 insertions(+), 41 deletions(-) create mode 100644 html/logo-wm.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c2d8e02c..4c1f6f521 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -60,6 +60,11 @@ - *image viewer* now displays image metadata - *themes* improve on-the-fly switching - *log monitor* flag server warnings/errors and overall improve display +- **Watermarking** + - SD.Next disables all known watermarks in models, but does allow user to set custom watermark + - See *settings -> image options -> watermarking* + - Invisible watermark: using steganogephy + - Image watermark: overlaid on top of image - **Fixes** - improve *model cpu offload* compatibility - improve *model sequential offload* compatibility diff --git a/html/logo-wm.png b/html/logo-wm.png new file mode 100644 index 0000000000000000000000000000000000000000..1bfdbf53ae3f953bf1c9db47ba54300703788d74 GIT binary patch literal 14328 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 mvm5!8p3+l#O8 0: fn = os.path.join(paths.data_path, shared.opts.save_log_fn) if not fn.endswith('.json'): @@ -839,24 +831,55 @@ def flatten(img, bgcolor): def set_watermark(image, watermark): - from imwatermark import WatermarkEncoder - wm_type = 'bytes' - wm_method = 'dwtDctSvd' - wm_length = 32 - length = wm_length // 8 - info = image.info - data = np.asarray(image) - encoder = WatermarkEncoder() - text = f"{watermark:<{length}}"[:length] - bytearr = text.encode(encoding='ascii', errors='ignore') - try: - encoder.set_watermark(wm_type, bytearr) - encoded = encoder.encode(data, wm_method) - image = Image.fromarray(encoded) - image.info = info - shared.log.debug(f'Set watermark: {watermark} method={wm_method} bits={wm_length}') - except Exception as e: - shared.log.warning(f'Set watermark error: {watermark} method={wm_method} bits={wm_length} {e}') + if shared.opts.image_watermark_position != 'none': # visible watermark + wm_image = None + try: + wm_image = Image.open(shared.opts.image_watermark_image) + except Exception as e: + shared.log.warning(f'Set image watermark: fn="{shared.opts.image_watermark_image}" {e}') + if wm_image is not None: + if shared.opts.image_watermark_position == 'top/left': + position = (0, 0) + elif shared.opts.image_watermark_position == 'top/right': + position = (image.width - wm_image.width, 0) + elif shared.opts.image_watermark_position == 'bottom/left': + position = (0, image.height - wm_image.height) + elif shared.opts.image_watermark_position == 'bottom/right': + position = (image.width - wm_image.width, image.height - wm_image.height) + elif shared.opts.image_watermark_position == 'center': + position = ((image.width - wm_image.width) // 2, (image.height - wm_image.height) // 2) + else: + position = (random.randint(0, image.width - wm_image.width), random.randint(0, image.height - wm_image.height)) + try: + for x in range(wm_image.width): + for y in range(wm_image.height): + r, g, b, _a = wm_image.getpixel((x, y)) + if not (r == 0 and g == 0 and b == 0): + image.putpixel((x+position[0], y+position[1]), (r, g, b)) + shared.log.debug(f'Set image watermark: fn="{shared.opts.image_watermark_image}" image={wm_image} position={position}') + except Exception as e: + shared.log.warning(f'Set image watermark: image={wm_image} {e}') + + if shared.opts.image_watermark_enabled: # invisible watermark + from imwatermark import WatermarkEncoder + wm_type = 'bytes' + wm_method = 'dwtDctSvd' + wm_length = 32 + length = wm_length // 8 + info = image.info + data = np.asarray(image) + encoder = WatermarkEncoder() + text = f"{watermark:<{length}}"[:length] + bytearr = text.encode(encoding='ascii', errors='ignore') + try: + encoder.set_watermark(wm_type, bytearr) + encoded = encoder.encode(data, wm_method) + image = Image.fromarray(encoded) + image.info = info + shared.log.debug(f'Set invisible watermark: {watermark} method={wm_method} bits={wm_length}') + except Exception as e: + shared.log.warning(f'Set invisible watermark error: {watermark} method={wm_method} bits={wm_length} {e}') + return image diff --git a/modules/shared.py b/modules/shared.py index fd85dcc38..d7afc75fd 100644 --- a/modules/shared.py +++ b/modules/shared.py @@ -514,8 +514,6 @@ options_templates.update(options_section(('saving-images', "Image Options"), { "image_metadata": OptionInfo(True, "Include metadata"), "save_txt": OptionInfo(False, "Create info file per image"), "save_log_fn": OptionInfo("", "Update JSON log file per image", component_args=hide_dirs), - "image_watermark_enabled": OptionInfo(False, "Include watermark"), - "image_watermark": OptionInfo('', "Watermark string"), "image_sep_grid": OptionInfo("

Grid Options

", "", gr.HTML), "grid_save": OptionInfo(True, "Save all generated image grids"), "grid_format": OptionInfo('jpg', 'File format', gr.Dropdown, {"choices": ["jpg", "png", "webp", "tiff", "jp2"]}), @@ -532,6 +530,12 @@ options_templates.update(options_section(('saving-images', "Image Options"), { "save_images_before_color_correction": OptionInfo(False, "Save image before color correction"), "save_mask": OptionInfo(False, "Save inpainting mask"), "save_mask_composite": OptionInfo(False, "Save inpainting masked composite"), + + "image_sep_watermark": OptionInfo("

Watermarking

", "", gr.HTML), + "image_watermark_enabled": OptionInfo(False, "Include invisible watermark"), + "image_watermark": OptionInfo('', "Invisible watermark string"), + "image_watermark_position": OptionInfo('none', 'Image watermark position', gr.Dropdown, {"choices": ["none", "top/left", "top/right", "bottom/left", "bottom/right", "center", "random"]}), + "image_watermark_image": OptionInfo('', "Image watermark file"), })) options_templates.update(options_section(('saving-paths', "Image Naming & Paths"), {