Внёс изменения в графический интерфейс

теперь есть возможность использовать метод DCT
This commit is contained in:
Likaon
2026-05-23 21:33:25 +03:00
parent 1f04fb3c2c
commit f053e2becc
2 changed files with 238 additions and 104 deletions

View File

@@ -1,6 +1,7 @@
""" """
Модуль DCT стеганографии с квантованием (JPEG-style) Модуль DCT стеганографии с квантованием (JPEG-style)
Оптимизирован для длинных сообщений Поддержка цветных изображений (RGB)
Оптимизирован для GUI
""" """
import numpy as np import numpy as np
@@ -10,16 +11,16 @@ from .utils import text_to_bits, bits_to_text
BLOCK_SIZE = 8 BLOCK_SIZE = 8
# Более мягкая таблица квантования (качество ~70-80%) # Таблица квантования JPEG (качество 50%)
QUANT_TABLE = np.array([ QUANT_TABLE = np.array([
[8, 6, 5, 8, 12, 20, 26, 31], [16, 11, 10, 16, 24, 40, 51, 61],
[6, 6, 7, 10, 13, 29, 30, 28], [12, 12, 14, 19, 26, 58, 60, 55],
[7, 7, 8, 12, 20, 29, 35, 28], [14, 13, 16, 24, 40, 57, 69, 56],
[7, 9, 11, 15, 26, 44, 40, 31], [14, 17, 22, 29, 51, 87, 80, 62],
[9, 11, 19, 28, 34, 55, 52, 39], [18, 22, 37, 56, 68, 109, 103, 77],
[12, 18, 28, 32, 41, 52, 57, 46], [24, 35, 55, 64, 81, 104, 113, 92],
[25, 32, 39, 44, 52, 61, 60, 51], [49, 64, 78, 87, 103, 121, 120, 101],
[36, 46, 48, 49, 56, 50, 52, 50] [72, 92, 95, 98, 112, 100, 103, 99]
]) ])
@@ -41,8 +42,8 @@ def _get_zigzag_order() -> list:
ZIGZAG_ORDER = _get_zigzag_order() ZIGZAG_ORDER = _get_zigzag_order()
# Используем больше коэффициентов (1-50) # Используем среднечастотные коэффициенты
MID_FREQ_INDICES = ZIGZAG_ORDER[1:50] MID_FREQ_INDICES = ZIGZAG_ORDER[10:40]
def _dct_quantize(block: np.ndarray) -> np.ndarray: def _dct_quantize(block: np.ndarray) -> np.ndarray:
@@ -99,6 +100,12 @@ def encode_dct(image_path: str, message: str, output_path: str) -> bool:
pixels = np.array(img, dtype=np.float64) pixels = np.array(img, dtype=np.float64)
height, width, channels = pixels.shape height, width, channels = pixels.shape
# Проверка размеров
if height % BLOCK_SIZE != 0 or width % BLOCK_SIZE != 0:
print(f"Ошибка: размеры {width}x{height} не кратны {BLOCK_SIZE}")
return False
# Формат: [длина: 32 бита] + [сообщение]
msg_bytes = message.encode('utf-8') msg_bytes = message.encode('utf-8')
msg_length = len(msg_bytes) msg_length = len(msg_bytes)
length_bits = format(msg_length, '032b') length_bits = format(msg_length, '032b')
@@ -107,6 +114,7 @@ def encode_dct(image_path: str, message: str, output_path: str) -> bool:
bit_list = [int(b) for b in all_bits] bit_list = [int(b) for b in all_bits]
total_bits = len(bit_list) total_bits = len(bit_list)
# Проверка вместимости
blocks_per_row = width // BLOCK_SIZE blocks_per_row = width // BLOCK_SIZE
blocks_per_col = height // BLOCK_SIZE blocks_per_col = height // BLOCK_SIZE
max_bits = blocks_per_row * blocks_per_col * len(MID_FREQ_INDICES) * 3 max_bits = blocks_per_row * blocks_per_col * len(MID_FREQ_INDICES) * 3
@@ -149,6 +157,10 @@ def decode_dct(image_path: str) -> str:
pixels = np.array(img, dtype=np.float64) pixels = np.array(img, dtype=np.float64)
height, width, channels = pixels.shape height, width, channels = pixels.shape
if height % BLOCK_SIZE != 0 or width % BLOCK_SIZE != 0:
return ""
# Извлекаем все биты
all_bits = [] all_bits = []
for i in range(0, height - BLOCK_SIZE + 1, BLOCK_SIZE): for i in range(0, height - BLOCK_SIZE + 1, BLOCK_SIZE):
@@ -159,6 +171,7 @@ def decode_dct(image_path: str) -> str:
bits_from_block = _extract_bits_from_block(quant_block, len(MID_FREQ_INDICES)) bits_from_block = _extract_bits_from_block(quant_block, len(MID_FREQ_INDICES))
all_bits.extend(bits_from_block) all_bits.extend(bits_from_block)
# Читаем длину сообщения (первые 32 бита)
if len(all_bits) < 32: if len(all_bits) < 32:
return "" return ""
@@ -166,15 +179,13 @@ def decode_dct(image_path: str) -> str:
length_str = ''.join(str(b) for b in length_bits) length_str = ''.join(str(b) for b in length_bits)
msg_length = int(length_str, 2) msg_length = int(length_str, 2)
if msg_length > (len(all_bits) - 32) // 8: # Проверка
if msg_length <= 0 or msg_length > (len(all_bits) - 32) // 8:
return "" return ""
# Читаем сообщение
message_bits = all_bits[32:32 + msg_length * 8] message_bits = all_bits[32:32 + msg_length * 8]
remainder = len(message_bits) % 8
if remainder != 0:
message_bits.extend([0] * (8 - remainder))
if len(message_bits) == 0: if len(message_bits) == 0:
return "" return ""
@@ -182,5 +193,5 @@ def decode_dct(image_path: str) -> str:
try: try:
return bits_to_text(bits_string) return bits_to_text(bits_string)
except Exception as e: except Exception as e:
print(f"Ошибка при декодировании: {e}") print(f"Ошибка декодирования: {e}")
return "" return ""

