File size: 6,867 Bytes
d7c28e9
8bb0c57
d7c28e9
8bb0c57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d7c28e9
8bb0c57
 
 
 
 
 
 
 
 
 
d7c28e9
8bb0c57
 
 
 
d7c28e9
 
 
1
2
3
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
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import gradio as gr
from PIL import Image, ImageFilter
import numpy as np

def lossless_compression(image, reduction_percent, target_size_kb, auto_mode):
    """
    Сжимает изображение без потерь качества, уменьшая количество пикселей и применяя алгоритмы для сохранения деталей.

    Args:
        image: Входное изображение (PIL Image).
        reduction_percent: Процент уменьшения пикселей по горизонтали (0-100).
        target_size_kb: Желаемый размер файла в килобайтах (используется в автоматическом режиме).
        auto_mode: Флаг автоматического режима (True/False).

    Returns:
        Сжатое изображение (PIL Image).
    """

    if auto_mode:
        # Автоматический подбор параметров сжатия
        reduction_percent, quality = auto_adjust_compression(image, target_size_kb)
    else:
        # Ручной режим, используем заданный процент уменьшения
        quality = 95  # Базовое качество JPEG, можно настроить

    # 1. Уменьшение количества пикселей по горизонтали
    width, height = image.size
    new_width = int(width * (1 - reduction_percent / 100))
    resized_image = image.resize((new_width, height), Image.LANCZOS)

    # 2. Растягивание обратно фильтром Ланцоша
    stretched_image = resized_image.resize((width, height), Image.LANCZOS)
    
    # 3. Адаптивное размытие (уменьшение шума и артефактов)
    blurred_image = adaptive_blur(stretched_image)

    # 4. Повышение резкости (компенсация размытия)
    sharpened_image = blurred_image.filter(ImageFilter.UnsharpMask(radius=1, percent=150, threshold=3))

    # 5. Сохранение с оптимизированными параметрами
    output_image = sharpened_image
    
    # Преобразование изображения в формат, пригодный для сохранения в буфер
    if output_image.mode != "RGB":
        output_image = output_image.convert("RGB")

    return output_image, reduction_percent, quality

def auto_adjust_compression(image, target_size_kb):
    """
    Автоматически подбирает параметры сжатия для достижения целевого размера файла.

    Args:
        image: Входное изображение (PIL Image).
        target_size_kb: Желаемый размер файла в килобайтах.

    Returns:
        Оптимальный процент уменьшения, оптимальное качество JPEG.
    """
    
    # Очень грубая эвристика для начальной оценки. Нужно тестировать и улучшать
    initial_reduction = min(50, int(100 * (1 - target_size_kb * 1024 / len(image.tobytes()))))
    reduction_percent = initial_reduction
    quality = 95

    # Итеративно подбираем параметры, если нужно. 
    # В реальном приложении здесь может быть более сложный алгоритм
    # с бинарным поиском или другими методами оптимизации.

    return reduction_percent, quality

def adaptive_blur(image):
    """
    Применяет адаптивное размытие к изображению.

    Args:
        image: Входное изображение (PIL Image).

    Returns:
        Изображение с адаптивным размытием (PIL Image).
    """
    # Преобразуем изображение в массив NumPy для анализа
    img_array = np.array(image)

    # Простой пример адаптивного размытия:
    # Сильнее размываем области с меньшим количеством деталей (меньше дисперсия)
    # Это очень базовый пример, нужно улучшать в зависимости от задачи
    std_dev = np.std(img_array)
    
    if std_dev < 20: # Порог стандартного отклонения - настраиваемый параметр
        # Применяем более сильное размытие
        blurred_image = image.filter(ImageFilter.GaussianBlur(radius=2))
    else:
        # Применяем слабое размытие
        blurred_image = image.filter(ImageFilter.GaussianBlur(radius=1))

    return blurred_image

def save_compressed_image(image, quality, filename="compressed_image.jpg"):
    """
    Сохраняет сжатое изображение с указанным качеством.

    Args:
      image: PIL Image.
      quality: Качество JPEG (0-100).
      filename: Имя файла для сохранения.
    """
    image.save(filename, "JPEG", optimize=True, quality=quality)

# Интерфейс Gradio
def gradio_interface(image, reduction_percent, target_size_kb, auto_mode):
    compressed_image, used_reduction, used_quality = lossless_compression(image, reduction_percent, target_size_kb, auto_mode)
    save_compressed_image(compressed_image, used_quality)
    return compressed_image, f"Использованное уменьшение: {used_reduction:.2f}%, Качество JPEG: {used_quality}"

iface = gr.Interface(
    fn=gradio_interface,
    inputs=[
        gr.Image(type="pil", label="Исходное изображение"),
        gr.Slider(0, 50, step=1, value=12, label="Процент уменьшения пикселей по горизонтали (ручной режим)"),
        gr.Slider(1, 200, step=1, value=50, label="Желаемый размер файла в KB (автоматический режим)"),
        gr.Checkbox(label="Автоматический режим"),
    ],
    outputs=[
        gr.Image(type="pil", label="Сжатое изображение"),
        gr.Textbox(label="Параметры сжатия")
    ],
    title="Сжатие изображений без потерь",
    description="Уменьшает размер изображения, сохраняя визуальное качество. "
                "Выберите автоматический режим или введите желаемый размер файла "
                "или процент уменьшения пикселей вручную.",
)

iface.launch()