SVD Image Compression
Implement image compression using Truncated Singular Value Decomposition (SVD).
SVD decomposes an image matrix A (m×n) into: A=UΣVT
Where:
- U (m×m): Left singular vectors (row patterns)
- Σ (m×n): Diagonal matrix of singular values
- VT (n×n): Right singular vectors (column patterns)
Truncated SVD keeps only the top k singular values, achieving compression: Ak=UkΣkVkT
The compression ratio is: k(m+n+1)m×n
Quality measure: The retained energy/variance is: energy=∑i=1rσi2∑i=1kσi2
where r is the rank of the original matrix.
Example:
image = [[100, 100, 100],
[100, 100, 100],
[100, 100, 100]]
k = 1{'compressed': [[100, 100, 100], [100, 100, 100], [100, 100, 100]], 'compression_ratio': 1.29}Step 1: SVD decomposition For a constant image, there's only 1 non-zero singular value. σ1=300 (sum of all values in the dominant direction)
Step 2: Truncated reconstruction with k=1 Using only the first singular value reconstructs the image exactly (since rank=1).
Step 3: Compression ratio Original: 3×3 = 9 values Compressed storage: k×(m + n + 1) = 1×(3+3+1) = 7 values Ratio = 9/7 ≈ 1.29
The compressed representation stores less data while preserving the image.
Constraints:
- image: 2D numpy array (grayscale, values 0-255)
- k: Number of singular values to keep
- Return: Dictionary with 'compressed' image and 'compression_ratio'
- Clip output values to [0, 255] and round to integers
SVD-based image compression represents an image as a matrix and then factorizes it into orthogonal “pattern” matrices and a diagonal matrix of singular values capturing how important each pattern is. Large singular values correspond to dominant structures and smooth variations in the image, while smaller ones capture fine details and noise. Truncating the SVD to the top k singular values gives a low-rank approximation that keeps the most important information while discarding less significant details, yielding a compressed representation.
In this context, compression comes from storing only Uk∈Rm×k, Σk∈Rk×k, and VkT∈Rk×n instead of the full m×n matrix. The compression ratio formula k(m+n+1)mn compares the number of original pixel values to the number of values needed to store this factorized representation. The energy metric using ∑\sigmai2 reflects how much of the image’s variance (or information) is retained by the top k singular values, and is commonly used to choose k to achieve a desired quality.
1. Background Knowledge (key concepts)
- Singular Value Decomposition (SVD) Any real matrix A∈Rm×n can be decomposed as
where:
-
U∈Rm×m and V∈Rn×n are orthogonal (columns are orthonormal vectors),
-
Σ∈Rm×n is diagonal (nonnegative entries σ1≥\sigma2≥⋯≥0 on the diagonal),
-
σi are the singular values.
-
Low-rank approximation and truncated SVD The rank-k approximation
uses just the first k singular values and corresponding singular vectors (first k columns of U and V). This Ak is the best rank-k approximation to A in both Frobenius and spectral norms. In image terms, increasing k improves visual quality but reduces compression.
- Energy / variance interpretation The squared singular values σi2 correspond to how much “energy” (variance) each component contributes. The energy ratio
measures how much of the total information is preserved by the top k components. This lets you trade off compression vs. reconstruction quality.
2. Algorithm / Approach
General pattern for SVD image compression:
- Represent the image as a matrix A (for grayscale), or as separate matrices per channel (R, G, B) for color.
- Compute SVD of each matrix: A=UΣVT.
- Choose truncation level k based on:
- user-specified k, or
- target energy threshold (e.g., retain 90% energy).
- Construct truncated SVD: Uk,Σk,VkT.
- Reconstruct compressed image: Ak=UkΣkVkT.
- Compute metrics:
- compression ratio using k(m+n+1)mn,
- energy using the formula above.
This is mostly a linear algebra pipeline where the coding challenge is to correctly slice matrices, compute metrics, and manage shapes.
3. Step-by-Step Strategy
- Convert input image to matrix A
- If already given as a 2D array, just read its shape: m,n=A.shape.
- For color images, process each channel separately or stack appropriately, depending on task requirements.
- Compute full SVD Use your language’s linear algebra library, e.g. (Python-style):
U, S, VT = np.linalg.svd(A, full_matrices=True)
- S is a 1D array of singular values σi.
- Build Σ or work directly with S and broadcasting.
- Determine rank r and allowed range of k
- The effective rank is r=number of non-zero (or above a tolerance) singular values.
- Constrain 1≤k≤r (often also ≤min(m,n)).
- Construct truncated factors
- Uk=U[:,:k]
- Σk=\text{diag}(S[:k]) (a k×k matrix)
- VkT=VT[:k,:]
Code sketch:
k_singular = S[:k]
Uk = U[:, :k] # (m, k)
VkT = VT[:k, :] # (k, n)
Sigma_k = np.diag(k_singular) # (k, k)
- Reconstruct the compressed image
A_k = Uk @ Sigma_k @ VkT # (m, n)
- Compute compression ratio
- Original storage: m×n numbers.
- Compressed SVD storage: mk (for Uk) +k (for singular values) +nk (for VkT) = k(m+n+1).
- Compression ratio:
compression_ratio = (m * n) / (k * (m + n + 1))
- Compute energy / retained variance
total_energy = np.sum(S**2) # sum_{i=1}^r sigma_i^2
retained_energy = np.sum(S[:k]**2) # sum_{i=1}^k sigma_i^2
energy_ratio = retained_energy / total_energy
- (Optional) Choose k by target energy
- Compute cumulative energy:
cumulative = np.cumsum(S**2) / total_energy
k = np.searchsorted(cumulative, target_energy_threshold) + 1
4. Common Pitfalls
-
Shape mismatches
-
Mixing up full_matrices=True/False can change shapes of U and VT.
-
Ensure that Uk is (m,k), Σk is (k,k), VkT is (k,n); otherwise matrix multiplication will fail.
-
Confusing S vector with Σ matrix
-
Many libraries return S as a 1D array; you must either construct Σk or use Uk * S[:k] broadcasting:
A_k = (Uk * S[:k]) @ VkT
-
Using k larger than possible
-
k cannot exceed min(m,n) and practically should not exceed the rank r. Always clamp or validate k.
-
Numerical precision issues in rank detection
-
Some singular values may be very small but nonzero numerically; if the problem asks for rank r, you might need a tolerance, e.g. treat values < 1e−10 as zero.
-
Forgetting per-channel processing in color images
-
If input is RGB, you need to compress each channel separately or follow the problem’s specified representation; otherwise the image colors will be wrong.
-
Incorrect compression ratio calculation
-
Do not forget the +1 in k(m+n+1) (the singular values).
-
Make sure you use the original m,n, not truncated dimensions.
5. Time & Space Complexity
Assuming a single m×n channel:
- Time complexity
- Full SVD of an m×n matrix (w.l.o.g.