novelai-python 0.2.9__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelai_python-0.2.9 → novelai_python-0.3.0}/PKG-INFO +2 -1
- {novelai_python-0.2.9 → novelai_python-0.3.0}/pyproject.toml +2 -1
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/ai/generate_image.py +10 -8
- novelai_python-0.3.0/src/novelai_python/utils/useful.py +104 -0
- novelai_python-0.2.9/src/novelai_python/utils/useful.py +0 -44
- {novelai_python-0.2.9 → novelai_python-0.3.0}/LICENSE +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/NOTICE.MD +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/README.md +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_exceptions.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/ai/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/ai/generate_image.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/ai/upscale.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/schema.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/user/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/user/information.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/user/login.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/user/subscription.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/credential/ApiToken.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/credential/JwtToken.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/credential/UserAuth.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/credential/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/credential/_base.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/ai/LICENSE +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/ai/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/ai/upscale.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/schema.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/user/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/user/information.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/user/login.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/sdk/user/subscription.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/server.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/encode.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/hash.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/random_prompt/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/random_prompt/tag.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/random_prompt/tag_artist.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/random_prompt/tag_character.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/random_prompt/tag_nsfw.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/tests/__init__.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/tests/test_random_prompt.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/tests/test_server.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/tests/test_server_run.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/tests/test_upscale.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/tests/test_user_information.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/tests/test_user_login.py +0 -0
- {novelai_python-0.2.9 → novelai_python-0.3.0}/tests/test_user_subscription.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: novelai-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Novelai Python Binding With Pydantic
|
|
5
5
|
Author-Email: sudoskys <coldlando@hotmail.com>
|
|
6
6
|
License: Apache License 2.0
|
|
@@ -19,6 +19,7 @@ Requires-Dist: fastapi>=0.109.0
|
|
|
19
19
|
Requires-Dist: uvicorn[standard]>=0.27.0.post1
|
|
20
20
|
Requires-Dist: numpy>=1.24.4
|
|
21
21
|
Requires-Dist: argon2-cffi>=23.1.0
|
|
22
|
+
Requires-Dist: opencv-python>=4.8.1.78
|
|
22
23
|
Requires-Dist: pytest>=7.4.4; extra == "testing"
|
|
23
24
|
Requires-Dist: pytest-asyncio>=0.23.4; extra == "testing"
|
|
24
25
|
Provides-Extra: testing
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "novelai-python"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "Novelai Python Binding With Pydantic"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "sudoskys", email = "coldlando@hotmail.com" },
|
|
@@ -18,6 +18,7 @@ dependencies = [
|
|
|
18
18
|
"uvicorn[standard]>=0.27.0.post1",
|
|
19
19
|
"numpy>=1.24.4",
|
|
20
20
|
"argon2-cffi>=23.1.0",
|
|
21
|
+
"opencv-python>=4.8.1.78",
|
|
21
22
|
]
|
|
22
23
|
requires-python = ">=3.8"
|
|
23
24
|
readme = "README.md"
|
|
@@ -207,6 +207,14 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
207
207
|
parameters: Params = Params()
|
|
208
208
|
model_config = ConfigDict(extra="ignore")
|
|
209
209
|
|
|
210
|
+
@property
|
|
211
|
+
def endpoint(self):
|
|
212
|
+
return self._endpoint
|
|
213
|
+
|
|
214
|
+
@endpoint.setter
|
|
215
|
+
def endpoint(self, value):
|
|
216
|
+
self._endpoint = value
|
|
217
|
+
|
|
210
218
|
@override
|
|
211
219
|
def model_post_init(self, *args) -> None:
|
|
212
220
|
"""
|
|
@@ -241,20 +249,14 @@ class GenerateImageInfer(ApiBaseModel):
|
|
|
241
249
|
def validate_model(self):
|
|
242
250
|
if self.action == Action.INFILL and not self.parameters.mask:
|
|
243
251
|
logger.warning("Mask maybe required for infill mode.")
|
|
252
|
+
if self.action == Action.INFILL:
|
|
253
|
+
self.parameters.extra_noise_seed = self.parameters.seed
|
|
244
254
|
return self
|
|
245
255
|
|
|
246
256
|
@property
|
|
247
257
|
def base_url(self):
|
|
248
258
|
return f"{self.endpoint.strip('/')}/ai/generate-image"
|
|
249
259
|
|
|
250
|
-
@property
|
|
251
|
-
def endpoint(self):
|
|
252
|
-
return self._endpoint
|
|
253
|
-
|
|
254
|
-
@endpoint.setter
|
|
255
|
-
def endpoint(self, value):
|
|
256
|
-
self._endpoint = value
|
|
257
|
-
|
|
258
260
|
def calculate_cost(self, is_opus: bool = False):
|
|
259
261
|
"""
|
|
260
262
|
Calculate the Anlas cost of current parameters.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# @Time : 2024/2/9 下午10:41
|
|
3
|
+
# @Author : sudoskys
|
|
4
|
+
# @File : useful.py
|
|
5
|
+
# @Software: PyCharm
|
|
6
|
+
import collections
|
|
7
|
+
import random
|
|
8
|
+
from typing import List, Union
|
|
9
|
+
|
|
10
|
+
import cv2 as cv
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def enum_to_list(enum_):
|
|
15
|
+
return list(map(lambda x: x.value, enum_._member_map_.values()))
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class QueSelect(object):
|
|
19
|
+
def __init__(self, data: List[str]):
|
|
20
|
+
"""
|
|
21
|
+
A queue selector
|
|
22
|
+
:param data:
|
|
23
|
+
"""
|
|
24
|
+
self.data = collections.deque(data)
|
|
25
|
+
self.used = collections.deque()
|
|
26
|
+
self.users = {}
|
|
27
|
+
|
|
28
|
+
def get(self, user_id: Union[int, str]) -> str:
|
|
29
|
+
user_id = str(user_id)
|
|
30
|
+
if user_id not in self.users:
|
|
31
|
+
self.users[user_id] = {'data': self.data.copy(), 'used': collections.deque()}
|
|
32
|
+
|
|
33
|
+
user_data = self.users[user_id]['data']
|
|
34
|
+
user_used = self.users[user_id]['used']
|
|
35
|
+
|
|
36
|
+
if len(user_data) == 0:
|
|
37
|
+
user_data, user_used = user_used, user_data
|
|
38
|
+
# 随机掉 User data
|
|
39
|
+
if len(user_data) > 2:
|
|
40
|
+
random.shuffle(user_data)
|
|
41
|
+
self.users[user_id]['data'] = user_data
|
|
42
|
+
self.users[user_id]['used'] = user_used
|
|
43
|
+
|
|
44
|
+
selected = user_data.popleft()
|
|
45
|
+
user_used.append(selected)
|
|
46
|
+
|
|
47
|
+
return selected
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def create_mask_from_sketch(original_img_bytes: bytes,
|
|
51
|
+
sketch_img_bytes: bytes,
|
|
52
|
+
output_format: str = '.png',
|
|
53
|
+
jagged_edges: bool = True
|
|
54
|
+
) -> bytes:
|
|
55
|
+
"""
|
|
56
|
+
Function to create a mask from original and sketch images input as bytes. Returns bytes.
|
|
57
|
+
|
|
58
|
+
:param jagged_edges: Whether to create jagged edges in the mask. Defaults to False.
|
|
59
|
+
:param original_img_bytes: Bytes corresponding to the original image.
|
|
60
|
+
:param sketch_img_bytes: Bytes corresponding to the sketch image.
|
|
61
|
+
:param output_format: Format of the output image. Defaults to '.png'. It could also be '.jpg'
|
|
62
|
+
:returns bytes: Bytes corresponding to the resultant mask
|
|
63
|
+
"""
|
|
64
|
+
# Load images
|
|
65
|
+
ori_img = cv.imdecode(np.frombuffer(original_img_bytes, np.uint8), cv.IMREAD_COLOR)
|
|
66
|
+
sketch_img = cv.imdecode(np.frombuffer(sketch_img_bytes, np.uint8), cv.IMREAD_COLOR)
|
|
67
|
+
|
|
68
|
+
# Check if images have the same size
|
|
69
|
+
if ori_img.shape != sketch_img.shape:
|
|
70
|
+
raise ValueError("Images must have the same size.")
|
|
71
|
+
|
|
72
|
+
# Calculate difference between the original and sketch images
|
|
73
|
+
diff_img = cv.absdiff(ori_img, sketch_img)
|
|
74
|
+
|
|
75
|
+
# Convert the difference image to grayscale
|
|
76
|
+
diff_gray = cv.cvtColor(diff_img, cv.COLOR_BGR2GRAY)
|
|
77
|
+
|
|
78
|
+
# Threshold to create the mask
|
|
79
|
+
_, thresh = cv.threshold(diff_gray, 10, 255, cv.THRESH_BINARY)
|
|
80
|
+
|
|
81
|
+
if jagged_edges:
|
|
82
|
+
# Use a bigger kernel for dilation to create larger 'step' effect at the edges
|
|
83
|
+
kernel = np.ones((7, 7), np.uint8)
|
|
84
|
+
else:
|
|
85
|
+
kernel = np.ones((3, 3), np.uint8)
|
|
86
|
+
|
|
87
|
+
# Perform morphological opening to remove small noise
|
|
88
|
+
opening = cv.morphologyEx(thresh, cv.MORPH_OPEN, kernel, iterations=2)
|
|
89
|
+
|
|
90
|
+
# Further remove noise with a Gaussian filter
|
|
91
|
+
smooth = cv.GaussianBlur(opening, (5, 5), 0)
|
|
92
|
+
|
|
93
|
+
if jagged_edges:
|
|
94
|
+
# Apply additional thresholding to create sharper, jagged edges
|
|
95
|
+
_, smooth = cv.threshold(smooth, 128, 255, cv.THRESH_BINARY)
|
|
96
|
+
|
|
97
|
+
# Convert image to BytesIO object
|
|
98
|
+
is_success, buffer = cv.imencode(output_format, smooth)
|
|
99
|
+
if is_success:
|
|
100
|
+
output_io = buffer.tobytes()
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError("Error during conversion of image to BytesIO object")
|
|
103
|
+
|
|
104
|
+
return output_io
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
# @Time : 2024/2/9 下午10:41
|
|
3
|
-
# @Author : sudoskys
|
|
4
|
-
# @File : useful.py
|
|
5
|
-
# @Software: PyCharm
|
|
6
|
-
import collections
|
|
7
|
-
import random
|
|
8
|
-
from typing import List, Union
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def enum_to_list(enum_):
|
|
12
|
-
return list(map(lambda x: x.value, enum_._member_map_.values()))
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class QueSelect(object):
|
|
16
|
-
def __init__(self, data: List[str]):
|
|
17
|
-
"""
|
|
18
|
-
A queue selector
|
|
19
|
-
:param data:
|
|
20
|
-
"""
|
|
21
|
-
self.data = collections.deque(data)
|
|
22
|
-
self.used = collections.deque()
|
|
23
|
-
self.users = {}
|
|
24
|
-
|
|
25
|
-
def get(self, user_id: Union[int, str]) -> str:
|
|
26
|
-
user_id = str(user_id)
|
|
27
|
-
if user_id not in self.users:
|
|
28
|
-
self.users[user_id] = {'data': self.data.copy(), 'used': collections.deque()}
|
|
29
|
-
|
|
30
|
-
user_data = self.users[user_id]['data']
|
|
31
|
-
user_used = self.users[user_id]['used']
|
|
32
|
-
|
|
33
|
-
if len(user_data) == 0:
|
|
34
|
-
user_data, user_used = user_used, user_data
|
|
35
|
-
# 随机掉 User data
|
|
36
|
-
if len(user_data) > 2:
|
|
37
|
-
random.shuffle(user_data)
|
|
38
|
-
self.users[user_id]['data'] = user_data
|
|
39
|
-
self.users[user_id]['used'] = user_used
|
|
40
|
-
|
|
41
|
-
selected = user_data.popleft()
|
|
42
|
-
user_used.append(selected)
|
|
43
|
-
|
|
44
|
-
return selected
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/ai/generate_image.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/user/information.py
RENAMED
|
File without changes
|
|
File without changes
|
{novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/_response/user/subscription.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/random_prompt/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/random_prompt/tag_artist.py
RENAMED
|
File without changes
|
|
File without changes
|
{novelai_python-0.2.9 → novelai_python-0.3.0}/src/novelai_python/utils/random_prompt/tag_nsfw.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|