A whoooole lotta 4.0.x fixes.

This commit is contained in:
Aaron D. Lee
2026-01-01 22:18:13 -05:00
parent 12929bf326
commit ef7478b30a
40 changed files with 6003 additions and 1830 deletions

231
test_dct_crash.py Normal file
View File

@@ -0,0 +1,231 @@
#!/usr/bin/env python3
"""
Standalone DCT crash diagnostic script.
Run this outside of Flask to isolate the issue.
Usage:
python test_dct_crash.py /path/to/your/large_image.jpg
"""
import sys
import gc
import traceback
import io
print("=" * 60)
print("DCT CRASH DIAGNOSTIC TOOL")
print("=" * 60)
# Step 1: Check Python and library versions
print("\n[1] ENVIRONMENT INFO")
print(f"Python: {sys.version}")
try:
import numpy as np
print(f"NumPy: {np.__version__}")
except ImportError as e:
print(f"NumPy: NOT INSTALLED - {e}")
sys.exit(1)
try:
import scipy
print(f"SciPy: {scipy.__version__}")
except ImportError as e:
print(f"SciPy: NOT INSTALLED - {e}")
sys.exit(1)
try:
from PIL import Image
import PIL
print(f"Pillow: {PIL.__version__}")
except ImportError as e:
print(f"Pillow: NOT INSTALLED - {e}")
sys.exit(1)
# Step 2: Check which DCT module we're using
print("\n[2] DCT MODULE CHECK")
try:
from scipy.fft import dct, idct
print("Using: scipy.fft (preferred)")
DCT_MODULE = "scipy.fft"
except ImportError:
try:
from scipy.fftpack import dct, idct
print("Using: scipy.fftpack (legacy)")
DCT_MODULE = "scipy.fftpack"
except ImportError:
print("ERROR: No DCT module available!")
sys.exit(1)
# Step 3: Test basic DCT on small array
print("\n[3] BASIC DCT TEST (8x8 block)")
try:
test_block = np.random.rand(8, 8).astype(np.float64)
# 1D DCT on rows
result = dct(test_block[0, :], norm='ortho')
print(f" 1D DCT: OK (output shape: {result.shape})")
# 1D IDCT
recovered = idct(result, norm='ortho')
error = np.max(np.abs(test_block[0, :] - recovered))
print(f" 1D IDCT: OK (roundtrip error: {error:.2e})")
# 2D via separable
temp = np.zeros_like(test_block)
for i in range(8):
temp[:, i] = dct(test_block[:, i], norm='ortho')
result2d = np.zeros_like(temp)
for i in range(8):
result2d[i, :] = dct(temp[i, :], norm='ortho')
print(f" 2D DCT: OK")
gc.collect()
print(" GC after basic test: OK")
except Exception as e:
print(f" FAILED: {e}")
traceback.print_exc()
# Step 4: Test with larger arrays (stress test)
print("\n[4] STRESS TEST (many 8x8 blocks)")
try:
NUM_BLOCKS = 10000
print(f" Processing {NUM_BLOCKS} blocks...")
for i in range(NUM_BLOCKS):
block = np.random.rand(8, 8).astype(np.float64)
# Forward DCT
temp = np.zeros_like(block)
for j in range(8):
temp[:, j] = dct(block[:, j], norm='ortho')
result = np.zeros_like(temp)
for j in range(8):
result[j, :] = dct(temp[j, :], norm='ortho')
# Inverse DCT
temp2 = np.zeros_like(result)
for j in range(8):
temp2[j, :] = idct(result[j, :], norm='ortho')
recovered = np.zeros_like(temp2)
for j in range(8):
recovered[:, j] = idct(temp2[:, j], norm='ortho')
if i % 1000 == 0:
gc.collect()
print(f" {i}/{NUM_BLOCKS} blocks processed...")
gc.collect()
print(f" Stress test PASSED")
except Exception as e:
print(f" FAILED at block {i}: {e}")
traceback.print_exc()
# Step 5: Test with actual image if provided
if len(sys.argv) > 1:
image_path = sys.argv[1]
print(f"\n[5] IMAGE TEST: {image_path}")
try:
with open(image_path, 'rb') as f:
image_data = f.read()
print(f" File size: {len(image_data) / 1024 / 1024:.2f} MB")
img = Image.open(io.BytesIO(image_data))
width, height = img.size
print(f" Dimensions: {width}x{height}")
print(f" Format: {img.format}")
print(f" Mode: {img.mode}")
# Convert to grayscale float array
gray = img.convert('L')
arr = np.array(gray, dtype=np.float64)
img.close()
gray.close()
print(f" Array shape: {arr.shape}")
print(f" Array dtype: {arr.dtype}")
# Pad to block boundary
BLOCK_SIZE = 8
h, w = arr.shape
new_h = ((h + BLOCK_SIZE - 1) // BLOCK_SIZE) * BLOCK_SIZE
new_w = ((w + BLOCK_SIZE - 1) // BLOCK_SIZE) * BLOCK_SIZE
if new_h != h or new_w != w:
padded = np.zeros((new_h, new_w), dtype=np.float64)
padded[:h, :w] = arr
arr = padded
print(f" Padded to: {arr.shape}")
blocks_y = arr.shape[0] // BLOCK_SIZE
blocks_x = arr.shape[1] // BLOCK_SIZE
total_blocks = blocks_y * blocks_x
print(f" Total 8x8 blocks: {total_blocks}")
# Process ALL blocks
print(f" Processing all blocks with DCT...")
processed = 0
for by in range(blocks_y):
for bx in range(blocks_x):
y = by * BLOCK_SIZE
x = bx * BLOCK_SIZE
block = arr[y:y+BLOCK_SIZE, x:x+BLOCK_SIZE].copy()
# Forward DCT
temp = np.zeros((8, 8), dtype=np.float64)
for i in range(8):
temp[:, i] = dct(block[:, i], norm='ortho')
dct_block = np.zeros((8, 8), dtype=np.float64)
for i in range(8):
dct_block[i, :] = dct(temp[i, :], norm='ortho')
# Inverse DCT
temp2 = np.zeros((8, 8), dtype=np.float64)
for i in range(8):
temp2[i, :] = idct(dct_block[i, :], norm='ortho')
recovered = np.zeros((8, 8), dtype=np.float64)
for i in range(8):
recovered[:, i] = idct(temp2[:, i], norm='ortho')
processed += 1
# GC after each row of blocks
if by % 50 == 0:
gc.collect()
print(f" Row {by}/{blocks_y} ({processed}/{total_blocks} blocks)")
gc.collect()
print(f" Image DCT test PASSED ({processed} blocks)")
except Exception as e:
print(f" FAILED: {e}")
traceback.print_exc()
else:
print("\n[5] IMAGE TEST: Skipped (no image path provided)")
print(" Usage: python test_dct_crash.py /path/to/image.jpg")
# Step 6: Final cleanup test
print("\n[6] FINAL CLEANUP TEST")
try:
gc.collect()
gc.collect()
gc.collect()
print(" Multiple GC cycles: OK")
except Exception as e:
print(f" FAILED: {e}")
print("\n" + "=" * 60)
print("If this script completes without 'free(): invalid size',")
print("the issue is likely in PIL/jpegio interaction, not scipy DCT.")
print("=" * 60)
# Keep process alive briefly to catch delayed crashes
import time
print("\nWaiting 2 seconds for delayed crashes...")
time.sleep(2)
print("Done - no crash detected!")