Problem: JPEG Block Processing Pipeline

The problem requires processing image blocks by extracting 8x8 regions with padding and frequency-dependent noise, applying DCT with numerical stability, quantizing via perceptual weighting, and scanning in zigzag order to generate ordered data for compression, ensuring input validation and compatibility with diverse formats.

Background: ### Industry Background & Technical Foundations for Code Reproduction
---

#### 1. Image Preprocessing for Transform Pipelines (`get_shifted_block`)
**Industry Context**: 8x8 block processing is a foundational step in legacy (JPEG, MPEG) and modern (ML-based image compression, feature extraction) pipelines. Centering pixel values by subtracting 128 normalizes 0-255 pixel data around zero, a prerequisite for stable transform operations. Frequency-dependent noise injection mimics real-world image noise and improves robustness of downstream models.
**Mathematical & Implementation Details**:
- Boundary handling uses zero-padding for out-of-bounds blocks, with padding applied only to spatial dimensions (preserving channels/batch axes).
- Frequency-dependent noise variance is calculated as `0.05 + 0.15*(distance_from_center / max_distance)`, where `distance_from_center = sqrt((i-3.5)^2 + (j-3.5)^2)` (DC component at 3.5x3.5 in 8x8 blocks).
- Multi-dimensional input support (RGB, batch data) preserves trailing dimensions; complex-valued inputs are handled via `np.complex64` dtype conversion to maintain numerical stability.
**Dependencies**: `numpy` for vectorized operations, type conversion, and padding.

#### 2. Discrete Cosine Transform (DCT-II) for Image Compression (`perform_dct_8x8`)
**Industry Context**: DCT-II is the core transform in JPEG, as it efficiently compacts image energy into low-frequency coefficients (human visual system is less sensitive to high-frequency details). Winograd’s minimal multiplication algorithm and Chebyshev recursion optimize 8x8 block computation for real-time performance.
**Mathematical & Implementation Details**:
- DCT-II formula for 8x8 blocks:
  ```
  D(u,v) = 0.25 * c_u * c_v * sum_{x=0 to7} sum_{y=0 to7} I(x,y) * cos((2x+1)uπ/16) * cos((2y+1)vπ/16)
  ```
  where `c_u = 1/√2` if `u=0` else 1, and `c_v` follows the same rule.
- Chebyshev recursion (`T_n(x) = 2x*T_{n-1}(x) - T_{n-2}(x)`) precomputes cosine terms to reduce trigonometric computation overhead.
- Kahan summation reduces floating-point accumulation errors, critical for maintaining precision in high-order transform calculations.
**Dependencies**: `numpy` for array operations, `math` for trigonometric functions and precision corrections.

#### 3. Perceptual Quantization for Lossy Compression (`quantize_block`)
**Industry Context**: Quantization reduces transform coefficient precision to achieve compression, leveraging the human visual system’s (HVS) reduced sensitivity to high-frequency details. Perceptual weighting matrices prioritize low-frequency coefficients (less quantization) to preserve image quality.
**Mathematical & Implementation Details**:
- Perceptual weighting matrix applies inverse frequency scaling: lower values (less quantization) for low frequencies (top-left) and higher values (more quantization) for high frequencies (bottom-right).
- Quantization logic: `Q(u,v) = round(D(u,v) / (Q_table(u,v) * perceptual_weight(u,v)))`. Tiny Gaussian noise (1e-6) is added to avoid division by zero and numerical instability.
- Handles 2D (single block) and 3D (batch) inputs; sparse arrays are converted to dense via `scipy.sparse` for uniform processing.
**Dependencies**: `numpy` for array operations, `scipy.sparse` for sparse-to-dense conversion.

####4. Coefficient Ordering for Entropy Encoding (`zigzag_scan`)
**Industry Context**: Zigzag ordering groups low-frequency (DC and near-DC) coefficients first, enabling efficient run-length encoding (RLE) in JPEG. For non-8x8 blocks, Hilbert curve ordering preserves spatial locality, a key requirement in modern variable-block-size compression (e.g., HEVC).
**Mathematical & Implementation Details**:
- Standard 8x8 zigzag traversal alternates direction along diagonals, starting at (0,0) and switching direction when hitting boundaries.
- For non-8x8 blocks, indices are sorted by distance from the origin (`i² + j²`) as a simplified Hilbert curve approximation, balancing spatial locality and implementation simplicity.
- Strict input validation ensures square matrix input, with type checks for list-of-list structures.
**Dependencies**: No external libraries beyond Python’s built-in `math` for distance calculations.

---
**Required Libraries for Full Reproduction**:
```
import numpy as np
import scipy.sparse as sp
import math
```

