Skip to content

Commit 8d952d2

Browse files
authored
Merge pull request #25 from TareHimself/dev
Dev
2 parents 941625e + 891015d commit 8d952d2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1344
-792
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ datasets/*
1010
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
1111

1212
# dependencies
13-
/node_modules
13+
*/node_modules
1414
/.pnp
1515
.pnp.js
1616

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Manga Translator
22

3-
<img src="assets/examples/ui.png"/>
3+
<img src="assets/examples/ui_09_11_23.png"/>
44

55
## Why ?
66

assets/examples/ui.png

-865 KB
Binary file not shown.

assets/examples/ui_09_11_23.png

696 KB
Loading

server.py

Lines changed: 42 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,21 @@ def run_task():
3939

4040
return wrapper
4141

42+
def run_in_thread(func):
43+
async def wrapper(*args, **kwargs):
44+
loop = asyncio.get_event_loop()
45+
pending_task = asyncio.Future()
46+
47+
def run_task():
48+
nonlocal loop
49+
loop.call_soon_threadsafe(pending_task.set_result, func(*args, **kwargs))
50+
51+
Thread(target=run_task, group=None, daemon=True).start()
52+
result = await pending_task
53+
return result
54+
55+
return wrapper
56+
4257

4358

4459
def cv2_image_from_url(url: str):
@@ -88,16 +103,22 @@ def set_default_headers(self):
88103
def options(self):
89104
self.set_status(200)
90105

91-
@run_in_thread
92-
def post(self):
106+
async def post(self):
93107
try:
94-
data = json.loads(self.request.body)
95108

96-
image_url = data.get('image')
109+
image = self.request.files.get('file')
110+
111+
if image is None:
112+
raise BaseException("No Image Sent")
113+
114+
data = json.loads(self.get_argument('data'))
115+
116+
97117
cleaner_id, cleaner_params = data.get('cleaner', 0), data.get('cleanerArgs', {})
98-
image_cv2 = cv2_image_from_url(image_url)
99-
result = FullConversion(ocr=CleanOcr(),cleaner=get_cleaners()[cleaner_id](**cleaner_params))([image_cv2])[0]
100-
converted_pil = cv2_to_pil(result)
118+
image_cv2 = pil_to_cv2(Image.open(io.BytesIO(image[0]['body'])))
119+
converter = FullConversion(ocr=CleanOcr(),cleaner=get_cleaners()[cleaner_id](**cleaner_params))
120+
results = await converter([image_cv2])
121+
converted_pil = cv2_to_pil(results[0])
101122
img_byte_arr = io.BytesIO()
102123
converted_pil.save(img_byte_arr, format="PNG")
103124
# Create response given the bytes
@@ -123,13 +144,15 @@ def set_default_headers(self):
123144
def options(self):
124145
self.set_status(200)
125146

126-
@run_in_thread
127-
def post(self):
147+
async def post(self):
128148
try:
129149

130-
data = json.loads(self.request.body)
150+
image = self.request.files.get('file')
131151

132-
image_url = data.get('image')
152+
if image is None:
153+
raise BaseException("No Image Sent")
154+
155+
data = json.loads(self.get_argument('data'))
133156

134157
translator_id, translator_params = data.get('translator', 0), data.get('translatorArgs', {})
135158

@@ -139,14 +162,14 @@ def post(self):
139162

140163
cleaner_id, cleaner_params = data.get('cleaner', 0), data.get('cleanerArgs', {})
141164

142-
image_cv2 = cv2_image_from_url(image_url)
165+
image_cv2 = pil_to_cv2(Image.open(io.BytesIO(image[0]['body'])))
143166

144167
converter = FullConversion(translator=get_translators()[translator_id](**translator_params),
145168
ocr=get_ocr()[ocr_id](**ocr_params), drawer=get_drawers()[drawer_id](**drawer_params),cleaner=get_cleaners()[cleaner_id](**cleaner_params),color_detect_model=None)
146169

147-
result = converter([image_cv2])[0]
170+
results = await converter([image_cv2])
148171

149-
converted_pil = cv2_to_pil(result)
172+
converted_pil = cv2_to_pil(results[0])
150173
img_byte_arr = io.BytesIO()
151174
converted_pil.save(img_byte_arr, format="PNG")
152175
# Create response given the bytes
@@ -257,8 +280,7 @@ def set_default_headers(self):
257280
def options(self):
258281
self.set_status(200)
259282

260-
@run_in_thread
261-
def post(self):
283+
async def post(self):
262284
try:
263285

