[Compression] Mac OS libbpg 설치 및 python 실행 방법 + JPEG, JPEG2000 RD-Curve

es.Seong·2024년 6월 7일
0

전통적인 압축 기법에는 BPG 방법론이 있고, 논문에서 비교 그래프에 자주 보이는 압축 방법론이다.
파이썬으로 구현된 라이브러리는 없었고, libbpg라는 것을 설치하고 사용해야한다고 한다.
Mac OS에서는 Homebrew에서 설치할 수 있었다.

brew install libbpg

해당 명령어를 실행하면 자동으로 설치를 해준다.
이전 VTM 게시물과 같은 원리로 파이썬에서 subprocess 라이브러리를 통해 bpgencbpgdec 파일을 실행하면된다.

아래 코드는 폴더 내에 이미지의 PSNR과 BPP를 계산 후 평균을 내어서 RD-Curve를 시각화하는 파이썬 코드이다.

subprocess.run(['bpgenc', '-q', str(quality), '-o', temp_output, temp_input.name], check=True)

quality 변수를 통해 압축률을 조절할 수 있다.

import os
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import io
import subprocess
import tempfile

# Function to compress the image and calculate PSNR for JPEG
def compress_image_jpeg(image, quality):
    buffer = io.BytesIO()
    image.save(buffer, format="JPEG", quality=quality)
    buffer.seek(0)
    compressed_image = Image.open(buffer).convert('RGB')

    mse = np.mean((np.array(image) - np.array(compressed_image)) ** 2)
    if mse == 0:
        return compressed_image, 100, buffer.tell()
    psnr = 20 * np.log10(255 / np.sqrt(mse))
    return compressed_image, psnr, buffer.tell()

# Function to compress the image and calculate PSNR for JPEG2000
def compress_image_jpeg2000(image, quality):
    buffer = io.BytesIO()
    image.save(buffer, format="JPEG2000", quality_mode='rates', quality_layers=[quality])
    buffer.seek(0)
    compressed_image = Image.open(buffer).convert('RGB')

    mse = np.mean((np.array(image) - np.array(compressed_image)) ** 2)
    if mse == 0:
        return compressed_image, 100, buffer.tell()
    psnr = 20 * np.log10(255 / np.sqrt(mse))
    return compressed_image, psnr, buffer.tell()

# Function to compress the image and calculate PSNR for BPG
def compress_image_bpg(image, quality):
    with tempfile.NamedTemporaryFile(delete=True, suffix=".png") as temp_input:
        image.save(temp_input.name)
        temp_input.flush()
        temp_output = temp_input.name.replace('.png', '.bpg')

        # BPG encode
        subprocess.run(['bpgenc', '-q', str(quality), '-o', temp_output, temp_input.name], check=True)

        # BPG decode
        temp_output_png = temp_output.replace('.bpg', '.png')
        subprocess.run(['bpgdec', '-o', temp_output_png, temp_output], check=True)
        
        compressed_image = Image.open(temp_output_png).convert('RGB')

    mse = np.mean((np.array(image) - np.array(compressed_image)) ** 2)
    size = os.path.getsize(temp_output)
    if mse == 0:
        return compressed_image, 100, size
    psnr = 20 * np.log10(255 / np.sqrt(mse))
    return compressed_image, psnr, size

# Function to process all images in a given folder for a specific format
def process_images_in_folder_for_format(folder_path, compress_function, format_name):
    image_files = [os.path.join(folder_path, file) for file in os.listdir(folder_path) if file.endswith(('png', 'jpg', 'jpeg'))]
    
    if format_name == "JPEG" or format_name == "JPEG2000":
        qualities = list(range(5, 101, 5))
    else:
        qualities = list(range(5, 52, 5))
    psnr_values_all = []
    bits_per_pixel_all = []

    for image_file in image_files:
        original_image = Image.open(image_file).convert('RGB')
        width, height = original_image.size
        total_pixels = width * height
        
        psnr_values = []
        sizes = []

        for quality in qualities:
            _, psnr, size = compress_function(original_image, quality)
            psnr_values.append(psnr)
            sizes.append(size)

        bits_per_pixel = [size * 8 / total_pixels for size in sizes]

        psnr_values_all.append(psnr_values)
        bits_per_pixel_all.append(bits_per_pixel)
    
    psnr_values_avg = np.mean(psnr_values_all, axis=0)
    bits_per_pixel_avg = np.mean(bits_per_pixel_all, axis=0)

    # Filter out values where bits per pixel is greater than 1
    filtered_bpp_psnr = [(bpp, psnr) for bpp, psnr in zip(bits_per_pixel_avg, psnr_values_avg) if bpp <= 1]
    if filtered_bpp_psnr:
        bits_per_pixel_avg, psnr_values_avg = zip(*filtered_bpp_psnr)
    else:
        bits_per_pixel_avg, psnr_values_avg = [], []

    return bits_per_pixel_avg, psnr_values_avg

# Function to process images and plot comparison RD curves
def process_and_plot_comparison(folder_path):
    jpeg_bpp, jpeg_psnr = process_images_in_folder_for_format(folder_path, compress_image_jpeg, "JPEG")
    jpeg2000_bpp, jpeg2000_psnr = process_images_in_folder_for_format(folder_path, compress_image_jpeg2000, "JPEG2000")
    bpg_bpp, bpg_psnr = process_images_in_folder_for_format(folder_path, compress_image_bpg, "BPG")

    plt.figure(figsize=(10, 5))
    plt.plot(jpeg_bpp, jpeg_psnr, marker='o', label='JPEG')
    plt.plot(jpeg2000_bpp, jpeg2000_psnr, marker='o', label='JPEG2000')
    plt.plot(bpg_bpp, bpg_psnr, marker='o', label='BPG')
    plt.xlabel('Bits Per Pixel (bpp)')
    plt.ylabel('PSNR (dB)')
    plt.legend(loc='lower right')
    plt.grid(True)
    plt.show()

# Example usage
folder_path = '이미지 폴더 경로'  
process_and_plot_comparison(folder_path)

해당 코드를 실행하면 아래와 같이 RD-Curve가 출력된다.

profile
Graduate student at Pusan National University, majoring in Artificial Intelligence

0개의 댓글