Esmaill1 commited on
Commit ·
2faaae6
1
Parent(s): abcb567
Fix IndentationErrors and optimize image loading performance
Browse files- web/server.py +27 -14
- web/web_storage/index.html +6 -3
web/server.py
CHANGED
|
@@ -115,19 +115,19 @@ async def upload_image(file: UploadFile = File(...)):
|
|
| 115 |
from PIL import ImageOps
|
| 116 |
# FIX: Handle EXIF orientation (rotation)
|
| 117 |
img = ImageOps.exif_transpose(img)
|
| 118 |
-
|
| 119 |
# Get original dimensions after transposition for the web cropper
|
| 120 |
width, height = img.size
|
| 121 |
-
|
| 122 |
-
|
|
|
|
| 123 |
thumb_path = UPLOAD_DIR / f"{file_id}_thumb.jpg"
|
| 124 |
if img.mode in ("RGBA", "LA"):
|
| 125 |
bg = Image.new("RGB", img.size, (255, 255, 255))
|
| 126 |
bg.paste(img, mask=img.split()[-1])
|
| 127 |
-
bg.save(thumb_path, "JPEG")
|
| 128 |
else:
|
| 129 |
-
img.convert("RGB").save(thumb_path, "JPEG")
|
| 130 |
-
|
| 131 |
return {
|
| 132 |
"id": file_id,
|
| 133 |
"filename": file.filename,
|
|
@@ -216,19 +216,32 @@ async def process_image(
|
|
| 216 |
|
| 217 |
print(f"Pipeline: Generating final layout for {file_id}...")
|
| 218 |
final_layout = generate_layout(
|
| 219 |
-
final_processed, name, id_number,
|
| 220 |
-
add_studio_name=add_studio_name,
|
| 221 |
-
add_logo=add_logo,
|
| 222 |
add_date=add_date
|
| 223 |
)
|
| 224 |
-
|
| 225 |
result_path = RESULT_DIR / f"{file_id}_layout.jpg"
|
| 226 |
final_layout.save(result_path, "JPEG", quality=95, dpi=(300, 300))
|
| 227 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 228 |
if temp_crop.exists(): temp_crop.unlink()
|
| 229 |
-
|
| 230 |
-
return {
|
| 231 |
-
|
|
|
|
|
|
|
|
|
|
| 232 |
except Exception as e:
|
| 233 |
import traceback
|
| 234 |
traceback.print_exc()
|
|
|
|
| 115 |
from PIL import ImageOps
|
| 116 |
# FIX: Handle EXIF orientation (rotation)
|
| 117 |
img = ImageOps.exif_transpose(img)
|
| 118 |
+
|
| 119 |
# Get original dimensions after transposition for the web cropper
|
| 120 |
width, height = img.size
|
| 121 |
+
|
| 122 |
+
# Create a faster, smaller thumbnail for the UI (400x400 is plenty for the queue)
|
| 123 |
+
img.thumbnail((400, 400), Image.BILINEAR)
|
| 124 |
thumb_path = UPLOAD_DIR / f"{file_id}_thumb.jpg"
|
| 125 |
if img.mode in ("RGBA", "LA"):
|
| 126 |
bg = Image.new("RGB", img.size, (255, 255, 255))
|
| 127 |
bg.paste(img, mask=img.split()[-1])
|
| 128 |
+
bg.save(thumb_path, "JPEG", quality=85)
|
| 129 |
else:
|
| 130 |
+
img.convert("RGB").save(thumb_path, "JPEG", quality=85)
|
|
|
|
| 131 |
return {
|
| 132 |
"id": file_id,
|
| 133 |
"filename": file.filename,
|
|
|
|
| 216 |
|
| 217 |
print(f"Pipeline: Generating final layout for {file_id}...")
|
| 218 |
final_layout = generate_layout(
|
| 219 |
+
final_processed, name, id_number,
|
| 220 |
+
add_studio_name=add_studio_name,
|
| 221 |
+
add_logo=add_logo,
|
| 222 |
add_date=add_date
|
| 223 |
)
|
| 224 |
+
|
| 225 |
result_path = RESULT_DIR / f"{file_id}_layout.jpg"
|
| 226 |
final_layout.save(result_path, "JPEG", quality=95, dpi=(300, 300))
|
| 227 |
+
|
| 228 |
+
# 5. Generate a lightweight WEB PREVIEW (max 1200px width) for the UI
|
| 229 |
+
preview_path = RESULT_DIR / f"{file_id}_preview.jpg"
|
| 230 |
+
pw, ph = final_layout.size
|
| 231 |
+
p_scale = 1200 / pw if pw > 1200 else 1.0
|
| 232 |
+
if p_scale < 1.0:
|
| 233 |
+
preview_img = final_layout.resize((int(pw * p_scale), int(ph * p_scale)), Image.BILINEAR)
|
| 234 |
+
preview_img.save(preview_path, "JPEG", quality=80)
|
| 235 |
+
else:
|
| 236 |
+
final_layout.save(preview_path, "JPEG", quality=80)
|
| 237 |
+
|
| 238 |
if temp_crop.exists(): temp_crop.unlink()
|
| 239 |
+
|
| 240 |
+
return {
|
| 241 |
+
"id": file_id,
|
| 242 |
+
"result_url": f"/static/results/{file_id}_layout.jpg",
|
| 243 |
+
"preview_url": f"/static/results/{file_id}_preview.jpg"
|
| 244 |
+
}
|
| 245 |
except Exception as e:
|
| 246 |
import traceback
|
| 247 |
traceback.print_exc()
|
web/web_storage/index.html
CHANGED
|
@@ -850,7 +850,7 @@
|
|
| 850 |
|
| 851 |
return `
|
| 852 |
<div onclick="selectImage(${idx})" class="queue-slide ${currentIndex === idx ? 'active' : ''}">
|
| 853 |
-
<img src="${img.
|
| 854 |
<div class="slide-status">${statusIcon}</div>
|
| 855 |
<button onclick="deleteImage(event, ${idx})" class="slide-delete" title="حذف"><i class="fa-solid fa-xmark"></i></button>
|
| 856 |
<div class="slide-name">${img.filename}</div>
|
|
@@ -895,7 +895,9 @@
|
|
| 895 |
reprocessBtn.disabled = !data.result_url;
|
| 896 |
|
| 897 |
const previewSkeleton = document.getElementById('preview-skeleton');
|
| 898 |
-
const url = data.
|
|
|
|
|
|
|
| 899 |
|
| 900 |
// Reset the image completely before loading the new one
|
| 901 |
mainPreview.classList.add('hidden');
|
|
@@ -950,7 +952,7 @@
|
|
| 950 |
mainPreview.src = data.thumb_url;
|
| 951 |
label.textContent = 'النتيجة';
|
| 952 |
} else {
|
| 953 |
-
mainPreview.src = data.result_url + '?t=' + t;
|
| 954 |
label.textContent = 'الأصلية';
|
| 955 |
}
|
| 956 |
mainPreview.onload = () => {
|
|
@@ -1042,6 +1044,7 @@
|
|
| 1042 |
const result = await res.json();
|
| 1043 |
if (result.error) throw new Error(result.error);
|
| 1044 |
imageData[idx].result_url = result.result_url;
|
|
|
|
| 1045 |
imageData[idx].status = 'done';
|
| 1046 |
imageData[idx].steps = {
|
| 1047 |
rmbg: document.getElementById('step-rmbg').checked,
|
|
|
|
| 850 |
|
| 851 |
return `
|
| 852 |
<div onclick="selectImage(${idx})" class="queue-slide ${currentIndex === idx ? 'active' : ''}">
|
| 853 |
+
<img src="${img.preview_url ? img.preview_url + '?t=' + t : img.thumb_url}" alt="">
|
| 854 |
<div class="slide-status">${statusIcon}</div>
|
| 855 |
<button onclick="deleteImage(event, ${idx})" class="slide-delete" title="حذف"><i class="fa-solid fa-xmark"></i></button>
|
| 856 |
<div class="slide-name">${img.filename}</div>
|
|
|
|
| 895 |
reprocessBtn.disabled = !data.result_url;
|
| 896 |
|
| 897 |
const previewSkeleton = document.getElementById('preview-skeleton');
|
| 898 |
+
const url = data.preview_url ? data.preview_url + '?t=' + Date.now() :
|
| 899 |
+
data.result_url ? data.result_url + '?t=' + Date.now() :
|
| 900 |
+
data.thumb_url;
|
| 901 |
|
| 902 |
// Reset the image completely before loading the new one
|
| 903 |
mainPreview.classList.add('hidden');
|
|
|
|
| 952 |
mainPreview.src = data.thumb_url;
|
| 953 |
label.textContent = 'النتيجة';
|
| 954 |
} else {
|
| 955 |
+
mainPreview.src = (data.preview_url || data.result_url) + '?t=' + t;
|
| 956 |
label.textContent = 'الأصلية';
|
| 957 |
}
|
| 958 |
mainPreview.onload = () => {
|
|
|
|
| 1044 |
const result = await res.json();
|
| 1045 |
if (result.error) throw new Error(result.error);
|
| 1046 |
imageData[idx].result_url = result.result_url;
|
| 1047 |
+
imageData[idx].preview_url = result.preview_url;
|
| 1048 |
imageData[idx].status = 'done';
|
| 1049 |
imageData[idx].steps = {
|
| 1050 |
rmbg: document.getElementById('step-rmbg').checked,
|