From 129d0ce2aba3115e68dafea3776b3b1b3ab89f1c Mon Sep 17 00:00:00 2001 From: sudoskys Date: Wed, 7 Feb 2024 03:09:20 +0800 Subject: [PATCH 1/3] :arrow_up: chore: bump version to 0.1.7 --- pyproject.toml | 3 +- src/novelai_python/sdk/ai/generate_image.py | 18 +++++++- src/novelai_python/utils/__init__.py | 7 ++- src/novelai_python/utils/hash.py | 48 ++++++++++++++++++--- 4 files changed, 66 insertions(+), 10 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 586eab8..6fd2709 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "novelai-python" -version = "0.1.6" +version = "0.1.7" description = "Novelai Python Binding With Pydantic" authors = [ { name = "sudoskys", email = "coldlando@hotmail.com" }, @@ -16,6 +16,7 @@ dependencies = [ "curl-cffi>=0.5.10", "fastapi>=0.109.0", "uvicorn[standard]>=0.27.0.post1", + "numpy>=1.24.4", ] requires-python = ">=3.8" readme = "README.md" diff --git a/src/novelai_python/sdk/ai/generate_image.py b/src/novelai_python/sdk/ai/generate_image.py index 298d38f..8814e40 100644 --- a/src/novelai_python/sdk/ai/generate_image.py +++ b/src/novelai_python/sdk/ai/generate_image.py @@ -12,6 +12,7 @@ import httpx from curl_cffi.requests import AsyncSession from loguru import logger +from novelai_python.utils import NovelAiMetadata from pydantic import BaseModel, ConfigDict, PrivateAttr, field_validator, model_validator from ..._exceptions import APIError, AuthError @@ -229,7 +230,15 @@ def build(cls, parameters=cls.Params(**param) ) - async def generate(self, session: Union[AsyncSession, JwtCredential]) -> ImageGenerateResp: + async def generate(self, session: Union[AsyncSession, JwtCredential], + *, + remove_sign: bool = True) -> ImageGenerateResp: + """ + 生成图片 + :param session: session + :param remove_sign: 移除追踪信息 + :return: + """ if isinstance(session, JwtCredential): session = session.session request_data = self.model_dump(exclude_none=True) @@ -274,7 +283,12 @@ async def generate(self, session: Union[AsyncSession, JwtCredential]) -> ImageGe response=try_jsonfy(response.content) ) for filename in file_list: - unzip_content.append((filename, zip_file.read(filename))) + data = zip_file.read(filename) + if remove_sign: + data = NovelAiMetadata.rehash(BytesIO(data), remove_stealth=True) + if not isinstance(data, bytes): + data = data.getvalue() + unzip_content.append((filename, data)) return ImageGenerateResp( meta=ImageGenerateResp.RequestParams( endpoint=self.base_url, diff --git a/src/novelai_python/utils/__init__.py b/src/novelai_python/utils/__init__.py index f1d9393..e58bffc 100644 --- a/src/novelai_python/utils/__init__.py +++ b/src/novelai_python/utils/__init__.py @@ -12,13 +12,16 @@ from .hash import NovelAiMetadata -def png_info_reset(img: Union[str, io.BytesIO]): +def png_info_reset(img: Union[str, io.BytesIO], + *, + remove_stealth: bool = False): """ reset png info hash value + :param remove_stealth: LCB :param img: BytesIO 对象 :return: """ - _fixed = NovelAiMetadata.rehash(img_io=img) + _fixed = NovelAiMetadata.rehash(img_io=img, remove_stealth=remove_stealth) return _fixed diff --git a/src/novelai_python/utils/hash.py b/src/novelai_python/utils/hash.py index 7b0b191..6a5b2b2 100644 --- a/src/novelai_python/utils/hash.py +++ b/src/novelai_python/utils/hash.py @@ -10,6 +10,7 @@ from io import BytesIO from typing import Union +import numpy as np from PIL import Image from PIL.PngImagePlugin import PngInfo from loguru import logger @@ -35,16 +36,33 @@ def metadata(self): return {"Title": self.title, "Description": self.description, "Comment": self.comment} @staticmethod - def rehash(img_io): - cls = NaiPic.read_from_img(img_io) + def rehash(img_io, remove_stealth: bool = False): + cls = NovelAiMetadata.build_from_img(img_io) cls.comment["signed_hash"] = sign_message(json.dumps(cls.description), "novalai-client") - cls.write_out(img_io=img_io) - return img_io + _new_img_io = cls.write_out(img_io=img_io, remove_stealth=remove_stealth) + return _new_img_io - def write_out(self, img_io: BytesIO): + @staticmethod + def remove_stealth_info(img_io): + image = Image.open(img_io).convert('RGBA') + data = np.array(image) + data[..., 3] = 254 + new_image = Image.fromarray(data, 'RGBA') + _new_img_io = BytesIO() + new_image.save(_new_img_io, format="PNG") + return _new_img_io + + def write_out(self, img_io: BytesIO, *, remove_stealth: bool = False): with Image.open(img_io) as img: + if remove_stealth: + img = img.convert('RGBA') + data = np.array(img) + data[..., 3] = 254 + img = Image.fromarray(data, 'RGBA') metadata = PngInfo() for k, v in self.metadata.items(): + if isinstance(v, dict): + v = json.dumps(v) metadata.add_text(k, v) _new_img = BytesIO() img.save(_new_img, format="PNG", pnginfo=metadata, quality=95, optimize=False) @@ -88,3 +106,23 @@ def decode_base64(encoded_data): # 使用base64解码 decoded_data = base64.b64decode(byte_data) return decoded_data.decode("UTF-8") # 再次使用UTF-8将字节对象转为字符串 + + +def decode_image(img: Union[str, BytesIO], output_path: str): + encoded_image = Image.open(img) + red_channel = encoded_image.split()[3] + x_size = encoded_image.size[0] + y_size = encoded_image.size[1] + decoded_image = Image.new("RGB", encoded_image.size) + pixels = decoded_image.load() + for i in range(x_size): + for j in range(y_size): + if i < 7: # the left 7 columns + r = red_channel.getpixel((i, j)) + if r > 254: # above the threshold + pixels[i, j] = (0, 0, 0) # black + else: + pixels[i, j] = (255, 255, 255) # white + else: + pixels[i, j] = (255, 255, 255) # default to white for right part of image + decoded_image.save(output_path) From 4381d7f4d0671a7364d03776b142deaff5af8473 Mon Sep 17 00:00:00 2001 From: sudoskys Date: Wed, 7 Feb 2024 03:09:28 +0800 Subject: [PATCH 2/3] :arrow_up: chore: bump version to 0.1.7 --- pdm.lock | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/pdm.lock b/pdm.lock index 264bce4..aba63e3 100644 --- a/pdm.lock +++ b/pdm.lock @@ -5,7 +5,7 @@ groups = ["default", "dev", "testing"] strategy = ["cross_platform", "inherit_metadata"] lock_version = "4.4.1" -content_hash = "sha256:24f77c30bdcdf2fcccf4867ed9346ec6060d6fa3324b065bbf148b190c29ad9f" +content_hash = "sha256:4380c0e0d145f0e2709a296490964bb1abbd62c88a7f64ebb774dd998db100d0" [[package]] name = "annotated-types" @@ -492,6 +492,43 @@ files = [ {file = "nodeenv-1.8.0.tar.gz", hash = "sha256:d51e0c37e64fbf47d017feac3145cdbb58836d7eee8c6f6d3b6880c5456227d2"}, ] +[[package]] +name = "numpy" +version = "1.24.4" +requires_python = ">=3.8" +summary = "Fundamental package for array computing in Python" +groups = ["default"] +files = [ + {file = "numpy-1.24.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:c0bfb52d2169d58c1cdb8cc1f16989101639b34c7d3ce60ed70b19c63eba0b64"}, + {file = "numpy-1.24.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:ed094d4f0c177b1b8e7aa9cba7d6ceed51c0e569a5318ac0ca9a090680a6a1b1"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:79fc682a374c4a8ed08b331bef9c5f582585d1048fa6d80bc6c35bc384eee9b4"}, + {file = "numpy-1.24.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ffe43c74893dbf38c2b0a1f5428760a1a9c98285553c89e12d70a96a7f3a4d6"}, + {file = "numpy-1.24.4-cp310-cp310-win32.whl", hash = "sha256:4c21decb6ea94057331e111a5bed9a79d335658c27ce2adb580fb4d54f2ad9bc"}, + {file = "numpy-1.24.4-cp310-cp310-win_amd64.whl", hash = "sha256:b4bea75e47d9586d31e892a7401f76e909712a0fd510f58f5337bea9572c571e"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f136bab9c2cfd8da131132c2cf6cc27331dd6fae65f95f69dcd4ae3c3639c810"}, + {file = "numpy-1.24.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e2926dac25b313635e4d6cf4dc4e51c8c0ebfed60b801c799ffc4c32bf3d1254"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:222e40d0e2548690405b0b3c7b21d1169117391c2e82c378467ef9ab4c8f0da7"}, + {file = "numpy-1.24.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7215847ce88a85ce39baf9e89070cb860c98fdddacbaa6c0da3ffb31b3350bd5"}, + {file = "numpy-1.24.4-cp311-cp311-win32.whl", hash = "sha256:4979217d7de511a8d57f4b4b5b2b965f707768440c17cb70fbf254c4b225238d"}, + {file = "numpy-1.24.4-cp311-cp311-win_amd64.whl", hash = "sha256:b7b1fc9864d7d39e28f41d089bfd6353cb5f27ecd9905348c24187a768c79694"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1452241c290f3e2a312c137a9999cdbf63f78864d63c79039bda65ee86943f61"}, + {file = "numpy-1.24.4-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:04640dab83f7c6c85abf9cd729c5b65f1ebd0ccf9de90b270cd61935eef0197f"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a5425b114831d1e77e4b5d812b69d11d962e104095a5b9c3b641a218abcc050e"}, + {file = "numpy-1.24.4-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd80e219fd4c71fc3699fc1dadac5dcf4fd882bfc6f7ec53d30fa197b8ee22dc"}, + {file = "numpy-1.24.4-cp38-cp38-win32.whl", hash = "sha256:4602244f345453db537be5314d3983dbf5834a9701b7723ec28923e2889e0bb2"}, + {file = "numpy-1.24.4-cp38-cp38-win_amd64.whl", hash = "sha256:692f2e0f55794943c5bfff12b3f56f99af76f902fc47487bdfe97856de51a706"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:2541312fbf09977f3b3ad449c4e5f4bb55d0dbf79226d7724211acc905049400"}, + {file = "numpy-1.24.4-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:9667575fb6d13c95f1b36aca12c5ee3356bf001b714fc354eb5465ce1609e62f"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3a86ed21e4f87050382c7bc96571755193c4c1392490744ac73d660e8f564a9"}, + {file = "numpy-1.24.4-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d11efb4dbecbdf22508d55e48d9c8384db795e1b7b51ea735289ff96613ff74d"}, + {file = "numpy-1.24.4-cp39-cp39-win32.whl", hash = "sha256:6620c0acd41dbcb368610bb2f4d83145674040025e5536954782467100aa8835"}, + {file = "numpy-1.24.4-cp39-cp39-win_amd64.whl", hash = "sha256:befe2bf740fd8373cf56149a5c23a0f601e82869598d41f8e188a0e9869926f8"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:31f13e25b4e304632a4619d0e0777662c2ffea99fcae2029556b17d8ff958aef"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95f7ac6540e95bc440ad77f56e520da5bf877f87dca58bd095288dce8940532a"}, + {file = "numpy-1.24.4-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e98f220aa76ca2a977fe435f5b04d7b3470c0a2e6312907b37ba6068f26787f2"}, + {file = "numpy-1.24.4.tar.gz", hash = "sha256:80f5e3a4e498641401868df4208b74581206afbee7cf7b8329daae82676d9463"}, +] + [[package]] name = "packaging" version = "23.2" From c7a27a8480024b2e1c9c61ae2ada0f13bf213128 Mon Sep 17 00:00:00 2001 From: sudoskys Date: Wed, 7 Feb 2024 03:10:14 +0800 Subject: [PATCH 3/3] :arrow_up: chore: bump version to 0.1.7 --- src/novelai_python/sdk/ai/generate_image.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/novelai_python/sdk/ai/generate_image.py b/src/novelai_python/sdk/ai/generate_image.py index 8814e40..74cf696 100644 --- a/src/novelai_python/sdk/ai/generate_image.py +++ b/src/novelai_python/sdk/ai/generate_image.py @@ -232,7 +232,7 @@ def build(cls, async def generate(self, session: Union[AsyncSession, JwtCredential], *, - remove_sign: bool = True) -> ImageGenerateResp: + remove_sign: bool = False) -> ImageGenerateResp: """ 生成图片 :param session: session