264286
image = self.request.files.get('file')
@@ -270,10 +292,10 @@ def post(self):
270292

271293
converter = FullConversion(color_detect_model=None,translator=DeepLTranslator(auth_token=os.environ['DEEPL_AUTH']),ocr=MangaOcr(),translate_free_text=True)
272294

273-
translated = converter([to_convert])[0]
295+
translated = await converter([to_convert])
274296

275297
# display_image(translated,"Translated")
276-
converted_pil = cv2_to_pil(translated)
298+
converted_pil = cv2_to_pil(translated[0])
277299

278300
img_byte_arr = io.BytesIO()
279301

@@ -316,9 +338,9 @@ async def main():
316338
(r"/info", BaseHandler),
317339
(r"/clean", CleanFromWebHandler),
318340
(r"/translate", TranslateFromWebHandler),
319-
(r"/images/.*", ImageHandler),
341+
# (r"/images/.*", ImageHandler),
320342
(r"/mira/translate", MiraTranslateWebHandler),
321-
(r"/(.*)", UiFilesHandler, dict(build_path=build_path)),
343+
# (r"/(.*)", UiFilesHandler, dict(build_path=build_path)),
322344
], **settings)
323345
app.listen(app_port)
324346
webbrowser.open(f'http://localhost:{app_port}')

test.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from translator.utils import resize_and_pad
2+
import numpy as np
3+
4+
5+
img = np.zeros((300,180,3),dtype=np.uint8)
6+
7+
print(resize_and_pad(img,target_size=(1000,500)).shape)

translator/cleaners/deepfillv2.py

Lines changed: 42 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import queue
2-
from typing import Union
2+
from typing import Union
33
import numpy as np
44
from PIL import Image
55
from numpy import ndarray
@@ -16,9 +16,11 @@
1616
import sys
1717
import atexit
1818

19-
class DeepFillV2Cleaner(Cleaner):
2019

21-
IN_PAINT_MODEL_DEVICE = torch.device("cuda" if torch.cuda.is_available() and torch.cuda.device_count() > 0 else "cpu")
20+
class DeepFillV2Cleaner(Cleaner):
21+
IN_PAINT_MODEL_DEVICE = torch.device(
22+
"cuda" if torch.cuda.is_available() and torch.cuda.device_count() > 0 else "cpu"
23+
)
2224

2325
DEFAULT_MODEL_PATH = os.path.join("models", "inpainting.pth")
2426

@@ -30,22 +32,24 @@ class DeepFillV2Cleaner(Cleaner):
3032

3133
_pending_tasks = []
3234

33-
_thread: Union[None,threading.Thread] = None
35+
_thread: Union[None, threading.Thread] = None
3436

3537
@staticmethod
3638
def get_model(path: str):
37-
3839
if path == DeepFillV2Cleaner._model_path:
3940
return DeepFillV2Cleaner._model
4041
else:
41-
DeepFillV2Cleaner._model = load_model(path,DeepFillV2Cleaner.IN_PAINT_MODEL_DEVICE)
42+
DeepFillV2Cleaner._model = load_model(
43+
path, DeepFillV2Cleaner.IN_PAINT_MODEL_DEVICE
44+
)
4245
DeepFillV2Cleaner._model_path = path
4346
return DeepFillV2Cleaner._model
47+
4448
@staticmethod
4549
def in_paint(
46-
image: Image,
47-
mask: Image,
48-
model_path: str = DEFAULT_MODEL_PATH,
50+
image: Image,
51+
mask: Image,
52+
model_path: str = DEFAULT_MODEL_PATH,
4953
) -> Image:
5054
generator = DeepFillV2Cleaner.get_model(model_path)
5155

