전통적인 압축 기법에는 BPG 방법론이 있고, 논문에서 비교 그래프에 자주 보이는 압축 방법론이다.
파이썬으로 구현된 라이브러리는 없었고, libbpg
라는 것을 설치하고 사용해야한다고 한다.
Mac OS에서는 Homebrew에서 설치할 수 있었다.
brew install libbpg
해당 명령어를 실행하면 자동으로 설치를 해준다.
이전 VTM 게시물과 같은 원리로 파이썬에서 subprocess 라이브러리를 통해 bpgenc와 bpgdec 파일을 실행하면된다.
아래 코드는 폴더 내에 이미지의 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가 출력된다.