View File

@@ -4,7 +4,6 @@ GUI интерфейс для стеганографического прило
import tkinter as tk import tkinter as tk
from tkinter import ttk, filedialog, messagebox from tkinter import ttk, filedialog, messagebox
from PIL import Image, ImageTk
import threading import threading
import os import os
import sys import sys
@@ -22,15 +21,16 @@ class StegoGUI:
def __init__(self, root): def __init__(self, root):
self.root = root self.root = root
self.root.title("Stego App - Стеганография") self.root.title("Stego App - Стеганографическое приложение")
self.root.geometry("750x650") self.root.geometry("800x700")
self.root.resizable(True, True) self.root.resizable(True, True)
# Переменные # Переменные
self.image_path = tk.StringVar() self.encode_image_path = tk.StringVar()
self.output_path = tk.StringVar() self.encode_output_path = tk.StringVar()
self.method = tk.StringVar(value="lsb") self.decode_image_path = tk.StringVar()
self.mode = tk.StringVar(value="encode") self.method_encode = tk.StringVar(value="lsb")
self.method_decode = tk.StringVar(value="lsb")
self.setup_ui() self.setup_ui()
@@ -60,27 +60,45 @@ class StegoGUI:
row = 0 row = 0
# Метод # Метод
ttk.Label(self.encode_frame, text="Метод:").grid(row=row, column=0, sticky='w', padx=5, pady=5) ttk.Label(self.encode_frame, text="Метод стеганографии:").grid(row=row, column=0, sticky='w', padx=5, pady=5)
ttk.Radiobutton(self.encode_frame, text="LSB", variable=self.method, value="lsb").grid(row=row, column=1, padx=5) ttk.Radiobutton(self.encode_frame, text="LSB (рекомендуется)", variable=self.method_encode, value="lsb").grid(row=row, column=1, padx=5, sticky='w')
ttk.Radiobutton(self.encode_frame, text="DCT", variable=self.method, value="dct").grid(row=row, column=2, padx=5) ttk.Radiobutton(self.encode_frame, text="DCT (для гладких/одноцветных изображений)", variable=self.method_encode, value="dct").grid(row=row, column=2, padx=5, sticky='w')
row += 1
# Разделитель
ttk.Separator(self.encode_frame, orient='horizontal').grid(row=row, column=0, columnspan=4, sticky='ew', padx=5, pady=5)
row += 1 row += 1
# Исходное изображение # Исходное изображение
ttk.Label(self.encode_frame, text="Исходное изображение:").grid(row=row, column=0, sticky='w', padx=5, pady=5) ttk.Label(self.encode_frame, text="Исходное изображение:").grid(row=row, column=0, sticky='w', padx=5, pady=5)
ttk.Entry(self.encode_frame, textvariable=self.image_path, width=50).grid(row=row, column=1, columnspan=2, padx=5) self.encode_image_entry = ttk.Entry(self.encode_frame, textvariable=self.encode_image_path, width=55)
ttk.Button(self.encode_frame, text="Обзор...", command=self.select_image).grid(row=row, column=3, padx=5) self.encode_image_entry.grid(row=row, column=1, columnspan=2, padx=5, sticky='ew')
ttk.Button(self.encode_frame, text="Обзор...", command=self.select_encode_image).grid(row=row, column=3, padx=5)
row += 1 row += 1
# Выходное изображение # Выходное изображение
ttk.Label(self.encode_frame, text="Сохранить как:").grid(row=row, column=0, sticky='w', padx=5, pady=5) ttk.Label(self.encode_frame, text="Сохранить как (PNG):").grid(row=row, column=0, sticky='w', padx=5, pady=5)
ttk.Entry(self.encode_frame, textvariable=self.output_path, width=50).grid(row=row, column=1, columnspan=2, padx=5) self.encode_output_entry = ttk.Entry(self.encode_frame, textvariable=self.encode_output_path, width=55)
ttk.Button(self.encode_frame, text="Обзор...", command=self.select_output).grid(row=row, column=3, padx=5) self.encode_output_entry.grid(row=row, column=1, columnspan=2, padx=5, sticky='ew')
ttk.Button(self.encode_frame, text="Обзор...", command=self.select_encode_output).grid(row=row, column=3, padx=5)
row += 1
# Разделитель
ttk.Separator(self.encode_frame, orient='horizontal').grid(row=row, column=0, columnspan=4, sticky='ew', padx=5, pady=5)
row += 1 row += 1
# Сообщение # Сообщение
ttk.Label(self.encode_frame, text="Сообщение:").grid(row=row, column=0, sticky='nw', padx=5, pady=5) ttk.Label(self.encode_frame, text="Сообщение для сокрытия:").grid(row=row, column=0, sticky='nw', padx=5, pady=5)
self.message_text = tk.Text(self.encode_frame, height=10, width=60) self.message_text = tk.Text(self.encode_frame, height=8, width=65, wrap=tk.WORD)
self.message_text.grid(row=row, column=1, columnspan=3, padx=5, pady=5) self.message_text.grid(row=row, column=1, columnspan=3, padx=5, pady=5, sticky='ew')
row += 1
# Кнопка очистки сообщения
ttk.Button(self.encode_frame, text="Очистить сообщение", command=self.clear_message).grid(row=row, column=1, sticky='w', padx=5, pady=5)
row += 1
# Разделитель
ttk.Separator(self.encode_frame, orient='horizontal').grid(row=row, column=0, columnspan=4, sticky='ew', padx=5, pady=5)
row += 1 row += 1
# Вместимость # Вместимость
@@ -88,20 +106,20 @@ class StegoGUI:
self.capacity_label.grid(row=row, column=1, columnspan=2, sticky='w', padx=5) self.capacity_label.grid(row=row, column=1, columnspan=2, sticky='w', padx=5)
row += 1 row += 1
# Прогресс
self.encode_progress = ttk.Progressbar(self.encode_frame, mode='indeterminate', length=400)
# Кнопка # Кнопка
self.encode_btn = ttk.Button(self.encode_frame, text="Скрыть сообщение", command=self.do_encode) self.encode_btn = ttk.Button(self.encode_frame, text="Скрыть сообщение", command=self.do_encode)
self.encode_btn.grid(row=row, column=1, columnspan=2, pady=10) self.encode_btn.grid(row=row, column=1, columnspan=2, pady=15)
row += 1 row += 1
# Статус # Статус
self.status_label = ttk.Label(self.encode_frame, text="", foreground="blue") self.status_label = ttk.Label(self.encode_frame, text="", foreground="blue")
self.status_label.grid(row=row, column=0, columnspan=4, pady=5) self.status_label.grid(row=row, column=0, columnspan=4, pady=5)
# Прогресс # Привязка событий
self.progress = ttk.Progressbar(self.encode_frame, mode='indeterminate') self.encode_image_path.trace('w', lambda *args: self.update_capacity())
# Привязка события для обновления вместимости
self.image_path.trace('w', lambda *args: self.update_capacity())
def setup_decode_tab(self): def setup_decode_tab(self):
"""Вкладка декодирования""" """Вкладка декодирования"""
@@ -109,30 +127,47 @@ class StegoGUI:
# Метод # Метод
ttk.Label(self.decode_frame, text="Метод:").grid(row=row, column=0, sticky='w', padx=5, pady=5) ttk.Label(self.decode_frame, text="Метод:").grid(row=row, column=0, sticky='w', padx=5, pady=5)
ttk.Radiobutton(self.decode_frame, text="LSB", variable=self.method, value="lsb").grid(row=row, column=1, padx=5) ttk.Radiobutton(self.decode_frame, text="LSB (рекомендуется)", variable=self.method_decode, value="lsb").grid(row=row, column=1, padx=5, sticky='w')
ttk.Radiobutton(self.decode_frame, text="DCT", variable=self.method, value="dct").grid(row=row, column=2, padx=5) ttk.Radiobutton(self.decode_frame, text="DCT (для гладких/одноцветных изображений)", variable=self.method_decode, value="dct").grid(row=row, column=2, padx=5, sticky='w')
row += 1
# Разделитель
ttk.Separator(self.decode_frame, orient='horizontal').grid(row=row, column=0, columnspan=4, sticky='ew', padx=5, pady=5)
row += 1 row += 1
# Изображение # Изображение
ttk.Label(self.decode_frame, text="Изображение со скрытым сообщением:").grid(row=row, column=0, sticky='w', padx=5, pady=5) ttk.Label(self.decode_frame, text="Изображение со скрытым сообщением:").grid(row=row, column=0, sticky='w', padx=5, pady=5)
ttk.Entry(self.decode_frame, textvariable=self.image_path, width=50).grid(row=row, column=1, columnspan=2, padx=5) self.decode_image_entry = ttk.Entry(self.decode_frame, textvariable=self.decode_image_path, width=55)
ttk.Button(self.decode_frame, text="Обзор...", command=self.select_image).grid(row=row, column=3, padx=5) self.decode_image_entry.grid(row=row, column=1, columnspan=2, padx=5, sticky='ew')
ttk.Button(self.decode_frame, text="Обзор...", command=self.select_decode_image).grid(row=row, column=3, padx=5)
row += 1 row += 1
# Разделитель
ttk.Separator(self.decode_frame, orient='horizontal').grid(row=row, column=0, columnspan=4, sticky='ew', padx=5, pady=5)
row += 1
# Прогресс
self.decode_progress = ttk.Progressbar(self.decode_frame, mode='indeterminate', length=400)
# Кнопка # Кнопка
self.decode_btn = ttk.Button(self.decode_frame, text="Извлечь сообщение", command=self.do_decode) self.decode_btn = ttk.Button(self.decode_frame, text="Извлечь сообщение", command=self.do_decode)
self.decode_btn.grid(row=row, column=1, columnspan=2, pady=10) self.decode_btn.grid(row=row, column=1, columnspan=2, pady=15)
row += 1 row += 1
# Результат # Результат
ttk.Label(self.decode_frame, text="Извлеченное сообщение:").grid(row=row, column=0, sticky='nw', padx=5, pady=5) ttk.Label(self.decode_frame, text="Извлеченное сообщение:").grid(row=row, column=0, sticky='nw', padx=5, pady=5)
self.result_text = tk.Text(self.decode_frame, height=10, width=60) self.result_text = tk.Text(self.decode_frame, height=8, width=65, wrap=tk.WORD)
self.result_text.grid(row=row, column=1, columnspan=3, padx=5, pady=5) self.result_text.grid(row=row, column=1, columnspan=3, padx=5, pady=5, sticky='ew')
row += 1 row += 1
# Кнопка сохранения # Кнопки
self.save_btn = ttk.Button(self.decode_frame, text="Сохранить в файл", command=self.save_result, state='disabled') button_frame = ttk.Frame(self.decode_frame)
self.save_btn.grid(row=row, column=1, columnspan=2, pady=5) button_frame.grid(row=row, column=1, columnspan=2, pady=5)
self.save_btn = ttk.Button(button_frame, text="Сохранить в файл", command=self.save_result, state='disabled')
self.save_btn.pack(side='left', padx=5)
ttk.Button(button_frame, text="Очистить результат", command=self.clear_result).pack(side='left', padx=5)
row += 1 row += 1
# Статус # Статус
@@ -143,55 +178,121 @@ class StegoGUI:
def setup_about_tab(self, frame): def setup_about_tab(self, frame):
"""Вкладка 'О программе'""" """Вкладка 'О программе'"""
ttk.Label(frame, text="Stego App", font=("Arial", 16)).pack(pady=10) # Основная информация
ttk.Label(frame, text="Стеганографическое приложение").pack() info_frame = ttk.Frame(frame)
ttk.Label(frame, text="").pack() info_frame.pack(pady=20)
ttk.Label(frame, text="Методы:").pack()
ttk.Label(frame, text=" • LSB (Least Significant Bit) - сокрытие в последних битах пикселей").pack()
ttk.Label(frame, text=" • DCT (Discrete Cosine Transform) - сокрытие в частотной области").pack()
ttk.Label(frame, text="").pack()
ttk.Label(frame, text="Поддерживаемые форматы: PNG, BMP, JPG").pack()
ttk.Label(frame, text="").pack()
ttk.Label(frame, text=" Курсовая работа по стеганографии").pack()
ttk.Label(frame, text="Петров Игорь ИУ10-24").pack()
def select_image(self): ttk.Label(info_frame, text="Stego App", font=("Arial", 16, "bold")).pack(pady=5)
"""Выбор изображения""" ttk.Label(info_frame, text="Стеганографическое приложение", font=("Arial", 10)).pack(pady=2)
ttk.Label(info_frame, text="").pack(pady=5)
ttk.Label(info_frame, text="Петров Игорь ИУ10-24").pack()
ttk.Label(info_frame, text="").pack(pady=5)
# Год
ttk.Label(info_frame, text="2026", font=("Arial", 10, "italic")).pack(pady=5)
# Разделитель
ttk.Separator(frame, orient='horizontal').pack(fill='x', padx=20, pady=10)
# Техническая информация
tech_frame = ttk.Frame(frame)
tech_frame.pack(pady=10)
ttk.Label(tech_frame, text="Методы стеганографии:", font=("Arial", 10, "bold")).pack(anchor='w')
ttk.Label(tech_frame, text=" • LSB (Least Significant Bit) - сокрытие в последних битах пикселей").pack(anchor='w')
ttk.Label(tech_frame, text=" Работает с любыми изображениями, высокая вместимость").pack(anchor='w', padx=20)
ttk.Label(tech_frame, text="").pack()
ttk.Label(tech_frame, text=" • DCT (Discrete Cosine Transform) - сокрытие в частотной области").pack(anchor='w')
ttk.Label(tech_frame, text=" Рекомендуется для гладких/однотонных изображений").pack(anchor='w', padx=20)
ttk.Label(tech_frame, text="").pack()
ttk.Label(tech_frame, text="Поддерживаемые форматы:", font=("Arial", 10, "bold")).pack(anchor='w')
ttk.Label(tech_frame, text=" • PNG, BMP, JPG, JPEG").pack(anchor='w')
ttk.Label(tech_frame, text="").pack()
ttk.Label(tech_frame, text="Рекомендации:", font=("Arial", 10, "bold")).pack(anchor='w')
ttk.Label(tech_frame, text=" • Для сохранения результата используйте PNG").pack(anchor='w')
ttk.Label(tech_frame, text=" • LSB - универсальный метод, рекомендуется по умолчанию").pack(anchor='w')
def select_encode_image(self):
"""Выбор изображения для кодирования"""
filename = filedialog.askopenfilename( filename = filedialog.askopenfilename(
title="Выберите изображение", title="Выберите исходное изображение",
filetypes=[("Изображения", "*.png *.bmp *.jpg *.jpeg"), ("Все файлы", "*.*")] filetypes=[("Изображения", "*.png *.bmp *.jpg *.jpeg"), ("Все файлы", "*.*")]
) )
if filename: if filename:
self.image_path.set(filename) self.encode_image_path.set(filename)
def select_output(self): def select_encode_output(self):
"""Выбор выходного файла""" """Выбор выходного файла"""
filename = filedialog.asksaveasfilename( filename = filedialog.asksaveasfilename(
title="Сохранить как", title="Сохранить результат как",
defaultextension=".png", defaultextension=".png",
filetypes=[("PNG", "*.png"), ("BMP", "*.bmp"), ("Все файлы", "*.*")] filetypes=[("PNG", "*.png"), ("BMP", "*.bmp"), ("Все файлы", "*.*")]
) )
if filename: if filename:
self.output_path.set(filename) self.encode_output_path.set(filename)
def select_decode_image(self):
"""Выбор изображения для декодирования"""
filename = filedialog.askopenfilename(
title="Выберите изображение со скрытым сообщением",
filetypes=[("Изображения", "*.png *.bmp *.jpg *.jpeg"), ("Все файлы", "*.*")]
)
if filename:
self.decode_image_path.set(filename)
def clear_message(self):
"""Очистка поля сообщения"""
self.message_text.delete("1.0", tk.END)
def clear_result(self):
"""Очистка поля результата"""
self.result_text.delete("1.0", tk.END)
self.decoded_message = ""
self.save_btn.config(state='disabled')
def update_capacity(self): def update_capacity(self):
"""Обновление информации о вместимости""" """Обновление информации о вместимости"""
path = self.image_path.get() path = self.encode_image_path.get()
if path and os.path.exists(path): if path and os.path.exists(path):
try: try:
capacity = calculate_capacity(path) capacity = calculate_capacity(path)
self.capacity_label.config(text=f"Максимальная вместимость: {capacity} байт (~{capacity/1024:.1f} КБ)") kb = capacity / 1024
self.capacity_label.config(text=f"📊 Максимальная вместимость: {capacity} байт (~{kb:.1f} КБ)")
except Exception as e: except Exception as e:
self.capacity_label.config(text=f"Ошибка: {e}") self.capacity_label.config(text=f"⚠️ Ошибка: {e}")
else: else:
self.capacity_label.config(text="") self.capacity_label.config(text="")
def check_image_for_dct(self, image_path):
"""Проверка изображения для DCT метода"""
try:
from PIL import Image, ImageStat
img = Image.open(image_path)
w, h = img.size
# Проверка размеров
if w % 8 != 0 or h % 8 != 0:
return False, f"Размеры изображения ({w}x{h}) должны быть кратны 8 для DCT метода"
# Проверка "гладкости" изображения
stat = ImageStat.Stat(img)
# Если стандартное отклонение цветов большое (>50) — изображение пёстрое
if max(stat.stddev) > 50:
return False, "Изображение содержит много деталей/цветов.\nDCT рекомендуется только для гладких/однотонных изображений.\nИспользуйте LSB метод."
return True, ""
except Exception as e:
return False, str(e)
def do_encode(self): def do_encode(self):
"""Выполнение кодирования""" """Выполнение кодирования"""
image = self.image_path.get() image = self.encode_image_path.get()
output = self.output_path.get() output = self.encode_output_path.get()
message = self.message_text.get("1.0", tk.END).strip() message = self.message_text.get("1.0", tk.END).strip()
method = self.method.get() method = self.method_encode.get()
if not image: if not image:
messagebox.showerror("Ошибка", "Выберите исходное изображение") messagebox.showerror("Ошибка", "Выберите исходное изображение")
@@ -203,11 +304,18 @@ class StegoGUI:
messagebox.showerror("Ошибка", "Введите сообщение для сокрытия") messagebox.showerror("Ошибка", "Введите сообщение для сокрытия")
return return
# Проверка для DCT
if method == 'dct':
is_ok, error_msg = self.check_image_for_dct(image)
if not is_ok:
messagebox.showerror("Ошибка", error_msg)
return
# Запускаем в отдельном потоке # Запускаем в отдельном потоке
self.encode_btn.config(state='disabled') self.encode_btn.config(state='disabled')
self.progress.grid(row=5, column=1, columnspan=2, sticky='we', padx=5, pady=5) self.encode_progress.grid(row=11, column=1, columnspan=2, sticky='ew', padx=5, pady=5)
self.progress.start() self.encode_progress.start()
self.status_label.config(text="Кодирование...") self.status_label.config(text="Кодирование... Пожалуйста, подождите", foreground="blue")
thread = threading.Thread(target=self._do_encode, args=(image, output, message, method)) thread = threading.Thread(target=self._do_encode, args=(image, output, message, method))
thread.daemon = True thread.daemon = True
@@ -221,34 +329,38 @@ class StegoGUI:
else: else:
success = encode_dct(image, message, output) success = encode_dct(image, message, output)
self.root.after(0, self._encode_done, success) self.root.after(0, self._encode_done, success, None)
except Exception as e: except Exception as e:
self.root.after(0, self._encode_done, False, str(e)) self.root.after(0, self._encode_done, False, str(e))
def _encode_done(self, success, error=None): def _encode_done(self, success, error=None):
"""Завершение кодирования""" """Завершение кодирования"""
self.progress.stop() self.encode_progress.stop()
self.progress.grid_forget() self.encode_progress.grid_forget()
self.encode_btn.config(state='normal') self.encode_btn.config(state='normal')
if success: if success:
self.status_label.config(text="✅ Кодирование успешно завершено!", foreground="green") self.status_label.config(text="✅ Кодирование успешно завершено!", foreground="green")
messagebox.showinfo("Успех", "Сообщение успешно скрыто в изображении!") messagebox.showinfo("Успех", f"Сообщение успешно скрыто в изображении!\n\nСохранено: {self.encode_output_path.get()}")
else: else:
self.status_label.config(text=f"❌ Ошибка: {error or 'Кодирование не удалось'}", foreground="red") error_msg = error or "Неизвестная ошибка"
messagebox.showerror("Ошибка", error or "Не удалось скрыть сообщение") self.status_label.config(text=f"Ошибка кодирования", foreground="red")
messagebox.showerror("Ошибка", f"Не удалось скрыть сообщение:\n{error_msg}")
def do_decode(self): def do_decode(self):
"""Выполнение декодирования""" """Выполнение декодирования"""
image = self.image_path.get() image = self.decode_image_path.get()
method = self.method.get() method = self.method_decode.get()
if not image: if not image:
messagebox.showerror("Ошибка", "Выберите изображение") messagebox.showerror("Ошибка", "Выберите изображение")
return return
# Запускаем в отдельном потоке
self.decode_btn.config(state='disabled') self.decode_btn.config(state='disabled')
self.decode_status.config(text="Декодирование...") self.decode_progress.grid(row=2, column=1, columnspan=2, sticky='ew', padx=5, pady=5)
self.decode_progress.start()
self.decode_status.config(text="⏳ Декодирование... Пожалуйста, подождите", foreground="blue")
thread = threading.Thread(target=self._do_decode, args=(image, method)) thread = threading.Thread(target=self._do_decode, args=(image, method))
thread.daemon = True thread.daemon = True
@@ -262,35 +374,46 @@ class StegoGUI:
else: else:
message = decode_dct(image) message = decode_dct(image)
self.root.after(0, self._decode_done, message) self.root.after(0, self._decode_done, message, None)
except Exception as e: except Exception as e:
self.root.after(0, self._decode_done, None, str(e)) self.root.after(0, self._decode_done, None, str(e))
def _decode_done(self, message, error=None): def _decode_done(self, message, error=None):
"""Завершение декодирования""" """Завершение декодирования"""
self.decode_progress.stop()
self.decode_progress.grid_forget()
self.decode_btn.config(state='normal') self.decode_btn.config(state='normal')
if message and not error: if message and not error and message != "":
self.decoded_message = message self.decoded_message = message
self.result_text.delete("1.0", tk.END) self.result_text.delete("1.0", tk.END)
self.result_text.insert("1.0", message) self.result_text.insert("1.0", message)
self.save_btn.config(state='normal') self.save_btn.config(state='normal')
self.decode_status.config(text="✅ Декодирование успешно завершено!", foreground="green") self.decode_status.config(text="✅ Декодирование успешно завершено!", foreground="green")
messagebox.showinfo("Успех", f"✅ Сообщение успешно извлечено!\n\nДлина: {len(message)} символов")
else: else:
self.decode_status.config(text=f"❌ Ошибка: {error or 'Не удалось извлечь сообщение'}", foreground="red") error_msg = error or "Сообщение не найдено или повреждено"
self.decode_status.config(text=f"❌ Ошибка декодирования", foreground="red")
self.save_btn.config(state='disabled') self.save_btn.config(state='disabled')
messagebox.showerror("Ошибка", f"Не удалось извлечь сообщение:\n{error_msg}")
def save_result(self): def save_result(self):
"""Сохранение извлеченного сообщения в файл""" """Сохранение извлеченного сообщения в файл"""
if not self.decoded_message:
return
filename = filedialog.asksaveasfilename( filename = filedialog.asksaveasfilename(
title="Сохранить сообщение", title="Сохранить сообщение",
defaultextension=".txt", defaultextension=".txt",
filetypes=[("Текстовые файлы", "*.txt"), ("Все файлы", "*.*")] filetypes=[("Текстовые файлы", "*.txt"), ("Все файлы", "*.*")]
) )
if filename and self.decoded_message: if filename:
with open(filename, 'w', encoding='utf-8') as f: try:
f.write(self.decoded_message) with open(filename, 'w', encoding='utf-8') as f:
messagebox.showinfo("Успех", f"Сообщение сохранено в {filename}") f.write(self.decoded_message)
messagebox.showinfo("Успех", f"✅ Сообщение сохранено в:\n{filename}")
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось сохранить файл:\n{e}")
def main(): def main():