@@ -60,7 +64,9 @@ def in_paint(
6064
pad_height = grid - h % grid if h % grid > 0 else 0
6165
pad_width = grid - w % grid if w % grid > 0 else 0
6266

63-
image = torch.nn.functional.pad(image, (0, pad_width, 0, pad_height)).unsqueeze(0)
67+
image = torch.nn.functional.pad(image, (0, pad_width, 0, pad_height)).unsqueeze(
68+
0
69+
)
6470
mask = torch.nn.functional.pad(mask, (0, pad_width, 0, pad_height)).unsqueeze(0)
6571

6672
image = (image * 2 - 1.0).to(
@@ -73,7 +79,9 @@ def in_paint(
7379
image_masked = image * (1.0 - mask) # mask image
7480

7581
ones_x = torch.ones_like(image_masked)[:, 0:1, :, :]
76-
x = torch.cat([image_masked, ones_x, ones_x * mask], dim=1) # concatenate channels
82+
x = torch.cat(
83+
[image_masked, ones_x, ones_x * mask], dim=1
84+
) # concatenate channels
7785

7886
with torch.inference_mode():
7987
_, x_stage2 = generator(x, mask)
@@ -90,14 +98,12 @@ def in_paint(
9098
# img_out = img_out.crop((0, 0, w, h))
9199

92100
return img_out
93-
101+
94102
@staticmethod
95103
def _in_paint_thread():
96-
97104
payload = DeepFillV2Cleaner._job_queue.get()
98105

99106
while payload is not None:
100-
101107
data = payload
102108
pil_image, pil_mask, model_path, callback = data
103109

@@ -106,31 +112,25 @@ def _in_paint_thread():
106112
payload = DeepFillV2Cleaner._job_queue.get()
107113

108114
@staticmethod
109-
async def in_paint_async(image: Image,
110-
mask: Image,
111-
model_path: str = DEFAULT_MODEL_PATH):
115+
async def add_in_paint_task(
116+
image: np.ndarray, mask: np.ndarray, model_path: str = DEFAULT_MODEL_PATH
117+
):
112118
loop = asyncio.get_event_loop()
113119

114120
pending_task = asyncio.Future()
115121

116122
def callback(in_paint_result):
117123
nonlocal loop
118124

119-
loop.call_soon_threadsafe(pending_task.set_result, in_paint_result)
125+
loop.call_soon_threadsafe(pending_task.set_result, pil_to_cv2(in_paint_result))
120126

121-
DeepFillV2Cleaner._job_queue.put((image, mask, model_path, callback))
127+
DeepFillV2Cleaner._job_queue.put((cv2_to_pil(image), cv2_to_pil(mask), model_path, callback))
122128
DeepFillV2Cleaner._pending_tasks.append(pending_task)
123129

124130
result = await pending_task
125131
DeepFillV2Cleaner._pending_tasks.remove(pending_task)
126132
return result
127133

128-
@staticmethod
129-
def in_paint_threadsafe(image: Image,
130-
mask: Image,
131-
model_path: str = DEFAULT_MODEL_PATH):
132-
return asyncio.run(DeepFillV2Cleaner.in_paint_async(image, mask, model_path))
133-
134134
@staticmethod
135135
def _stop_in_paint_thread(signum, frame):
136136
sys.exit(signum)
@@ -147,28 +147,34 @@ def _exit_thread():
147147
except:
148148
pass
149149

150-
151-
152-
153150
def __init__(self) -> None:
154151
super().__init__()
155152

156153
if DeepFillV2Cleaner._thread is None:
157-
DeepFillV2Cleaner._thread = threading.Thread(target=lambda : DeepFillV2Cleaner._in_paint_thread(), group=None, daemon=True)
154+
DeepFillV2Cleaner._thread = threading.Thread(
155+
target=lambda: DeepFillV2Cleaner._in_paint_thread(),
156+
group=None,
157+
daemon=True,
158+
)
158159
DeepFillV2Cleaner._thread.start()
159160
atexit.register(DeepFillV2Cleaner._exit_thread)
160161

161-
162162
@staticmethod
163163
def get_name() -> str:
164164
return "Deep Fill V2"
165165

166-
def clean(self, frame: ndarray, mask: ndarray, detection_results: list[tuple[tuple[int,int,int,int],str,float]] = []) -> tuple[ndarray, ndarray]:
167-
return in_paint_optimized(
166+
async def clean(
167+
self,
168+
frame: ndarray,
169+
mask: ndarray,
170+
detection_results: list[tuple[tuple[int, int, int, int], str, float]] = [],
171+
) -> tuple[ndarray, ndarray]:
172+
loop = asyncio.get_event_loop()
173+
return await in_paint_optimized(
168174
frame,
169175
mask=mask,
170176
filtered=detection_results, # segmentation_results.boxes.xyxy.cpu().numpy()
171-
inpaint_fun= lambda frame,mask : pil_to_cv2(
172-
DeepFillV2Cleaner.in_paint_threadsafe(cv2_to_pil(frame), cv2_to_pil(mask))
173-
)
174-
)
177+
inpaint_fun=lambda frame, mask: loop.create_task(DeepFillV2Cleaner.add_in_paint_task(
178+
frame, mask
179+
)),
180+
)

0 commit comments

Comments
 (0)