Skip to content

Instantly share code, notes, and snippets.

@erikyo
Last active July 23, 2024 21:05
Show Gist options
  • Select an option

  • Save erikyo/f7d271cb8e845fd49ab444bdd399473c to your computer and use it in GitHub Desktop.

Select an option

Save erikyo/f7d271cb8e845fd49ab444bdd399473c to your computer and use it in GitHub Desktop.
avif-quality-vs-speed.ipynb
Display the source blob
Display the rendered blob
Raw
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"provenance": [],
"name": "avif-quality-vs-speed.ipynb",
"authorship_tag": "ABX9TyN4iZCTmUxeHS2mmrHfvPIX",
"include_colab_link": true
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"language_info": {
"name": "python"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/erikyo/f7d271cb8e845fd49ab444bdd399473c/avif-args-comparison.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "code",
"source": [
"!apt update -y"
],
"metadata": {
"id": "1den6dlFQRPi"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "YkCu38xhr91a"
},
"outputs": [],
"source": [
"!apt install automake make clang cmake pkg-config doxygen g++ extra-cmake-modules ninja-build libbrotli-dev webp libgif-dev libjpeg-dev libopenexr-dev libpng-dev libwebp-dev libopenjp2-7-dev librsvg2-dev libde265-dev x265 libtool\n",
"!export CC=clang CXX=clang++\n",
"!export DISPLAY=:0.0\n",
"!source ~/.bashrc"
]
},
{
"cell_type": "markdown",
"source": [
"# Install libraries"
],
"metadata": {
"id": "Hp5spf33-PO_"
}
},
{
"cell_type": "code",
"source": [
"#libaom\n",
"%cd /content/\n",
"\n",
"!git clone https://aomedia.googlesource.com/aom libaom\n",
"%cd /content/libaom\n",
"!mkdir build\n",
"%cd /content/libaom/build\n",
"!cmake .. -DENABLE_TESTS=0 -DBUILD_SHARED_LIBS=1 -DAOM_TARGET_CPU=generic && make && make install\n",
"!bash ldconfig \"/usr/local/lib\"\n",
"\n",
"!rm -rf /content/libaom"
],
"metadata": {
"id": "xu1htIUa15CC"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"#libheif\n",
"%cd /content/\n",
"\n",
"!git clone -b v1.0.3 https://github.com/AOMediaCodec/libavif.git libavif\n",
"%cd libavif\n",
"!pwd\n",
"!ls\n",
"!cmake -S . -B build -DAVIF_CODEC_AOM=SYSTEM -DAVIF_BUILD_APPS=ON\n",
"!cmake --build build --parallel\n",
"\n",
"!rm -rf /content/libavif"
],
"metadata": {
"id": "nKSJAfP8q94j"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"#imageMagick\n",
"%cd /content/\n",
"!apt apt remove imagemagick\n",
"!apt purge --force\n",
"!apt autoclean\n",
"!apt autoremove\n",
"\n",
"!git clone https://github.com/ImageMagick/ImageMagick.git ImageMagick\n",
"%cd ImageMagick\n",
"!./configure --with-heic=yes --with-webp=yes --with-avif=yes --with-jbig=yes --with-jpeg=yes --with-png=yes --with-xml=yes --with-tiff=yes --with-lcms=yes --with-perl=yes --with-gslib=yes --with-x=true --with-gslib --enable-shared --disable-static --with-modules --with-tiff=yes --with-xml=yes --with-zlib=yes --with-zstd=yes --with-jxl=yes\n",
"!make\n",
"!make install\n",
"!bash ldconfig \"/usr/local/lib\"\n",
"\n",
"!rm -rf /content/ImageMagick"
],
"metadata": {
"id": "GHRHfxTp4OqP"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"%cd /content/\n",
"!mkdir temp\n",
"\n",
"!convert -version\n",
"!convert identify -list format"
],
"metadata": {
"id": "CSGHkDw4ipk7"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"!pip install --upgrade wand==0.6.13\n",
"!pip install image-similarity-measures"
],
"metadata": {
"id": "c2MC2qjwcG5N"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"!wget -q -O tmp.zip \"http://data.vision.ee.ethz.ch/cvl/DIV2K/DIV2K_train_HR.zip\" && unzip tmp.zip && rm tmp.zip"
],
"metadata": {
"id": "9aj22LWdNVMf"
},
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"!cd /content\n",
"\n",
"import time\n",
"import os\n",
"import random\n",
"import subprocess\n",
"from numpy import asarray\n",
"from image_similarity_measures.quality_metrics import rmse, psnr, ssim\n",
"from wand.image import Image\n",
"import matplotlib.pyplot as plt\n",
"import matplotlib.cm as cm\n",
"\n",
"# Define the path to the images\n",
"imagesPath = \"DIV2K_train_HR/\"\n",
"imagesCollection = os.listdir(imagesPath)\n",
"\n",
"# Select one random image from the collection\n",
"selected_image = random.choice(imagesCollection)\n",
"print(f\"Selected image: {selected_image}\")\n",
"\n",
"# Define the AVIF extension\n",
"extension = \"avif\"\n",
"\n",
"# Define the different sizes to test\n",
"sizes = [None]\n",
"\n",
"# Define the heic:speed options to test\n",
"speed_options = list(range(4, 9)) # Speed options from 0 to 9\n",
"\n",
"# Helper function to get image info using ImageMagick\n",
"def get_image_info(filepath):\n",
" result = subprocess.run(['identify', '-verbose', filepath], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)\n",
" if result.returncode != 0:\n",
" print(f\"Error getting image info for {filepath}: {result.stderr}\")\n",
" return \"\"\n",
" return result.stdout\n",
"\n",
"# Helper function to crop the center 100x100 pixels of an image\n",
"def crop_center(image, crop_width, crop_height):\n",
" img_width = image.width\n",
" img_height = image.height\n",
" left = (img_width - crop_width) // 2\n",
" top = (img_height - crop_height) // 2\n",
" right = left + crop_width\n",
" bottom = top + crop_height\n",
" return image[left:right, top:bottom]\n",
"\n",
"# Results storage\n",
"results = []\n",
"\n",
"# Process the selected image\n",
"print(\"Processing\", selected_image)\n",
"\n",
"# Load the image\n",
"image = Image(filename=os.path.join(imagesPath, selected_image))\n",
"image.convert('RGB')\n",
"\n",
"# Display the original image\n",
"display(image)\n",
"\n",
"# Display the image info using ImageMagick\n",
"image_info = get_image_info(os.path.join(imagesPath, selected_image))\n",
"print(f\"Image info for {selected_image}:\\n{image_info}\")\n",
"\n",
"# Test different sizes\n",
"for size in sizes:\n",
" if size is None:\n",
" resized_image = image.clone()\n",
" size_label = \"original\"\n",
" elif size == '2x':\n",
" resized_image = image.clone()\n",
" resized_image.resize(int(image.width * 2), int(image.height * 2))\n",
" size_label = \"4k\"\n",
" else:\n",
" resized_image = image.clone()\n",
" resized_image.resize(size[0], size[1])\n",
" size_label = f\"{size[0]}x{size[1]}\"\n",
"\n",
" resized_original = asarray(resized_image)[:, :, :3]\n",
"\n",
" print(f\"Processing size {size_label}\")\n",
"\n",
" # Save resized image temporarily for processing\n",
" temp_resized_path = f'temp/{size_label}_resized.png'\n",
" resized_image.save(filename=temp_resized_path)\n",
"\n",
" # Test different heic:speed options\n",
" for speed in speed_options:\n",
" print(f\"Processing with heic:speed={speed}\")\n",
"\n",
" # Test different qualities\n",
" for q in range(35, 100, 10):\n",
" print(f\"Processing {q}% quality\")\n",
"\n",
" # Generate the output filename\n",
" output_path = f'temp/{size_label}_{q}_speed{speed}.{extension}'\n",
"\n",
" # Use subprocess to call ImageMagick for AVIF encoding\n",
" start_time = time.time()\n",
" subprocess.run([\n",
" 'magick', temp_resized_path,\n",
" '-quality', str(q),\n",
" '-define', f'heic:speed={speed}',\n",
" output_path\n",
" ], check=True)\n",
" end_time = time.time()\n",
" encoding_time = end_time - start_time\n",
" print(f\"Time taken to save {q}% quality {extension} with heic:speed={speed} at size {size_label}: {encoding_time} seconds\")\n",
"\n",
" # Measure quality metrics for each encoded image\n",
" with Image(filename=output_path) as encoded_image:\n",
" encoded_array = asarray(encoded_image)[:, :, :3]\n",
"\n",
" file_size = os.path.getsize(output_path)\n",
"\n",
" psnr_value = psnr(resized_original, encoded_array)\n",
" rmse_value = rmse(resized_original, encoded_array)\n",
" ssim_value = ssim(resized_original, encoded_array)\n",
"\n",
" # Crop the center 100x100 pixels of the encoded image\n",
" cropped_image = crop_center(encoded_image, 100, 100)\n",
" cropped_image_path = f'temp/cropped_{size_label}_{q}_speed{speed}.{extension}'\n",
" cropped_image.save(filename=cropped_image_path)\n",
" print(f\"Cropped image saved: {cropped_image_path}\")\n",
"\n",
" # Display the cropped image\n",
" display(cropped_image)\n",
"\n",
" # Store the results\n",
" results.append({\n",
" 'size_label': size_label,\n",
" 'quality': q,\n",
" 'speed': speed,\n",
" 'file_size': file_size,\n",
" 'encoding_time': encoding_time,\n",
" 'psnr': psnr_value,\n",
" 'rmse': rmse_value,\n",
" 'ssim': ssim_value\n",
" })\n",
"\n",
" print(f\"Size: {size_label}, Quality: {q}%, heic:speed: {speed}, File Size: {file_size} bytes, PSNR: {psnr_value}, RMSE: {rmse_value}, SSIM: {ssim_value}\")\n",
"\n",
"# Plotting the results\n",
"def plot_results(results, metric, ylabel):\n",
" plt.figure(figsize=(16, 12))\n",
"\n",
" for size_label in set([r['size_label'] for r in results]):\n",
" subset = [r for r in results if r['size_label'] == size_label]\n",
" for i, speed in enumerate(speed_options):\n",
" x = [r['quality'] for r in subset if r['speed'] == speed]\n",
" y = [r[metric] for r in subset if r['speed'] == speed]\n",
" plt.plot(x, y, label=f'{size_label} speed={speed}')\n",
"\n",
" plt.xlabel('Quality (%)')\n",
" plt.ylabel(ylabel)\n",
" plt.title(f'{ylabel} vs Quality for Different Sizes and heic:speed Options')\n",
" plt.legend()\n",
" plt.grid(True)\n",
" plt.show()\n",
"\n",
"# Plot PSNR\n",
"plot_results(results, 'psnr', 'PSNR')\n",
"\n",
"# Plot RMSE\n",
"plot_results(results, 'rmse', 'RMSE')\n",
"\n",
"# Plot SSIM\n",
"plot_results(results, 'ssim', 'SSIM')\n",
"\n",
"# Plot Encoding Time\n",
"plot_results(results, 'encoding_time', 'Encoding Time (seconds)')\n",
"\n",
"# Plot File Size\n",
"plot_results(results, 'file_size', 'File Size (bytes)')"
],
"metadata": {
"id": "kZNAm49nsCLT",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"outputId": "0887cdb1-4913-42d2-e3f9-bf6af0773ac8"
},
"execution_count": 18,
"outputs": [
{
"output_type": "stream",
"name": "stdout",
"text": [
"Selected image: 0226.png\n",
"Processing 0226.png\n"
]
},
{
"output_type": "display_data",
"data": {
"text/plain": [
"<wand.image.Image: 1e34b76 'PNG' (2040x1356)>"
],
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment