From d989e2479fb019c46e4b5c5ce9988cf802e7b80a Mon Sep 17 00:00:00 2001 From: sudoskys Date: Sun, 17 Mar 2024 01:53:54 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat:=20add=20apply=5Flsb=20script,?= =?UTF-8?q?=20update=20enum=20with=20new=20model,=20and=20fix=20lsb=20extr?= =?UTF-8?q?actor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add apply_lsb script for image metadata - Update enum with new model NAI_DIFFUSION_2 - Fix lsb_extractor in image metadata package --- .gitignore | 1 + playground/image_metadata/apply_lsb.py | 21 +++++ playground/image_metadata/read_image.py | 2 + playground/image_metadata/read_lsb.py | 4 +- playground/image_metadata/sample-0316-out.png | Bin 0 -> 67086 bytes pyproject.toml | 2 +- .../sdk/ai/generate_image/_enum.py | 17 ++++ .../tool/image_metadata/__init__.py | 75 ++++++++++++------ 8 files changed, 96 insertions(+), 26 deletions(-) create mode 100644 playground/image_metadata/apply_lsb.py create mode 100644 playground/image_metadata/sample-0316-out.png diff --git a/.gitignore b/.gitignore index d8c87a3..25918f9 100755 --- a/.gitignore +++ b/.gitignore @@ -175,3 +175,4 @@ cython_debug/ /playground/newtag/ /playground/oldtag/ /playground/art_assert/ +/playground/unpack/ diff --git a/playground/image_metadata/apply_lsb.py b/playground/image_metadata/apply_lsb.py new file mode 100644 index 0000000..fa4a716 --- /dev/null +++ b/playground/image_metadata/apply_lsb.py @@ -0,0 +1,21 @@ +from pathlib import Path + +from novelai_python.tool.image_metadata import ImageMetadata, ImageLsbDataExtractor +from PIL import Image + +image = Path(__file__).parent.joinpath("sample-0316.png") +write_out = Path(__file__).parent.joinpath("sample-0316-out.png") +try: + meta = ImageMetadata.load_image(image) + with Image.open(image) as img: + new_io = meta.apply_to_image(img, inject_lsb=True) + with open(write_out, 'wb') as f: + f.write(new_io.getvalue()) + new_meta = ImageMetadata.load_image(write_out) + data = ImageLsbDataExtractor().extract_data(write_out) +except ValueError: + raise LookupError("Cant find a MetaData") +print(data) +print(new_meta) +print(new_meta.used_model) +print(ImageMetadata.verify_image_is_novelai(Image.open(write_out))) diff --git a/playground/image_metadata/read_image.py b/playground/image_metadata/read_image.py index ae9f74e..946e7a0 100644 --- a/playground/image_metadata/read_image.py +++ b/playground/image_metadata/read_image.py @@ -10,6 +10,8 @@ title = img.info.get("Title", None) prompt = img.info.get("Description", None) comment = img.info.get("Comment", None) + print(img.info) + assert isinstance(comment, str), ValueError("Comment Empty") try: comment = json.loads(comment) diff --git a/playground/image_metadata/read_lsb.py b/playground/image_metadata/read_lsb.py index 8d0bc24..9c2b833 100644 --- a/playground/image_metadata/read_lsb.py +++ b/playground/image_metadata/read_lsb.py @@ -1,11 +1,11 @@ import os from pathlib import Path -from novelai_python.tool.image_metadata.lsb_extractor import ImageDataExtractor +from novelai_python.tool.image_metadata.lsb_extractor import ImageLsbDataExtractor image = Path(__file__).parent.joinpath("sample-0316.png") try: - data = ImageDataExtractor().extract_data(image) + data = ImageLsbDataExtractor().extract_data(image) except ValueError: raise LookupError("Cant find a MetaData") diff --git a/playground/image_metadata/sample-0316-out.png b/playground/image_metadata/sample-0316-out.png new file mode 100644 index 0000000000000000000000000000000000000000..8b366d106739981f660a23f33074e7e23cbca4af GIT binary patch literal 67086 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 Union[Model, None]: + """ + Get the model used to generate the image from the Model code + :return: Model or None + """ + return PROMOTION.get(self.Source, None) @staticmethod def reset_alpha(input_img: BytesIO) -> BytesIO: + """ + Remove LSB from the image, set the alpha channel to 254 + :param input_img: + :return: + """ image = Image.open(input_img).convert('RGBA') data = np.array(image) data[..., 3] = 254 @@ -82,23 +93,32 @@ def reset_alpha(input_img: BytesIO) -> BytesIO: new_image.save(_new_img_io, format="PNG") return _new_img_io - def write_out(self, - img_io: BytesIO, - *, - remove_lsb: bool = False - ): - if remove_lsb: - img_io = self.reset_alpha(img_io) - with Image.open(img_io) as img: - metadata = PngInfo() - for k, v in self.jsonfy.items(): - if isinstance(v, dict): - v = json.dumps(v) - metadata.add_text(k, v) - _new_img = BytesIO() - # Save original image with metadata - img.save(_new_img, format="PNG", pnginfo=metadata, optimize=False, compress_level=0) - return _new_img + def apply_to_image(self, + origin_image: Image.Image, + *, + inject_lsb: bool = True + ) -> BytesIO: + """ + Write metadata to origin_image + If you set inject_lsb to True, the image will be injected with metadata using LSB. + + **But if you set inject_lsb to False, the image will be reset to the 254 alpha channel** + :param origin_image: BytesIO + :param inject_lsb: Inject metadata using LSB + :return: BytesIO + """ + metadata = PngInfo() + for k, v in self.model_dump(mode="json").items(): + if isinstance(v, dict): + v = json.dumps(v) + metadata.add_text(k, v) + if inject_lsb: + # Inject metadata using LSB + origin_image = inject_data(origin_image, metadata) + # Save original image with metadata (and LSB if inject_lsb is True) + new_img = BytesIO() + origin_image.save(new_img, format="PNG", pnginfo=metadata, optimize=False, compress_level=0) + return new_img @classmethod def load_image(cls, @@ -106,8 +126,9 @@ def load_image(cls, ): """ Load image and extract metadata using LSB/Metadata - :param image_io: - :return: + :param image_io: str, bytes, Path, BytesIO + :return: ImageMetadata + :raises ValidationError: Data extraction failed """ try: image_data = ImageLsbDataExtractor().extract_data(image_io) @@ -115,7 +136,6 @@ def load_image(cls, except Exception as e: logger.debug(f"Error trying extracting data in LSB: {e}") else: - print(model) return model with Image.open(image_io) as img: title = img.info.get("Title", None) @@ -127,13 +147,22 @@ def load_image(cls, except Exception as e: logger.debug(f"Error loading comment: {e}") comment = {} - return cls(Title=title, Description=prompt, Comment=cls.CommentModel(**comment)) + return cls(Title=title, Description=prompt, Comment=cls.CommentModel(**comment)) @staticmethod def verify_image_is_novelai( image: Union[Image.Image, np.ndarray], verify_key_hex: str = "Y2JcQAOhLwzwSDUJPNgL04nS0Tbqm7cSRc4xk0vRMic=" ) -> bool: + """ + Verify if the image is a NovelAI generated image + :param image: Image.Image or np.ndarray + :param verify_key_hex: + :return: bool + :raises RuntimeError: No metadata found in image + :raises RuntimeError: Comment not in metadata + :raises RuntimeError: signed_hash not in comment + """ # MIT:https://github.com/NovelAI/novelai-image-metadata/blob/main/nai_sig.py if isinstance(image, Image.Image): image = np.array(image)