Fix: Handle non-ASCII filenames in image processing
Browse files- .gitignore +1 -0
- app.py +58 -32
.gitignore
CHANGED
@@ -27,6 +27,7 @@ share/python-wheels/
|
|
27 |
.installed.cfg
|
28 |
*.egg
|
29 |
MANIFEST
|
|
|
30 |
|
31 |
# PyInstaller
|
32 |
# Usually these files are written by a python script from a template
|
|
|
27 |
.installed.cfg
|
28 |
*.egg
|
29 |
MANIFEST
|
30 |
+
output/
|
31 |
|
32 |
# PyInstaller
|
33 |
# Usually these files are written by a python script from a template
|
app.py
CHANGED
@@ -10,9 +10,9 @@ import gradio as gr
|
|
10 |
import os
|
11 |
import cv2
|
12 |
import numpy as np
|
13 |
-
import tempfile
|
14 |
import shutil
|
15 |
from tqdm import tqdm
|
|
|
16 |
|
17 |
from image_processing.panel import generate_panel_blocks, generate_panel_blocks_by_ai
|
18 |
from manga_panel_processor import remove_border
|
@@ -49,20 +49,31 @@ def process_images(
|
|
49 |
if not input_files:
|
50 |
raise gr.Error("No images uploaded. Please upload at least one image.")
|
51 |
|
52 |
-
# Create a temporary directory
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
|
|
|
56 |
for image_file in tqdm(input_files, desc="Processing Images"):
|
57 |
try:
|
58 |
# The image_file object from gr.Files has a .name attribute with the temp path
|
59 |
original_filename = os.path.basename(image_file.name)
|
60 |
filename_no_ext, file_ext = os.path.splitext(original_filename)
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
-
# Read the image using OpenCV
|
63 |
-
image = cv2.imread(image_file.name)
|
64 |
if image is None:
|
65 |
-
print(f"Warning: Could not read image {original_filename}. Skipping.")
|
66 |
continue
|
67 |
|
68 |
# Select the processing function based on the chosen method
|
@@ -85,57 +96,65 @@ def process_images(
|
|
85 |
# Should not happen with Radio button selection
|
86 |
panel_blocks = []
|
87 |
|
|
|
88 |
if not panel_blocks:
|
89 |
-
print(f"Warning: No panels found in {original_filename}.")
|
90 |
-
|
91 |
|
92 |
# Determine the output path for the panels of this image
|
93 |
if separate_folders:
|
94 |
# Create a sub-directory for each image
|
95 |
-
image_output_folder = os.path.join(
|
96 |
os.makedirs(image_output_folder, exist_ok=True)
|
97 |
else:
|
98 |
# Output all panels to the root of the temp directory
|
99 |
-
image_output_folder =
|
100 |
|
101 |
# Save each panel block
|
102 |
for i, panel in enumerate(panel_blocks):
|
103 |
if remove_borders:
|
104 |
panel = remove_border(panel)
|
|
|
|
|
105 |
if separate_folders:
|
106 |
-
# e.g., /tmp/xyz/
|
107 |
-
panel_filename = f"panel_{i}{
|
108 |
else:
|
109 |
-
# e.g., /tmp/xyz/
|
110 |
-
panel_filename = f"{filename_no_ext}_panel_{i}{
|
111 |
|
112 |
output_path = os.path.join(image_output_folder, panel_filename)
|
113 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
|
115 |
except Exception as e:
|
116 |
print(f"Error processing {original_filename}: {e}")
|
117 |
-
raise
|
118 |
-
|
|
|
119 |
# After processing all images, check if any panels were generated
|
120 |
-
if not os.listdir(
|
121 |
raise gr.Error("Processing complete, but no panels were extracted from any of the images.")
|
122 |
-
|
123 |
-
# --- Create a zip file ---
|
124 |
-
|
125 |
-
|
126 |
-
#
|
127 |
-
|
128 |
-
|
129 |
-
# Define the base name for our archive (path + filename without extension)
|
130 |
-
zip_path_base = os.path.join(zip_output_dir, "adenzu_output")
|
131 |
|
132 |
-
# Create the zip file
|
133 |
-
# The first argument is the full path for the output file (minus extension).
|
134 |
-
# The third argument is the directory to be zipped.
|
135 |
final_zip_path = shutil.make_archive(
|
136 |
base_name=zip_path_base,
|
137 |
format='zip',
|
138 |
-
root_dir=
|
139 |
)
|
140 |
|
141 |
print(f"Created ZIP file at: {final_zip_path}")
|
@@ -143,6 +162,13 @@ def process_images(
|
|
143 |
# Gradio takes this path and provides it as a download link.
|
144 |
return final_zip_path
|
145 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
146 |
|
147 |
def main():
|
148 |
"""
|
|
|
10 |
import os
|
11 |
import cv2
|
12 |
import numpy as np
|
|
|
13 |
import shutil
|
14 |
from tqdm import tqdm
|
15 |
+
from datetime import datetime
|
16 |
|
17 |
from image_processing.panel import generate_panel_blocks, generate_panel_blocks_by_ai
|
18 |
from manga_panel_processor import remove_border
|
|
|
49 |
if not input_files:
|
50 |
raise gr.Error("No images uploaded. Please upload at least one image.")
|
51 |
|
52 |
+
# Create a unique, temporary sub-directory inside the 'output' folder for this run.
|
53 |
+
main_output_dir = "output"
|
54 |
+
os.makedirs(main_output_dir, exist_ok=True)
|
55 |
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
56 |
+
|
57 |
+
# All intermediate panel files will be stored here before being zipped.
|
58 |
+
# This directory will be created inside 'output' and removed after zipping.
|
59 |
+
panel_output_dir = os.path.join(main_output_dir, f"temp_panels_{timestamp}")
|
60 |
+
os.makedirs(panel_output_dir)
|
61 |
|
62 |
+
try:
|
63 |
for image_file in tqdm(input_files, desc="Processing Images"):
|
64 |
try:
|
65 |
# The image_file object from gr.Files has a .name attribute with the temp path
|
66 |
original_filename = os.path.basename(image_file.name)
|
67 |
filename_no_ext, file_ext = os.path.splitext(original_filename)
|
68 |
+
|
69 |
+
# Read image with Unicode path support to handle non-ASCII filenames.
|
70 |
+
# Read the file into a numpy array first.
|
71 |
+
stream = np.fromfile(image_file.name, np.uint8)
|
72 |
+
# Decode the numpy array into an image.
|
73 |
+
image = cv2.imdecode(stream, cv2.IMREAD_COLOR)
|
74 |
|
|
|
|
|
75 |
if image is None:
|
76 |
+
print(f"Warning: Could not read or decode image {original_filename}. Skipping.")
|
77 |
continue
|
78 |
|
79 |
# Select the processing function based on the chosen method
|
|
|
96 |
# Should not happen with Radio button selection
|
97 |
panel_blocks = []
|
98 |
|
99 |
+
# If no panels were detected, use the original image as a single panel.
|
100 |
if not panel_blocks:
|
101 |
+
print(f"Warning: No panels found in {original_filename}. Using the original image.")
|
102 |
+
panel_blocks = [image]
|
103 |
|
104 |
# Determine the output path for the panels of this image
|
105 |
if separate_folders:
|
106 |
# Create a sub-directory for each image
|
107 |
+
image_output_folder = os.path.join(panel_output_dir, filename_no_ext)
|
108 |
os.makedirs(image_output_folder, exist_ok=True)
|
109 |
else:
|
110 |
# Output all panels to the root of the temp directory
|
111 |
+
image_output_folder = panel_output_dir
|
112 |
|
113 |
# Save each panel block
|
114 |
for i, panel in enumerate(panel_blocks):
|
115 |
if remove_borders:
|
116 |
panel = remove_border(panel)
|
117 |
+
|
118 |
+
save_ext = file_ext if file_ext else '.png'
|
119 |
if separate_folders:
|
120 |
+
# e.g., /tmp/xyz/image_name/panel_0.png
|
121 |
+
panel_filename = f"panel_{i}{save_ext}"
|
122 |
else:
|
123 |
+
# e.g., /tmp/xyz/image_name_panel_0.png
|
124 |
+
panel_filename = f"{filename_no_ext}_panel_{i}{save_ext}"
|
125 |
|
126 |
output_path = os.path.join(image_output_folder, panel_filename)
|
127 |
+
|
128 |
+
# Write image with Unicode path support.
|
129 |
+
# Encode the image to a memory buffer based on the file extension.
|
130 |
+
is_success, buffer = cv2.imencode(save_ext, panel)
|
131 |
+
if not is_success:
|
132 |
+
print(f"Warning: Could not encode panel {panel_filename}. Skipping.")
|
133 |
+
continue
|
134 |
+
# Write the buffer to a file using Python's standard I/O.
|
135 |
+
with open(output_path, 'wb') as f:
|
136 |
+
f.write(buffer)
|
137 |
|
138 |
except Exception as e:
|
139 |
print(f"Error processing {original_filename}: {e}")
|
140 |
+
# Optionally, re-raise as a Gradio error to notify the user.
|
141 |
+
# raise gr.Error(f"Failed to process {original_filename}: {e}")
|
142 |
+
|
143 |
# After processing all images, check if any panels were generated
|
144 |
+
if not os.listdir(panel_output_dir):
|
145 |
raise gr.Error("Processing complete, but no panels were extracted from any of the images.")
|
146 |
+
|
147 |
+
# --- Create a zip file in the 'output' directory ---
|
148 |
+
zip_filename_base = f"adenzu_output_{timestamp}"
|
149 |
+
|
150 |
+
# Define the full path for our archive (path + filename without extension).
|
151 |
+
zip_path_base = os.path.join(main_output_dir, zip_filename_base)
|
|
|
|
|
|
|
152 |
|
153 |
+
# Create the zip file from the temporary panel directory.
|
|
|
|
|
154 |
final_zip_path = shutil.make_archive(
|
155 |
base_name=zip_path_base,
|
156 |
format='zip',
|
157 |
+
root_dir=panel_output_dir
|
158 |
)
|
159 |
|
160 |
print(f"Created ZIP file at: {final_zip_path}")
|
|
|
162 |
# Gradio takes this path and provides it as a download link.
|
163 |
return final_zip_path
|
164 |
|
165 |
+
finally:
|
166 |
+
# Clean up the temporary panel directory, leaving only the final ZIP file.
|
167 |
+
# This block executes whether the 'try' block succeeds or fails.
|
168 |
+
if os.path.exists(panel_output_dir):
|
169 |
+
print(f"Cleaning up temporary panel directory: {panel_output_dir}")
|
170 |
+
shutil.rmtree(panel_output_dir)
|
171 |
+
|
172 |
|
173 |
def main():
|
174 |
"""
|