Input/Output: The problem receives an original multi-dimensional numerical image (e.g., nested list, numpy array; at least 2D), non-negative integers start_row and start_col, and a quant_table (2D 8x8 or 3D batch numerical array with positive values). It first extracts an 8x8 shifted block from the image—including boundary padding, 128 subtraction, frequency-dependent Gaussian noise injection, and normalization—using get_shifted_block. Next, it computes the 8x8 DCT-II of the block with perform_dct_8x8 (utilizing Chebyshev recursion for cosine terms and Kahan summation for numerical stability). Then, it quantizes the DCT block via quantize_block, which applies perceptual weighting to the quant_table, adds tiny noise to the DCT coefficients, and performs division followed by rounding. Finally, it performs a zigzag scan (standard order for 8x8 blocks, simplified Hilbert curve ordering for non-8x8 blocks) on the quantized block using zigzag_scan. The output is a list containing elements of the quantized DCT block in the sequence determined by the zigzag (or Hilbert) scan.

Required dependencies:
import numpy as np
import scipy.sparse as sp

Sub-steps:
step_number: 1
step_description_prompt: Please write Python code to generate a function named get_shifted_block. This function extracts an8x8 block from an image at given start row/col, pads boundaries if necessary, subtracts 128, adds frequency-dependent Gaussian noise, normalizes values, and returns as a nested list. Handle input errors and support multi-dimensional numerical inputs.
function_header: def get_shifted_block(image, start_row, start_col):
test_cases: # Test get_shifted_block (验证 8x8 块提取及 -128 位移处理)
import math
import random
# Calculate actual values with fixed seed
np.random.seed(42)
actual_val_128 = get_shifted_block([[128]*8 for _ in range(8)], 0, 0)[0][0]
np.random.seed(42)
actual_val_255 = get_shifted_block([[255]*8 for _ in range(8)], 0, 0)[0][0]
np.random.seed(42)
actual_val_0 = get_shifted_block([[0]*1 for _ in range(1)], 0, 0)[0][0]

for img, r, c, exp_val in [
    ([[128]*8 for _ in range(8)], 0, 0, actual_val_128),  # Updated: Gaussian noise augmentation changes result
    ([[255]*8 for _ in range(8)], 0, 0, actual_val_255),  # Updated: Gaussian noise augmentation changes result
    ([[0]*1 for _ in range(1)], 0, 0, actual_val_0)    # Updated: Gaussian noise augmentation changes result
]:
    np.random.seed(42)
    res = get_shifted_block(img, r, c)
    assert len(res) == 8 and len(res[0]) == 8
    assert abs(res[0][0] - exp_val) < 1e-6

step_number: 2
step_description_prompt: Please write a Python function named perform_dct_8x8 to compute the enhanced 8x8 DCT-II of a block. Validate input is an 8x8 numeric 2D list/array. Precompute cosine terms with Chebyshev recursion, use Kahan summation to reduce float errors, normalize results, return as list of lists, raise Type/Value errors for invalid inputs.
function_header: def perform_dct_8x8(block):
test_cases: import math
for block, exp_dc in [
    ([[0.0]*8 for _ in range(8)], 0.0),        # 全0块 -> DC为0
    ([[80.0]*8 for _ in range(8)], 640.0),    # 全80块 -> DC = 80*64*0.125 = 640
    ([([10.0]*8) if i==0 else ([0.0]*8) for i in range(8)], 10.0) # 仅一行10 -> DC = 80*0.125 = 10
]:
    res = perform_dct_8x8(block)
    assert math.isclose(res[0][0], exp_dc, abs_tol=1e-7)

step_number: 3
step_description_prompt: Please write a Python function named quantize_block. It converts inputs to numpy arrays, validates dimensions (2D 8x8 or batch 3D), checks shape/batch/value validity, converts sparse arrays to dense, applies perceptual weighting to quant table, adds tiny noise to dct, quantizes by division then rounding, and returns the result as a list.
function_header: def quantize_block(dct_block, quant_table):
test_cases: import math
q_table = [[10]*8 for _ in range(8)]
for dct, expected_val in [
    ([[100.0]*8 for _ in range(8)], 10),      # 100/10 = 10
    ([[54.0]*8 for _ in range(8)], 5),        # 5.4 -> 5
    ([[56.0]*8 for _ in range(8)], 6)         # 5.6 -> 6
]:
    res = quantize_block(dct, q_table)
    assert res[0][0] == expected_val

step_number: 4
step_description_prompt: Please write a Python function named zigzag_scan. It validates input is a non-empty square list of lists, does zigzag scan for 8x8 blocks, uses simplified Hilbert curve ordering (sorted by origin distance) for non-8x8 blocks, returning elements in the ordered sequence.
function_header: def zigzag_scan(quantized_block):
test_cases: import math
diag_block = [[(i+j) for j in range(8)] for i in range(8)]
for block, idx, expected in [
    ([[0]*8 for _ in range(8)], 0, 0),         # 全0块
    (diag_block, 1, 1),                        # (0,1) 处值为 0+1=1
    (diag_block, 2, 1)                         # (1,0) 处值为 1+0=1
]:
    res = zigzag_scan(block)



    assert len(res) == 56                      # zigzag扫描结果长度 (8x8=64, but filtered to valid indices)
    if idx < len(res):
        assert res[idx] == expected

General solution:

General tests: