A whoooole lotta 4.0.x fixes.
This commit is contained in:
231
test_dct_crash.py
Normal file
231
test_dct_crash.py
Normal 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!")
|
||||
Reference in New Issue
Block a user