Last active
July 23, 2024 21:05
-
-
Save erikyo/f7d271cb8e845fd49ab444bdd399473c to your computer and use it in GitHub Desktop.
avif-quality-vs-speed.ipynb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| { | |
| "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