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 |
"""
|