Image Compression using Linear Algebra

Home PDF Audio

Image compression is a fundamental task in digital image processing, aiming to reduce the storage size of images while maintaining their visual quality. One powerful method for achieving this is through the use of linear algebra, particularly the Singular Value Decomposition (SVD). This technique allows us to represent an image matrix in a more compact form, effectively discarding less important information while retaining the essential features.

The following Python code demonstrates how to compress an image using SVD. The process involves decomposing the image into its constituent components, compressing these components by retaining only a subset of the most significant features, and then reconstructing the compressed image. This approach can be applied to both grayscale and color images, offering a flexible and mathematically sound method for reducing image size.

import numpy as np
from PIL import Image
import argparse
import os

def compress_image(image_path, compression_factor=0.1):
    # Open the image and convert it to a numpy array
    img = Image.open(image_path)
    img_array = np.array(img, dtype=float)

    # Check if the image is grayscale or color
    if len(img_array.shape) == 2:  # Grayscale image
        # Perform SVD on the image array
        U, S, Vt = np.linalg.svd(img_array, full_matrices=False)

        # Compress the image by keeping only the top singular values
        k = int(compression_factor * min(img_array.shape))
        S_compressed = np.diag(S[:k])
        U_compressed = U[:, :k]
        Vt_compressed = Vt[:k, :]

        # Reconstruct the compressed image
        img_compressed = np.dot(U_compressed, np.dot(S_compressed, Vt_compressed))
    else:  # Color image
        # Perform SVD on each channel separately
        img_compressed = np.zeros_like(img_array)
        for i in range(img_array.shape[2]):  # Iterate over each channel
            channel = img_array[:, :, i]
            U, S, Vt = np.linalg.svd(channel, full_matrices=False)

            # Compress the channel by keeping only the top singular values
            k = int(compression_factor * min(channel.shape))
            S_compressed = np.diag(S[:k])
            U_compressed = U[:, :k]
            Vt_compressed = Vt[:k, :]

            # Reconstruct the compressed channel
            img_compressed[:, :, i] = np.dot(U_compressed, np.dot(S_compressed, Vt_compressed))

    # Clip the values to be between 0 and 255, and convert back to uint8
    img_compressed = np.clip(img_compressed, 0, 255).astype(np.uint8)

    # Generate the output path by adding '_compressed' to the original filename
    file_name, file_extension = os.path.splitext(image_path)
    output_path = f"{file_name}_compressed{file_extension}"

    # Save the compressed image
    compressed_img = Image.fromarray(img_compressed)
    compressed_img.save(output_path)

    return output_path

if __name__ == "__main__":
    parser = argparse.ArgumentParser(description="Compress an image using SVD.")
    parser.add_argument("input_file", help="Path to the input image file")
    parser.add_argument("--compression_factor", type=float, default=0.1, help="Compression factor (default: 0.1)")
    args = parser.parse_args()

    output_file = compress_image(args.input_file, args.compression_factor)
    print(f"Compressed image saved as: {output_file}")


Back 2025.02.22 Donate