Fix decode progress - add Argon2 phase tracking
- decode.py now writes 20% "initializing" before Argon2 - decode.py writes 25% "extracting" after Argon2 completes - DCT extraction scales from 25-70% (was 5-70%) - Removed duplicate "loading" writes that caused backwards jumps Progress flow: 15% reading -> 20% initializing (Argon2) -> 25-70% extracting -> 75-95% decoding -> 100% complete Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1198,7 +1198,7 @@ def _extract_scipy_dct_safe(
|
|||||||
progress_file: str | None = None,
|
progress_file: str | None = None,
|
||||||
) -> bytes:
|
) -> bytes:
|
||||||
"""Extract using safe DCT operations with vectorized processing."""
|
"""Extract using safe DCT operations with vectorized processing."""
|
||||||
_write_progress(progress_file, 0, 100, "loading")
|
# Progress starts at 25% (decode.py writes 20% for Argon2, 25% before extraction)
|
||||||
|
|
||||||
img = Image.open(io.BytesIO(stego_image))
|
img = Image.open(io.BytesIO(stego_image))
|
||||||
width, height = img.size
|
width, height = img.size
|
||||||
@@ -1264,9 +1264,10 @@ def _extract_scipy_dct_safe(
|
|||||||
del blocks, dct_blocks, coeffs, quantized
|
del blocks, dct_blocks, coeffs, quantized
|
||||||
block_idx = batch_end
|
block_idx = batch_end
|
||||||
|
|
||||||
# Report progress (scale to 5-70% range, RS decode gets 70-100%)
|
# Report progress (scale to 25-70% range, RS decode gets 70-100%)
|
||||||
|
# Starts at 25% because decode.py writes 25% before calling extraction
|
||||||
if progress_file and block_idx % PROGRESS_INTERVAL < BATCH_SIZE:
|
if progress_file and block_idx % PROGRESS_INTERVAL < BATCH_SIZE:
|
||||||
extract_pct = 5 + int(65 * block_idx / num_blocks)
|
extract_pct = 25 + int(45 * block_idx / num_blocks)
|
||||||
_write_progress(progress_file, extract_pct, 100, "extracting")
|
_write_progress(progress_file, extract_pct, 100, "extracting")
|
||||||
|
|
||||||
# Check if we have enough bits (early exit)
|
# Check if we have enough bits (early exit)
|
||||||
@@ -1382,7 +1383,7 @@ def _extract_jpegio(
|
|||||||
"""Extract using jpegio for JPEG images."""
|
"""Extract using jpegio for JPEG images."""
|
||||||
import os
|
import os
|
||||||
|
|
||||||
_write_progress(progress_file, 0, 100, "loading")
|
# Progress starts at 25% (decode.py writes 20% for Argon2, 25% before extraction)
|
||||||
|
|
||||||
# Normalize JPEG to avoid crashes with quality=100 images
|
# Normalize JPEG to avoid crashes with quality=100 images
|
||||||
# (shouldn't happen with stego images, but be defensive)
|
# (shouldn't happen with stego images, but be defensive)
|
||||||
@@ -1397,7 +1398,7 @@ def _extract_jpegio(
|
|||||||
all_positions = _jpegio_get_usable_positions(coef_array)
|
all_positions = _jpegio_get_usable_positions(coef_array)
|
||||||
order = _jpegio_generate_order(len(all_positions), seed)
|
order = _jpegio_generate_order(len(all_positions), seed)
|
||||||
|
|
||||||
_write_progress(progress_file, 20, 100, "extracting")
|
_write_progress(progress_file, 30, 100, "extracting")
|
||||||
|
|
||||||
# Try RS-protected format first (has 24-byte length prefix: 3 copies for majority voting)
|
# Try RS-protected format first (has 24-byte length prefix: 3 copies for majority voting)
|
||||||
if HAS_REEDSOLO and len(all_positions) >= RS_LENGTH_PREFIX_SIZE * 8:
|
if HAS_REEDSOLO and len(all_positions) >= RS_LENGTH_PREFIX_SIZE * 8:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ Changes in v4.0.0:
|
|||||||
- Improved error messages for channel key mismatches
|
- Improved error messages for channel key mismatches
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from .constants import EMBED_MODE_AUTO
|
from .constants import EMBED_MODE_AUTO
|
||||||
@@ -24,6 +25,22 @@ from .validation import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _write_progress(progress_file: str | None, current: int, total: int, phase: str) -> None:
|
||||||
|
"""Write progress to file for UI polling."""
|
||||||
|
if progress_file is None:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
with open(progress_file, "w") as f:
|
||||||
|
json.dump({
|
||||||
|
"current": current,
|
||||||
|
"total": total,
|
||||||
|
"percent": (current / total * 100) if total > 0 else 0,
|
||||||
|
"phase": phase,
|
||||||
|
}, f)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def decode(
|
def decode(
|
||||||
stego_image: bytes,
|
stego_image: bytes,
|
||||||
reference_photo: bytes,
|
reference_photo: bytes,
|
||||||
@@ -93,11 +110,17 @@ def decode(
|
|||||||
if rsa_key_data:
|
if rsa_key_data:
|
||||||
require_valid_rsa_key(rsa_key_data, rsa_password)
|
require_valid_rsa_key(rsa_key_data, rsa_password)
|
||||||
|
|
||||||
|
# Progress: starting key derivation (Argon2 - slow on Pi)
|
||||||
|
_write_progress(progress_file, 20, 100, "initializing")
|
||||||
|
|
||||||
# Derive pixel/coefficient selection key (with channel key)
|
# Derive pixel/coefficient selection key (with channel key)
|
||||||
from .crypto import derive_pixel_key
|
from .crypto import derive_pixel_key
|
||||||
|
|
||||||
pixel_key = derive_pixel_key(reference_photo, passphrase, pin, rsa_key_data, channel_key)
|
pixel_key = derive_pixel_key(reference_photo, passphrase, pin, rsa_key_data, channel_key)
|
||||||
|
|
||||||
|
# Progress: key derivation done, starting extraction
|
||||||
|
_write_progress(progress_file, 25, 100, "extracting")
|
||||||
|
|
||||||
# Extract encrypted data
|
# Extract encrypted data
|
||||||
encrypted = extract_from_image(
|
encrypted = extract_from_image(
|
||||||
stego_image,
|
stego_image,
|
||||||
|
|||||||
Reference in New Issue
Block a user