Update app.py
Browse files
app.py
CHANGED
@@ -1,119 +1,134 @@
|
|
1 |
import gradio as gr
|
2 |
-
import
|
3 |
import numpy as np
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
def
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
iface = gr.Interface(
|
108 |
-
fn=
|
109 |
-
inputs=
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
|
|
|
|
|
|
115 |
],
|
116 |
-
|
|
|
|
|
|
|
117 |
)
|
118 |
|
119 |
iface.launch()
|
|
|
1 |
import gradio as gr
|
2 |
+
from PIL import Image, ImageFilter
|
3 |
import numpy as np
|
4 |
+
|
5 |
+
def lossless_compression(image, reduction_percent, target_size_kb, auto_mode):
|
6 |
+
"""
|
7 |
+
Сжимает изображение без потерь качества, уменьшая количество пикселей и применяя алгоритмы для сохранения деталей.
|
8 |
+
|
9 |
+
Args:
|
10 |
+
image: Входное изображение (PIL Image).
|
11 |
+
reduction_percent: Процент уменьшения пикселей по горизонтали (0-100).
|
12 |
+
target_size_kb: Желаемый размер файла в килобайтах (используется в автоматическом режиме).
|
13 |
+
auto_mode: Флаг автоматического режима (True/False).
|
14 |
+
|
15 |
+
Returns:
|
16 |
+
Сжатое изображение (PIL Image).
|
17 |
+
"""
|
18 |
+
|
19 |
+
if auto_mode:
|
20 |
+
# Автоматический подбор параметров сжатия
|
21 |
+
reduction_percent, quality = auto_adjust_compression(image, target_size_kb)
|
22 |
+
else:
|
23 |
+
# Ручной режим, используем заданный процент уменьшения
|
24 |
+
quality = 95 # Базовое качество JPEG, можно настроить
|
25 |
+
|
26 |
+
# 1. Уменьшение количества пикселей по горизонтали
|
27 |
+
width, height = image.size
|
28 |
+
new_width = int(width * (1 - reduction_percent / 100))
|
29 |
+
resized_image = image.resize((new_width, height), Image.LANCZOS)
|
30 |
+
|
31 |
+
# 2. Растягивание обратно фильтром Ланцоша
|
32 |
+
stretched_image = resized_image.resize((width, height), Image.LANCZOS)
|
33 |
+
|
34 |
+
# 3. Адаптивное размытие (уменьшение шума и артефактов)
|
35 |
+
blurred_image = adaptive_blur(stretched_image)
|
36 |
+
|
37 |
+
# 4. Повышение резкости (компенсация размытия)
|
38 |
+
sharpened_image = blurred_image.filter(ImageFilter.UnsharpMask(radius=1, percent=150, threshold=3))
|
39 |
+
|
40 |
+
# 5. Сохранение с оптимизированными параметрами
|
41 |
+
output_image = sharpened_image
|
42 |
+
|
43 |
+
# Преобразование изображения в формат, пригодный для сохранения в буфер
|
44 |
+
if output_image.mode != "RGB":
|
45 |
+
output_image = output_image.convert("RGB")
|
46 |
+
|
47 |
+
return output_image, reduction_percent, quality
|
48 |
+
|
49 |
+
def auto_adjust_compression(image, target_size_kb):
|
50 |
+
"""
|
51 |
+
Автоматически подбирает параметры сжатия для достижения целевого размера файла.
|
52 |
+
|
53 |
+
Args:
|
54 |
+
image: Входное изображение (PIL Image).
|
55 |
+
target_size_kb: Желаемый размер файла в килобайтах.
|
56 |
+
|
57 |
+
Returns:
|
58 |
+
Оптимальный процент уменьшения, оптимальное качество JPEG.
|
59 |
+
"""
|
60 |
+
|
61 |
+
# Очень грубая эвристика для начальной оценки. Нужно тестировать и улучшать
|
62 |
+
initial_reduction = min(50, int(100 * (1 - target_size_kb * 1024 / len(image.tobytes()))))
|
63 |
+
reduction_percent = initial_reduction
|
64 |
+
quality = 95
|
65 |
+
|
66 |
+
# Итеративно подбираем параметры, если нужно.
|
67 |
+
# В реальном приложении здесь может быть более сложный алгоритм
|
68 |
+
# с бинарным поиском или другими методами оптимизации.
|
69 |
+
|
70 |
+
return reduction_percent, quality
|
71 |
+
|
72 |
+
def adaptive_blur(image):
|
73 |
+
"""
|
74 |
+
Применяет адаптивное размытие к изображению.
|
75 |
+
|
76 |
+
Args:
|
77 |
+
image: Входное изображение (PIL Image).
|
78 |
+
|
79 |
+
Returns:
|
80 |
+
Изображение с адаптивным размытием (PIL Image).
|
81 |
+
"""
|
82 |
+
# Преобразуем изображение в массив NumPy для анализа
|
83 |
+
img_array = np.array(image)
|
84 |
+
|
85 |
+
# Простой пример адаптивного размытия:
|
86 |
+
# Сильнее размываем области с меньшим количеством деталей (меньше дисперсия)
|
87 |
+
# Это очень базовый пример, нужно улучшать в зависимости от задачи
|
88 |
+
std_dev = np.std(img_array)
|
89 |
+
|
90 |
+
if std_dev < 20: # Порог стандартного отклонения - настраиваемый параметр
|
91 |
+
# Применяем более сильное размытие
|
92 |
+
blurred_image = image.filter(ImageFilter.GaussianBlur(radius=2))
|
93 |
+
else:
|
94 |
+
# Применяем слабое размытие
|
95 |
+
blurred_image = image.filter(ImageFilter.GaussianBlur(radius=1))
|
96 |
+
|
97 |
+
return blurred_image
|
98 |
+
|
99 |
+
def save_compressed_image(image, quality, filename="compressed_image.jpg"):
|
100 |
+
"""
|
101 |
+
Сохраняет сжатое изображение с указанным качеством.
|
102 |
+
|
103 |
+
Args:
|
104 |
+
image: PIL Image.
|
105 |
+
quality: Качество JPEG (0-100).
|
106 |
+
filename: Имя файла для сохранения.
|
107 |
+
"""
|
108 |
+
image.save(filename, "JPEG", optimize=True, quality=quality)
|
109 |
+
|
110 |
+
# Интерфейс Gradio
|
111 |
+
def gradio_interface(image, reduction_percent, target_size_kb, auto_mode):
|
112 |
+
compressed_image, used_reduction, used_quality = lossless_compression(image, reduction_percent, target_size_kb, auto_mode)
|
113 |
+
save_compressed_image(compressed_image, used_quality)
|
114 |
+
return compressed_image, f"Использованное уменьшение: {used_reduction:.2f}%, Качество JPEG: {used_quality}"
|
115 |
+
|
116 |
iface = gr.Interface(
|
117 |
+
fn=gradio_interface,
|
118 |
+
inputs=[
|
119 |
+
gr.Image(type="pil", label="Исходное изображение"),
|
120 |
+
gr.Slider(0, 50, step=1, value=12, label="Процент уменьшения пикселей по горизонтали (ручной режим)"),
|
121 |
+
gr.Slider(1, 200, step=1, value=50, label="Желаемый размер файла в KB (автоматический режим)"),
|
122 |
+
gr.Checkbox(label="Автоматический режим"),
|
123 |
+
],
|
124 |
+
outputs=[
|
125 |
+
gr.Image(type="pil", label="Сжатое изображение"),
|
126 |
+
gr.Textbox(label="Параметры сжатия")
|
127 |
],
|
128 |
+
title="Сжатие изображений без потерь",
|
129 |
+
description="Уменьшает размер изображения, сохраняя визуальное качество. "
|
130 |
+
"Выберите автоматический режим или введите желаемый размер файла "
|
131 |
+
"или процент уменьшения пикселей вручную.",
|
132 |
)
|
133 |
|
134 |
iface.launch()
|