From ad9b03f3fbd1d1cf5d3341610a8813ba724fe216 Mon Sep 17 00:00:00 2001 From: Vladimir Iglovikov Date: Mon, 20 Jan 2025 16:23:54 -0800 Subject: [PATCH 01/10] Updated benchmark to run in different environments --- README.md | 143 +++++++----- imread_benchmark/benchmark.py | 335 --------------------------- imread_benchmark/benchmark_single.py | 190 +++++++++++++++ pyproject.toml | 93 ++------ requirements.in | 17 -- requirements.txt | 231 ------------------ run_benchmarks.sh | 127 ++++++++++ 7 files changed, 411 insertions(+), 725 deletions(-) delete mode 100644 imread_benchmark/benchmark.py create mode 100644 imread_benchmark/benchmark_single.py delete mode 100644 requirements.in delete mode 100644 requirements.txt create mode 100755 run_benchmarks.sh diff --git a/README.md b/README.md index 0310f66..879e5a9 100644 --- a/README.md +++ b/README.md @@ -1,101 +1,120 @@ -[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black) +# Image Loading Benchmark + [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) -# Image Loading Benchmark: From JPG to RGB Numpy Arrays +## Overview -![Benchmark-2024-06-05](images/2024-06-05.png) +This benchmark evaluates the efficiency of different libraries in loading JPG images +and converting them into RGB numpy arrays, essential for neural network training +data preparation. Inspired by the [Albumentations library]( +https://github.com/albumentations-team/albumentations/). -This benchmark evaluates the efficiency of different libraries in loading JPG images and converting them into RGB numpy arrays, essential for neural network training data preparation. Inspired by the [Albumentations library](https://github.com/albumentations-team/albumentations/). +![Benchmark-2024-06-05](images/2024-06-05.png) ## Important Note on Image Conversion -In the benchmark, it's crucial to standardize image formats for a fair comparison, despite different default formats used by OpenCV (BGR), torchvision, and TensorFlow (tensors). A conversion step to RGB numpy arrays is included for consistency. Note that in typical use cases, torchvision and TensorFlow do not require this conversion. Preliminary analysis shows that this extra step does not significantly impact the comparative performance of the libraries, ensuring that the benchmark accurately reflects realistic end-to-end image loading and preprocessing times. +In the benchmark, it's crucial to standardize image formats for a fair comparison. +Different libraries use different default formats: OpenCV (BGR), torchvision and +TensorFlow (tensors). A conversion step to RGB numpy arrays is included for +consistency. Note that in typical use cases, torchvision and TensorFlow do not +require this conversion. ## Installation and Setup -Before running the benchmark, ensure your system is equipped with the necessary dependencies. Start by installing `libturbojpeg`: +Before running the benchmark, ensure your system is equipped with the necessary +dependencies: + +### System Requirements ```bash +# On Ubuntu/Debian sudo apt-get install libturbojpeg + +# On macOS +brew install libjpeg-turbo ``` -Next, install all required Python libraries listed in `requirements.txt`: +### Python Setup + +The benchmark uses separate virtual environments for each library to avoid +dependency conflicts. You'll need: ```bash -sudo apt install requirements.txt +# Install uv for faster package installation +pip install uv ``` -Note: If you want to update package versions in `requirements.txt` +## Running the Benchmark -```bash -pip install pip-tools -``` +The benchmark script creates separate virtual environments for each library and +runs tests independently: ```bash -pip-compile requirements.in -``` -this will create new `requirements.txt` file +# Make the script executable +chmod +x run_benchmarks.sh -```bash -pip install -r requirements.txt +# Show help and options +./run_benchmarks.sh --help + +# Run benchmark with default settings (2000 images, 5 runs) +./run_benchmarks.sh /path/to/images + +# Run with custom settings +./run_benchmarks.sh /path/to/images 1000 3 ``` -to install latest versions -## Running the Benchmark +The script will: -To understand the benchmark's configuration options and run it according to your setup, use the following commands: +1. Create separate virtual environments for each library +2. Install required dependencies using `uv` +3. Run benchmarks independently +4. Save results to OS-specific directories -```bash -python imread_benchmark/benchmark.py -h - -usage: benchmark.py [-h] [-d DIR] [-n N] [-r N] [--show-std] [-m] [-p] [-s] [-o OUTPUT_PATH] - -Image reading libraries performance benchmark - -options: - -h, --help show this help message and exit - -d DIR, --data-dir DIR - path to a directory with images - -n N, --num_images N number of images for benchmarking (default: 2000) - -r N, --num_runs N number of runs for each benchmark (default: 5) - --show-std show standard deviation for benchmark runs - -m, --markdown print benchmarking results as a markdown table - -p, --print-package-versions - print versions of packages - -s, --shuffle Shuffle the list of images. - -o OUTPUT_PATH, --output_path OUTPUT_PATH - Path to save resulting dataframe. -``` +### Results Structure +Results are saved in JSON format under: -```bash -python imread_benchmark/benchmark.py \ - --data-dir \ - --num_images \ - --num_runs \ - --show-std \ - --print-package-versions \ - --print-package-versions +```text +output/ +├── linux/ # When run on Linux +│ ├── opencv_results.json +│ ├── pil_results.json +│ └── ... +└── darwin/ # When run on macOS + ├── opencv_results.json + ├── pil_results.json + └── ... ``` -Extra options: -`--print-package-versions` - to print benchmarked libraries versions -`--print-package-versions` - to shuffle images on every run -`--show-std` - to show standard deviation for measurements +## Libraries Being Benchmarked + +- OpenCV (opencv-python-headless) +- PIL (Pillow) +- Pillow-SIMD (pillow-simd) +- scikit-image +- imageio +- torchvision +- tensorflow +- kornia-rs +- jpeg4py ## Hardware and Software Specifications **CPU**: AMD Ryzen Threadripper 3970X 32-Core Processor -## Results +## Latest Results | | Library | Version | Performance (images/sec) | |---:|:-----------------------|:----------|:---------------------------| -| 0 | scikit-image | 0.23.2 | 538.48 ± 6.86 | -| 1 | imageio | 2.34.1 | 538.58 ± 6.84 | -| 2 | opencv-python-headless | 4.10.0.82 | 631.46 ± 0.43 | -| 3 | pillow | 10.3.0 | 589.56 ± 8.79 | -| 4 | jpeg4py | 0.1.4 | 700.60 ± 0.88 | -| 5 | torchvision | 0.18.1 | 658.68 ± 0.78 | -| 6 | tensorflow | 2.16.1 | 704.43 ± 1.10 | -| 7 | kornia-rs | 0.1.1 | 682.95 ± 1.21 | +| 0 | scikit-image | 0.23.2 | 538.48 ± 6.86 | +| 1 | imageio | 2.34.1 | 538.58 ± 6.84 | +| 2 | opencv-python-headless| 4.10.0.82 | 631.46 ± 0.43 | +| 3 | pillow | 10.3.0 | 589.56 ± 8.79 | +| 4 | jpeg4py | 0.1.4 | 700.60 ± 0.88 | +| 5 | torchvision | 0.18.1 | 658.68 ± 0.78 | +| 6 | tensorflow | 2.16.1 | 704.43 ± 1.10 | +| 7 | kornia-rs | 0.1.1 | 682.95 ± 1.21 | + +## Contributing + +Feel free to submit issues and enhancement requests! diff --git a/imread_benchmark/benchmark.py b/imread_benchmark/benchmark.py deleted file mode 100644 index d35d046..0000000 --- a/imread_benchmark/benchmark.py +++ /dev/null @@ -1,335 +0,0 @@ -import argparse -import logging -import os -import random -import sys -import time -from abc import ABC -from collections import defaultdict -from pathlib import Path - -import cv2 -import imageio.v2 as imageio -import jpeg4py -import kornia_rs as K -import numpy as np -import pandas as pd -import pkg_resources -import skimage -import tensorflow as tf -import torchvision -from PIL import Image -from pytablewriter import MarkdownTableWriter -from pytablewriter.style import Style -from tqdm import tqdm - -cv2.setNumThreads(0) -cv2.ocl.setUseOpenCL(False) - -os.environ["OMP_NUM_THREADS"] = "1" -os.environ["OPENBLAS_NUM_THREADS"] = "1" -os.environ["MKL_NUM_THREADS"] = "1" -os.environ["VECLIB_MAXIMUM_THREADS"] = "1" -os.environ["NUMEXPR_NUM_THREADS"] = "1" -os.environ["CUDA_VISIBLE_DEVICES"] = "" - -# Set up logging -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -try: - # Attempt to disable all GPUs - tf.config.set_visible_devices([], "GPU") - visible_devices = tf.config.get_visible_devices() - for device in visible_devices: - if device.device_type == "GPU": - logger.warning("GPU device is still visible, disabling failed.") -except tf.errors.NotFoundError: # Example of catching a more specific TensorFlow exception - logger.exception("Specific TensorFlow error encountered when trying to modify GPU visibility.") -except Exception: # Use this as a fallback if you're unsure which specific exceptions might be raised - logger.exception("Failed to modify GPU visibility due to an unexpected error.") - - -package_mapping = { - "opencv": "opencv-python-headless", # or "opencv-python" depending on which you use - "pil": "pillow", - "jpeg4py": "jpeg4py", - "skimage": "scikit-image", - "imageio": "imageio", - "torchvision": "torchvision", - "tensorflow": "tensorflow", - "kornia": "kornia-rs", -} - - -def get_package_versions(): - # Mapping of import names to package names as they might differ - - versions = {"Python": sys.version.split()[0]} # Just get the major.minor.patch - for package, dist_name in package_mapping.items(): - try: - versions[package] = pkg_resources.get_distribution(dist_name).version - except pkg_resources.DistributionNotFound: - versions[package] = "Not Installed" - return versions - - -class BenchmarkTest(ABC): - def __str__(self): - return self.__class__.__name__ - - def run(self, library, image_paths: list) -> None: - operation = getattr(self, library) - for image in image_paths: - operation(image) - - -class GetArray(BenchmarkTest): - def pil(self, image_path: str) -> np.array: - img = Image.open(image_path) - img = img.convert("RGB") - return np.asarray(img) - - def opencv(self, image_path: str) -> np.array: - img = cv2.imread(image_path) - return cv2.cvtColor(img, cv2.COLOR_BGR2RGB) - - def jpeg4py(self, image_path: str) -> np.array: - return jpeg4py.JPEG(image_path).decode() - - def skimage(self, image_path: str) -> np.array: - return skimage.io.imread(image_path) - - def imageio(self, image_path: str) -> np.array: - return imageio.imread(image_path) - - def torchvision(self, image_path: str) -> np.array: - image = torchvision.io.read_image(image_path) - return image.permute(1, 2, 0).numpy() - - def tensorflow(self, image_path: str) -> np.array: - # Read the image file - image_string = tf.io.read_file(image_path) - # Decode the image to tensor - image = tf.io.decode_image(image_string, channels=3) - # Convert the tensor to numpy array - return image.numpy() - - def kornia(self, image_path: str) -> np.array: - return K.read_image_jpeg(image_path) - - -class MarkdownGenerator: - def __init__(self, df, package_versions): - self._df = df - self._package_versions = package_versions - - def _highlight_best_result(self, results) -> list[str]: - # Convert all results to floats for comparison, filtering out any non-numeric values beforehand - numeric_results = [float(r) for r in results if r.replace(".", "", 1).isdigit()] - - if not numeric_results: - return results # Return the original results if no numeric values were found - - best_result = max(numeric_results) - - # Highlight the best result by comparing the float representation of each result - return [f"**{r}**" if float(r) == best_result else r for r in results] - - def _make_headers(self) -> list[str]: - libraries = self._df.columns.to_list() - columns = [] - for library in libraries: - version = self._package_versions[ - ( - library.replace("opencv", "opencv-python-headless") - .replace("pil", "pillow") - .replace("skimage", "scikit-image") - .replace("kornia", "kornia-rs") - ) - ] - - columns.append(f"{library}
{version}") - return ["", *columns] - - def _make_value_matrix(self) -> list[list]: - index = self._df.index.tolist() - values = self._df.to_numpy().tolist() - value_matrix = [] - for transform, results in zip(index, values, strict=False): - row = [transform, *self._highlight_best_result(results)] - value_matrix.append(row) - return value_matrix - - def _make_versions_text(self) -> str: - libraries = [ - "Python", - "numpy", - "pillow", - "opencv-python-headless", - "scikit-image", - "scipy", - "tensorflow", - "kornia-rs", - ] - libraries_with_versions = [ - "{library} {version}".format(library=library, version=self._package_versions[library].replace("\n", "")) - for library in libraries - ] - return f"Python and library versions: {', '.join(libraries_with_versions)}." - - def print(self) -> None: - writer = MarkdownTableWriter() - writer.headers = self._make_headers() - writer.value_matrix = self._make_value_matrix() - writer.styles = [Style(align="left")] + [Style(align="center") for _ in range(len(writer.headers) - 1)] - writer.write_table() - - -def run_single_benchmark(benchmark, library, image_paths): - """ - Runs a single benchmark for a given library and set of image paths. - Returns the images per second performance. - """ - start_time = time.perf_counter() - benchmark.run(library, image_paths) - end_time = time.perf_counter() - - run_time = end_time - start_time - return len(image_paths) / run_time - - -def warm_up(libraries, benchmarks, image_paths, warmup_runs, shuffle_paths): - """Performs warm-up runs for each library to ensure fair timing.""" - for library in tqdm(libraries, desc="Warming up libraries"): - for _ in tqdm(range(warmup_runs), desc=f"Warm-up runs for {library}"): - for benchmark in benchmarks: - if shuffle_paths: - random.shuffle(image_paths) - benchmark.run(library, image_paths) - - -def perform_benchmark(libraries, benchmarks, image_paths, num_runs, shuffle_paths): - """Main benchmarking logic, performing the benchmark for each library and benchmark combination.""" - images_per_second = defaultdict(lambda: defaultdict(list)) - - # for _ in range(num_runs): - for _ in tqdm(range(num_runs), desc="Benchmarking Runs"): - shuffled_libraries = libraries.copy() - random.shuffle(shuffled_libraries) # Shuffle library order for each run - - for library in tqdm(shuffled_libraries, desc="Libraries"): - # for library in shuffled_libraries: - for benchmark in benchmarks: - if shuffle_paths: - random.shuffle(image_paths) - - ips = run_single_benchmark(benchmark, library, image_paths) - images_per_second[library][str(benchmark)].append(ips) - - return images_per_second - - -def calculate_results(images_per_second): - """Calculates the average and standard deviation of images per second for each library and benchmark.""" - final_results = defaultdict(dict) - for library, benchmarks in images_per_second.items(): - for benchmark, times in benchmarks.items(): - avg_ips = np.mean(times) - std_ips = np.std(times) if len(times) > 1 else 0 - final_results[library][benchmark] = f"{avg_ips:.2f} ± {std_ips:.2f}" - - return final_results - - -def benchmark( - libraries: list, - benchmarks: list, - image_paths: list, - num_runs: int, - shuffle_paths: bool, - warmup_runs: int = 1, -) -> defaultdict: - """Orchestrates the benchmarking process, including warm-up, main benchmark, and result calculation.""" - # Warm-up phase - warm_up(libraries, benchmarks, image_paths, warmup_runs, shuffle_paths) - - # Main benchmarking - images_per_second = perform_benchmark(libraries, benchmarks, image_paths, num_runs, shuffle_paths) - - # Calculate and return final results - return calculate_results(images_per_second) - - -def parse_args(): - parser = argparse.ArgumentParser(description="Image reading libraries performance benchmark") - parser.add_argument("-d", "--data-dir", metavar="DIR", help="path to a directory with images") - parser.add_argument( - "-n", - "--num_images", - default=2000, - type=int, - metavar="N", - help="number of images for benchmarking (default: 2000)", - ) - parser.add_argument( - "-r", - "--num_runs", - default=5, - type=int, - metavar="N", - help="number of runs for each benchmark (default: 5)", - ) - parser.add_argument( - "--show-std", - dest="show_std", - action="store_true", - help="show standard deviation for benchmark runs", - ) - parser.add_argument("-m", "--markdown", action="store_true", help="print benchmarking results as a markdown table") - parser.add_argument("-p", "--print-package-versions", action="store_true", help="print versions of packages") - parser.add_argument("-s", "--shuffle", action="store_true", help="Shuffle the list of images.") - parser.add_argument("-o", "--output_path", type=Path, help="Path to save resulting dataframe.", default="output") - return parser.parse_args() - - -def get_image_paths(data_dir: str | Path, num_images: int) -> list: - image_paths = sorted(Path(data_dir).glob("*.*")) - return [str(x) for x in image_paths[:num_images]] - - -def main() -> None: - args = parse_args() - - Path(args.output_path).mkdir(parents=True, exist_ok=True) - - package_versions = get_package_versions() - - benchmarks = [GetArray()] # Add more benchmark classes as needed - libraries = ["skimage", "imageio", "opencv", "pil", "jpeg4py", "torchvision", "tensorflow", "kornia"] - - image_paths = get_image_paths(args.data_dir, args.num_images) - images_per_second = benchmark(libraries, benchmarks, image_paths, args.num_runs, args.shuffle) - - # Convert the results to a DataFrame - results = defaultdict(list) - for library in libraries: - for perf in images_per_second[library].values(): - results["Library"].append(package_mapping[library]) - results["Version"].append(package_versions.get(library, "Unknown")) - results["Performance (images/sec)"].append(perf) - - df = pd.DataFrame(results) - - if args.output_path: - df.to_csv(args.output_path, index=False) - - if args.markdown: - # Convert dataframe to markdown table - print(df.to_markdown()) - - return df # Return the dataframe if needed - - -if __name__ == "__main__": - df = main() diff --git a/imread_benchmark/benchmark_single.py b/imread_benchmark/benchmark_single.py new file mode 100644 index 0000000..4506d98 --- /dev/null +++ b/imread_benchmark/benchmark_single.py @@ -0,0 +1,190 @@ +import argparse +import json +import logging +import os +import platform +import sys +import time +from importlib.metadata import version +from pathlib import Path + +import numpy as np +from tqdm import tqdm + +# Set up logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +SUPPORTED_LIBRARIES = { + "opencv": "opencv-python-headless", + "pil": "pillow", + "jpeg4py": "jpeg4py", + "skimage": "scikit-image", + "imageio": "imageio", + "torchvision": "torchvision", + "tensorflow": "tensorflow", + "kornia": "kornia-rs", +} + + +def get_package_versions(): + import multiprocessing + + import cpuinfo + + # Get CPU info + try: + cpu_info = cpuinfo.get_cpu_info() + cpu_details = { + "brand_raw": cpu_info.get("brand_raw", "Unknown"), + "arch": cpu_info.get("arch", "Unknown"), + "hz_advertised_raw": cpu_info.get("hz_advertised_raw", "Unknown"), + "count": multiprocessing.cpu_count(), + } + except Exception as e: + logger.warning(f"Failed to get CPU info: {e}") + cpu_details = {"error": str(e)} + + versions = { + "Python": sys.version.split()[0], + "OS": platform.system(), + "OS Version": platform.version(), + "Machine": platform.machine(), + "CPU": cpu_details, + } + + lib_name = os.environ.get("BENCHMARK_LIBRARY") + if lib_name: + pkg_name = SUPPORTED_LIBRARIES.get(lib_name) + if pkg_name: + try: + versions[lib_name] = version(pkg_name) + except Exception as e: + versions[lib_name] = f"Error getting version: {e!s}" + + return versions + + +def setup_library(): + """Set up the image reading function based on the specified library.""" + library = os.environ.get("BENCHMARK_LIBRARY") + if not library: + raise ValueError("BENCHMARK_LIBRARY environment variable must be set") + + if library == "opencv": + import cv2 + + def read_image(path): + return cv2.imread(path, cv2.IMREAD_COLOR_RGB) + + elif library in {"pillow", "pillow-simd"}: + from PIL import Image + + def read_image(path): + img = Image.open(path) + img = img.convert("RGB") + return np.asarray(img) + + elif library == "jpeg4py": + import jpeg4py + + def read_image(path): + return jpeg4py.JPEG(path).decode() + + elif library == "skimage": + import skimage.io + + def read_image(path): + return skimage.io.imread(path) + + elif library == "imageio": + import imageio.v2 as imageio + + def read_image(path): + return imageio.imread(path) + + elif library == "torchvision": + import torchvision + + def read_image(path): + image = torchvision.io.read_image(path) + return image.permute(1, 2, 0).numpy() + + elif library == "tensorflow": + import tensorflow as tf + + def read_image(path): + image_string = tf.io.read_file(path) + image = tf.io.decode_image(image_string, channels=3) + return image.numpy() + + elif library == "kornia": + import kornia_rs as K + + def read_image(path): + return K.read_image_jpeg(path) + + else: + raise ValueError(f"Unsupported library: {library}") + + return library, read_image + + +def run_benchmark(read_image, image_paths, num_runs): + times = [] + for _ in tqdm(range(num_runs), desc="Benchmarking"): + start_time = time.perf_counter() + for path in image_paths: + read_image(path) + end_time = time.perf_counter() + + run_time = end_time - start_time + images_per_second = len(image_paths) / run_time + times.append(images_per_second) + + avg_ips = np.mean(times) + std_ips = np.std(times) + + return { + "images_per_second": f"{avg_ips:.2f} ± {std_ips:.2f}", + "raw_times": times, + } + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-d", "--data-dir", required=True, help="Path to image directory") + parser.add_argument("-n", "--num-images", type=int, default=2000) + parser.add_argument("-r", "--num-runs", type=int, default=5) + parser.add_argument("-o", "--output-dir", type=Path, required=True) + args = parser.parse_args() + + # Set up library and get read function once at startup + library, read_image = setup_library() + + # Create output directory + os_name = platform.system().lower() + output_dir = args.output_dir / os_name + output_dir.mkdir(parents=True, exist_ok=True) + + # Get image paths + image_paths = sorted(Path(args.data_dir).glob("*.*"))[: args.num_images] + image_paths = [str(x) for x in image_paths] + + # Run benchmark + results = { + "library": library, + "system_info": get_package_versions(), + "benchmark_results": run_benchmark(read_image, image_paths, args.num_runs), + "num_images": args.num_images, + "num_runs": args.num_runs, + } + + # Save results + output_file = output_dir / f"{library}_results.json" + with output_file.open("w") as f: + json.dump(results, f, indent=2) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 43a6d88..ddb7340 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ [tool.black] line-length = 120 target-version = [ - "py310", + "py312", ] include = '\.pyi?$' exclude = ''' @@ -27,8 +27,7 @@ exclude = ''' ''' [tool.ruff] -# Assume Python 3.10 -target-version = "py310" +target-version = "py312" # Same as Black. line-length = 120 @@ -75,91 +74,24 @@ format.line-ending = "auto" # Like Black, respect magic trailing commas. format.skip-magic-trailing-comma = false lint.select = [ - "A", - "ANN", - "ARG", - "ASYNC", - "B", - "BLE", - "C4", - "C90", - "COM", - "CPY", - "D", - "DJ", - "DTZ", - "E", - "EM", - "ERA", - "EXE", - "F", - "FBT", - "FIX", - "FLY", - "FURB", - "G", - "I", - "ICN", - "INP", - "INT", - "ISC", - "LOG", - "N", - "NPY", - "PD", - "PERF", - "PGH", - "PIE", - "PL", - "PT", - "PTH", - "PYI", - "Q", - "RET", - "RSE", - "RUF", - "S", - "SIM", - "SLF", - "SLOT", - "T10", - "T20", - "TCH", - "TD", - "TID", - "TRIO", - "TRY", - "UP", - "W", - "YTT", + "ALL", ] lint.ignore = [ "ANN001", - "ANN101", "ANN201", - "ANN204", - "B024", - "COM812", + "ANN202", + "BLE001", + "C901", "D100", - "D101", - "D102", "D103", - "D104", - "D105", - "D107", "D203", - "D205", - "D211", - "D212", - "D401", - "FBT001", - "ISC001", + "EM101", + "EM102", + "G004", "N812", - "PD901", - "PERF203", - "PLR0913", - "T201", + "TRY003", ] + lint.explicit-preview-rules = true # Allow fix for all enabled rules (when `--fix`) is provided. lint.fixable = [ @@ -167,11 +99,12 @@ lint.fixable = [ ] lint.unfixable = [ ] + # Allow unused variables when underscore-prefixed. lint.dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$" [tool.mypy] -python_version = "3.10" +python_version = "3.12" ignore_missing_imports = true follow_imports = "silent" warn_redundant_casts = true diff --git a/requirements.in b/requirements.in deleted file mode 100644 index 3332802..0000000 --- a/requirements.in +++ /dev/null @@ -1,17 +0,0 @@ --f https://download.pytorch.org/whl/torch_stable.html - -imageio -jpeg4py -kornia-rs -matplotlib -numpy -opencv-python-headless -pandas -pillow -pytablewriter -scikit-image -tensorflow -torchvision -tqdm -tabulate -seaborn diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 6003239..0000000 --- a/requirements.txt +++ /dev/null @@ -1,231 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile requirements.in -absl-py==2.1.0 - # via - # keras - # tensorboard - # tensorflow -astunparse==1.6.3 - # via tensorflow -certifi==2024.12.14 - # via requests -cffi==1.17.1 - # via jpeg4py -chardet==5.2.0 - # via mbstrdecoder -charset-normalizer==3.4.1 - # via requests -contourpy==1.3.1 - # via matplotlib -cycler==0.12.1 - # via matplotlib -dataproperty==1.1.0 - # via - # pytablewriter - # tabledata -filelock==3.16.1 - # via torch -flatbuffers==24.12.23 - # via tensorflow -fonttools==4.55.3 - # via matplotlib -fsspec==2024.12.0 - # via torch -gast==0.6.0 - # via tensorflow -google-pasta==0.2.0 - # via tensorflow -grpcio==1.69.0 - # via - # tensorboard - # tensorflow -h5py==3.12.1 - # via - # keras - # tensorflow -idna==3.10 - # via requests -imageio==2.37.0 - # via - # -r requirements.in - # scikit-image -jinja2==3.1.5 - # via torch -jpeg4py==0.1.4 - # via -r requirements.in -keras==3.8.0 - # via tensorflow -kiwisolver==1.4.8 - # via matplotlib -kornia-rs==0.1.8 - # via -r requirements.in -lazy-loader==0.4 - # via scikit-image -libclang==18.1.1 - # via tensorflow -markdown==3.7 - # via tensorboard -markdown-it-py==3.0.0 - # via rich -markupsafe==3.0.2 - # via - # jinja2 - # werkzeug -matplotlib==3.10.0 - # via - # -r requirements.in - # seaborn -mbstrdecoder==1.1.4 - # via - # dataproperty - # pytablewriter - # typepy -mdurl==0.1.2 - # via markdown-it-py -ml-dtypes==0.4.1 - # via - # keras - # tensorflow -mpmath==1.3.0 - # via sympy -namex==0.0.8 - # via keras -networkx==3.4.2 - # via - # scikit-image - # torch -numpy==2.0.2 - # via - # -r requirements.in - # contourpy - # h5py - # imageio - # jpeg4py - # keras - # matplotlib - # ml-dtypes - # opencv-python-headless - # pandas - # scikit-image - # scipy - # seaborn - # tensorboard - # tensorflow - # tifffile - # torchvision -opencv-python-headless==4.11.0.86 - # via -r requirements.in -opt-einsum==3.4.0 - # via tensorflow -optree==0.14.0 - # via keras -packaging==24.2 - # via - # keras - # lazy-loader - # matplotlib - # scikit-image - # tensorboard - # tensorflow - # typepy -pandas==2.2.3 - # via - # -r requirements.in - # seaborn -pathvalidate==3.2.3 - # via pytablewriter -pillow==11.1.0 - # via - # -r requirements.in - # imageio - # matplotlib - # scikit-image - # torchvision -protobuf==5.29.3 - # via - # tensorboard - # tensorflow -pycparser==2.22 - # via cffi -pygments==2.19.1 - # via rich -pyparsing==3.2.1 - # via matplotlib -pytablewriter==1.2.1 - # via -r requirements.in -python-dateutil==2.9.0.post0 - # via - # matplotlib - # pandas - # typepy -pytz==2024.2 - # via - # pandas - # typepy -requests==2.32.3 - # via tensorflow -rich==13.9.4 - # via keras -scikit-image==0.25.0 - # via -r requirements.in -scipy==1.15.1 - # via scikit-image -seaborn==0.13.2 - # via -r requirements.in -setuptools==75.8.0 - # via - # pytablewriter - # tensorboard - # tensorflow - # torch -six==1.17.0 - # via - # astunparse - # google-pasta - # python-dateutil - # tensorboard - # tensorflow -sympy==1.13.1 - # via torch -tabledata==1.3.4 - # via pytablewriter -tabulate==0.9.0 - # via -r requirements.in -tcolorpy==0.1.7 - # via pytablewriter -tensorboard==2.18.0 - # via tensorflow -tensorboard-data-server==0.7.2 - # via tensorboard -tensorflow==2.18.0 - # via -r requirements.in -termcolor==2.5.0 - # via tensorflow -tifffile==2025.1.10 - # via scikit-image -torch==2.5.1 - # via torchvision -torchvision==0.20.1 - # via -r requirements.in -tqdm==4.67.1 - # via -r requirements.in -typepy==1.3.4 - # via - # dataproperty - # pytablewriter - # tabledata -typing-extensions==4.12.2 - # via - # optree - # tensorflow - # torch -tzdata==2024.2 - # via pandas -urllib3==2.3.0 - # via requests -werkzeug==3.1.3 - # via tensorboard -wheel==0.45.1 - # via astunparse -wrapt==1.17.2 - # via tensorflow diff --git a/run_benchmarks.sh b/run_benchmarks.sh new file mode 100755 index 0000000..0a7d6be --- /dev/null +++ b/run_benchmarks.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +# Function to show help message +show_help() { + cat << EOF +Usage: ./run_benchmarks.sh [num_images] [num_runs] + +This script runs image reading benchmarks for multiple Python libraries. +It creates separate virtual environments for each library and saves results +to output//_results.json + +Arguments: + path_to_image_directory (Required) Directory containing images to benchmark + num_images (Optional) Number of images to process (default: 2000) + num_runs (Optional) Number of benchmark runs (default: 5) + +Example usage: + # Basic usage with defaults (2000 images, 5 runs): + ./run_benchmarks.sh ~/dataset/images + + # Custom number of images and runs: + ./run_benchmarks.sh ~/dataset/images 1000 3 + +Libraries being benchmarked: + - opencv (opencv-python-headless) + - pil (Pillow) + - skimage (scikit-image) + - imageio + - torchvision + - tensorflow + - kornia (kornia-rs) + +Results will be saved in: + output/ + ├── linux/ # When run on Linux + │ ├── opencv_results.json + │ ├── pil_results.json + │ └── ... + └── darwin/ # When run on macOS + ├── opencv_results.json + ├── pil_results.json + └── ... +EOF +} + +# Show help if -h or --help is passed +if [[ "$1" == "-h" || "$1" == "--help" ]]; then + show_help + exit 0 +fi + +# Exit on error +set -e + +# Base directory for virtual environments +VENV_DIR="venvs" +mkdir -p "$VENV_DIR" + +# Create output directory +mkdir -p output + +# List of libraries to benchmark +LIBRARIES=("opencv" "pillow" "jpeg4py" "skimage" "imageio" "torchvision" "tensorflow" "kornia" "pillow-simd") + +# Function to create and activate virtual environment +setup_venv() { + local lib=$1 + echo "Setting up environment for $lib..." + python -m venv "$VENV_DIR/$lib" + + # Activate virtual environment (works on both Unix and Windows) + if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then + source "$VENV_DIR/$lib/Scripts/activate" + else + source "$VENV_DIR/$lib/bin/activate" + fi + + pip install uv + + # Install requirements + uv pip install -r requirements/base.txt + uv pip install -r "requirements/$lib.txt" +} + +# Function to run benchmark for a single library +run_benchmark() { + local lib=$1 + echo "Running benchmark for $lib..." + export BENCHMARK_LIBRARY=$lib + python imread_benchmark/benchmark_single.py \ + --data-dir "$DATA_DIR" \ + --num-images "$NUM_IMAGES" \ + --num-runs "$NUM_RUNS" \ + --output-dir output +} + +# Check if required arguments are provided +if [ -z "$1" ]; then + echo "Error: Image directory path is required" + echo + show_help + exit 1 +fi + +DATA_DIR=$1 +NUM_IMAGES=${2:-2000} +NUM_RUNS=${3:-5} + +echo "Starting benchmarks with:" +echo " Image directory: $DATA_DIR" +echo " Number of images: $NUM_IMAGES" +echo " Number of runs: $NUM_RUNS" +echo + +# Run benchmarks for each library +for lib in "${LIBRARIES[@]}"; do + echo "Processing $lib..." + setup_venv "$lib" + run_benchmark "$lib" + deactivate + echo "Completed $lib" + echo +done + +echo "All benchmarks completed!" +echo "Results are saved in the output directory organized by operating system." +echo "Check output/$(uname -s | tr '[:upper:]' '[:lower:]')/ for results." From 9fdd8d98566095b2be0c76859ff75bb82b921177 Mon Sep 17 00:00:00 2001 From: Vladimir Iglovikov Date: Mon, 20 Jan 2025 16:24:25 -0800 Subject: [PATCH 02/10] works --- output/darwin/imageio_results.json | 43 ++++++++++++++++++++++++++ output/darwin/kornia_results.json | 43 ++++++++++++++++++++++++++ output/darwin/opencv_results.json | 43 ++++++++++++++++++++++++++ output/darwin/pillow_results.json | 42 +++++++++++++++++++++++++ output/darwin/skimage_results.json | 43 ++++++++++++++++++++++++++ output/darwin/tensorflow_results.json | 43 ++++++++++++++++++++++++++ output/darwin/torchvision_results.json | 43 ++++++++++++++++++++++++++ requirements/base.txt | 5 +++ requirements/imageio.txt | 1 + requirements/jpeg4py.txt | 1 + requirements/kornia.txt | 1 + requirements/opencv.txt | 1 + requirements/pillow-simd.txt | 1 + requirements/pillow.txt | 1 + requirements/skimage.txt | 1 + requirements/tensorflow.txt | 1 + requirements/torchvision.txt | 4 +++ 17 files changed, 317 insertions(+) create mode 100644 output/darwin/imageio_results.json create mode 100644 output/darwin/kornia_results.json create mode 100644 output/darwin/opencv_results.json create mode 100644 output/darwin/pillow_results.json create mode 100644 output/darwin/skimage_results.json create mode 100644 output/darwin/tensorflow_results.json create mode 100644 output/darwin/torchvision_results.json create mode 100644 requirements/base.txt create mode 100644 requirements/imageio.txt create mode 100644 requirements/jpeg4py.txt create mode 100644 requirements/kornia.txt create mode 100644 requirements/opencv.txt create mode 100644 requirements/pillow-simd.txt create mode 100644 requirements/pillow.txt create mode 100644 requirements/skimage.txt create mode 100644 requirements/tensorflow.txt create mode 100644 requirements/torchvision.txt diff --git a/output/darwin/imageio_results.json b/output/darwin/imageio_results.json new file mode 100644 index 0000000..9400878 --- /dev/null +++ b/output/darwin/imageio_results.json @@ -0,0 +1,43 @@ +{ + "library": "imageio", + "system_info": { + "Python": "3.12.7", + "OS": "Darwin", + "OS Version": "Darwin Kernel Version 24.1.0: Thu Oct 10 21:06:57 PDT 2024; root:xnu-11215.41.3~3/RELEASE_ARM64_T6041", + "Machine": "arm64", + "CPU": { + "brand_raw": "Apple M4 Max", + "arch": "ARM_8", + "hz_advertised_raw": "Unknown", + "count": 16 + }, + "imageio": "2.37.0" + }, + "benchmark_results": { + "images_per_second": "777.49 \u00b1 11.70", + "raw_times": [ + 773.3362551778018, + 788.0979997280438, + 787.738650909651, + 787.4840179428744, + 780.3523028334013, + 781.6396623215167, + 774.687086672151, + 730.0676139616584, + 782.6431786574269, + 783.7668689780774, + 782.1544299803793, + 776.4593567244634, + 780.9970655883873, + 777.2857290199994, + 779.7222317321122, + 773.8445318199068, + 776.4496607142421, + 777.8145009024621, + 778.6917997557493, + 776.5908468776514 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/darwin/kornia_results.json b/output/darwin/kornia_results.json new file mode 100644 index 0000000..efc50c3 --- /dev/null +++ b/output/darwin/kornia_results.json @@ -0,0 +1,43 @@ +{ + "library": "kornia", + "system_info": { + "Python": "3.12.7", + "OS": "Darwin", + "OS Version": "Darwin Kernel Version 24.1.0: Thu Oct 10 21:06:57 PDT 2024; root:xnu-11215.41.3~3/RELEASE_ARM64_T6041", + "Machine": "arm64", + "CPU": { + "brand_raw": "Apple M4 Max", + "arch": "ARM_8", + "hz_advertised_raw": "Unknown", + "count": 16 + }, + "kornia": "0.1.8" + }, + "benchmark_results": { + "images_per_second": "1034.47 \u00b1 18.42", + "raw_times": [ + 1030.0550536145663, + 1041.5514363711877, + 1040.1540186745633, + 1040.148000717588, + 1041.137196232814, + 1039.533367243049, + 1027.7022933070384, + 1031.7264922909167, + 1042.073160979155, + 956.1449571708179, + 1038.644207186712, + 1037.017753305197, + 1036.057342652493, + 1040.0293425434209, + 1041.4460601700132, + 1041.174481257482, + 1041.2124691574397, + 1040.290403644477, + 1041.7558305884224, + 1041.4534714855283 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/darwin/opencv_results.json b/output/darwin/opencv_results.json new file mode 100644 index 0000000..a3d6192 --- /dev/null +++ b/output/darwin/opencv_results.json @@ -0,0 +1,43 @@ +{ + "library": "opencv", + "system_info": { + "Python": "3.12.7", + "OS": "Darwin", + "OS Version": "Darwin Kernel Version 24.1.0: Thu Oct 10 21:06:57 PDT 2024; root:xnu-11215.41.3~3/RELEASE_ARM64_T6041", + "Machine": "arm64", + "CPU": { + "brand_raw": "Apple M4 Max", + "arch": "ARM_8", + "hz_advertised_raw": "Unknown", + "count": 16 + }, + "opencv": "4.11.0.86" + }, + "benchmark_results": { + "images_per_second": "1016.02 \u00b1 7.01", + "raw_times": [ + 1024.4635275982616, + 1026.0244252552316, + 1025.6640371358717, + 1026.4049077442344, + 1015.9824195523589, + 1025.3303809736785, + 1022.1921101041164, + 1019.3528599748722, + 1019.6086893660788, + 1018.4911311348419, + 1013.4911724175921, + 1005.5940569754418, + 1011.0163715516046, + 1013.2027282063441, + 1009.9494539517264, + 1009.2187296894285, + 1009.9681331064289, + 1009.4144297721575, + 1007.93437525726, + 1007.0939272684269 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/darwin/pillow_results.json b/output/darwin/pillow_results.json new file mode 100644 index 0000000..ca8cb6d --- /dev/null +++ b/output/darwin/pillow_results.json @@ -0,0 +1,42 @@ +{ + "library": "pillow", + "system_info": { + "Python": "3.12.7", + "OS": "Darwin", + "OS Version": "Darwin Kernel Version 24.1.0: Thu Oct 10 21:06:57 PDT 2024; root:xnu-11215.41.3~3/RELEASE_ARM64_T6041", + "Machine": "arm64", + "CPU": { + "brand_raw": "Apple M4 Max", + "arch": "ARM_8", + "hz_advertised_raw": "Unknown", + "count": 16 + } + }, + "benchmark_results": { + "images_per_second": "774.55 \u00b1 14.32", + "raw_times": [ + 755.4795761175405, + 796.0627798519542, + 789.9106813530816, + 785.3795446727694, + 783.9906368396933, + 779.205460746831, + 776.6398386515864, + 776.0803622195672, + 776.7283385085334, + 776.0572247091527, + 778.5114113115783, + 775.595449774128, + 777.5430408253858, + 772.7972036206445, + 773.4511599301802, + 722.002991586953, + 773.0171409330501, + 774.6960514977644, + 774.798228444907, + 772.987002960018 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/darwin/skimage_results.json b/output/darwin/skimage_results.json new file mode 100644 index 0000000..c28cfef --- /dev/null +++ b/output/darwin/skimage_results.json @@ -0,0 +1,43 @@ +{ + "library": "skimage", + "system_info": { + "Python": "3.12.7", + "OS": "Darwin", + "OS Version": "Darwin Kernel Version 24.1.0: Thu Oct 10 21:06:57 PDT 2024; root:xnu-11215.41.3~3/RELEASE_ARM64_T6041", + "Machine": "arm64", + "CPU": { + "brand_raw": "Apple M4 Max", + "arch": "ARM_8", + "hz_advertised_raw": "Unknown", + "count": 16 + }, + "skimage": "0.25.0" + }, + "benchmark_results": { + "images_per_second": "766.00 \u00b1 9.06", + "raw_times": [ + 766.8692176050578, + 783.7257132190405, + 785.1604027762115, + 782.9970622353658, + 777.9844534423798, + 769.247288272154, + 769.0387435805927, + 764.5407528267608, + 758.6448169794073, + 761.1050578579769, + 764.0291754555469, + 763.0891003677041, + 762.492994946151, + 760.8338820060599, + 760.6101431374296, + 760.9394920566515, + 755.0257821724493, + 756.1723513083739, + 758.7697182310162, + 758.8173748618219 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/darwin/tensorflow_results.json b/output/darwin/tensorflow_results.json new file mode 100644 index 0000000..1810853 --- /dev/null +++ b/output/darwin/tensorflow_results.json @@ -0,0 +1,43 @@ +{ + "library": "tensorflow", + "system_info": { + "Python": "3.12.7", + "OS": "Darwin", + "OS Version": "Darwin Kernel Version 24.1.0: Thu Oct 10 21:06:57 PDT 2024; root:xnu-11215.41.3~3/RELEASE_ARM64_T6041", + "Machine": "arm64", + "CPU": { + "brand_raw": "Apple M4 Max", + "arch": "ARM_8", + "hz_advertised_raw": "Unknown", + "count": 16 + }, + "tensorflow": "2.18.0" + }, + "benchmark_results": { + "images_per_second": "664.28 \u00b1 8.80", + "raw_times": [ + 653.2195612875155, + 659.194694745475, + 666.8145792211423, + 665.4012488359364, + 662.5056985564643, + 656.8600920071962, + 632.020921101284, + 668.058482984238, + 667.8843498303524, + 669.9456130250902, + 670.2474076612411, + 671.7269068251509, + 671.9704266827009, + 670.0983425982946, + 668.274925958546, + 668.2849465726157, + 667.8620934556499, + 664.2110231642316, + 664.6250124212706, + 666.3288934200496 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/darwin/torchvision_results.json b/output/darwin/torchvision_results.json new file mode 100644 index 0000000..363f86e --- /dev/null +++ b/output/darwin/torchvision_results.json @@ -0,0 +1,43 @@ +{ + "library": "torchvision", + "system_info": { + "Python": "3.12.7", + "OS": "Darwin", + "OS Version": "Darwin Kernel Version 24.1.0: Thu Oct 10 21:06:57 PDT 2024; root:xnu-11215.41.3~3/RELEASE_ARM64_T6041", + "Machine": "arm64", + "CPU": { + "brand_raw": "Apple M4 Max", + "arch": "ARM_8", + "hz_advertised_raw": "Unknown", + "count": 16 + }, + "torchvision": "0.20.1" + }, + "benchmark_results": { + "images_per_second": "992.20 \u00b1 17.15", + "raw_times": [ + 987.0900145449132, + 1007.415522739194, + 987.876796119717, + 994.1233847772254, + 982.7930140201138, + 985.7552611822863, + 993.1553799866899, + 983.3769754969791, + 1000.9418027519556, + 1006.9690865957836, + 1006.2856376033868, + 983.7422050660286, + 928.4764959985383, + 983.2389710143362, + 997.5434245862426, + 1004.565394113687, + 995.4654269216271, + 1004.3494609436391, + 1004.6521044775942, + 1006.1666273940092 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/requirements/base.txt b/requirements/base.txt new file mode 100644 index 0000000..b2da4ca --- /dev/null +++ b/requirements/base.txt @@ -0,0 +1,5 @@ +numpy +pandas +py-cpuinfo +pytablewriter +tqdm diff --git a/requirements/imageio.txt b/requirements/imageio.txt new file mode 100644 index 0000000..a464e4c --- /dev/null +++ b/requirements/imageio.txt @@ -0,0 +1 @@ +imageio diff --git a/requirements/jpeg4py.txt b/requirements/jpeg4py.txt new file mode 100644 index 0000000..37eeca0 --- /dev/null +++ b/requirements/jpeg4py.txt @@ -0,0 +1 @@ +jpeg4py diff --git a/requirements/kornia.txt b/requirements/kornia.txt new file mode 100644 index 0000000..f41272c --- /dev/null +++ b/requirements/kornia.txt @@ -0,0 +1 @@ +kornia-rs diff --git a/requirements/opencv.txt b/requirements/opencv.txt new file mode 100644 index 0000000..6ab6d0d --- /dev/null +++ b/requirements/opencv.txt @@ -0,0 +1 @@ +opencv-python-headless diff --git a/requirements/pillow-simd.txt b/requirements/pillow-simd.txt new file mode 100644 index 0000000..1a3a520 --- /dev/null +++ b/requirements/pillow-simd.txt @@ -0,0 +1 @@ +pillow-simd diff --git a/requirements/pillow.txt b/requirements/pillow.txt new file mode 100644 index 0000000..3868fb1 --- /dev/null +++ b/requirements/pillow.txt @@ -0,0 +1 @@ +pillow diff --git a/requirements/skimage.txt b/requirements/skimage.txt new file mode 100644 index 0000000..391ca2f --- /dev/null +++ b/requirements/skimage.txt @@ -0,0 +1 @@ +scikit-image diff --git a/requirements/tensorflow.txt b/requirements/tensorflow.txt new file mode 100644 index 0000000..0f57144 --- /dev/null +++ b/requirements/tensorflow.txt @@ -0,0 +1 @@ +tensorflow diff --git a/requirements/torchvision.txt b/requirements/torchvision.txt new file mode 100644 index 0000000..284c569 --- /dev/null +++ b/requirements/torchvision.txt @@ -0,0 +1,4 @@ +-f https://download.pytorch.org/whl/torch_stable.html + +torch +torchvision From cdfb40d31c79b76d51b12c1debc6b1c7cd9310ff Mon Sep 17 00:00:00 2001 From: Vladimir Iglovikov Date: Tue, 21 Jan 2025 13:08:38 -0800 Subject: [PATCH 03/10] added benchmarks for Linux --- .gitignore | 2 ++ output/linux/imageio_results.json | 43 +++++++++++++++++++++++++++ output/linux/jpeg4py_results.json | 43 +++++++++++++++++++++++++++ output/linux/kornia_results.json | 43 +++++++++++++++++++++++++++ output/linux/opencv_results.json | 43 +++++++++++++++++++++++++++ output/linux/pillow-simd_results.json | 42 ++++++++++++++++++++++++++ output/linux/pillow_results.json | 42 ++++++++++++++++++++++++++ output/linux/skimage_results.json | 43 +++++++++++++++++++++++++++ output/linux/tensorflow_results.json | 43 +++++++++++++++++++++++++++ output/linux/torchvision_results.json | 43 +++++++++++++++++++++++++++ run_benchmarks.sh | 20 +++++++++++-- 11 files changed, 404 insertions(+), 3 deletions(-) create mode 100644 output/linux/imageio_results.json create mode 100644 output/linux/jpeg4py_results.json create mode 100644 output/linux/kornia_results.json create mode 100644 output/linux/opencv_results.json create mode 100644 output/linux/pillow-simd_results.json create mode 100644 output/linux/pillow_results.json create mode 100644 output/linux/skimage_results.json create mode 100644 output/linux/tensorflow_results.json create mode 100644 output/linux/torchvision_results.json diff --git a/.gitignore b/.gitignore index 0f8b6df..d2eb9b3 100644 --- a/.gitignore +++ b/.gitignore @@ -108,3 +108,5 @@ venv.bak/ .idea/ .ruff_cache/ + +venvs/ diff --git a/output/linux/imageio_results.json b/output/linux/imageio_results.json new file mode 100644 index 0000000..ebcf41f --- /dev/null +++ b/output/linux/imageio_results.json @@ -0,0 +1,43 @@ +{ + "library": "imageio", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + }, + "imageio": "2.37.0" + }, + "benchmark_results": { + "images_per_second": "536.11 \u00b1 5.72", + "raw_times": [ + 516.8561561826256, + 536.1494348059053, + 536.7697513662142, + 541.2213418297825, + 535.802765009853, + 531.524755680685, + 535.5521861708272, + 537.932087738096, + 543.6772889887665, + 537.5952031308753, + 537.5911167712384, + 538.0330889614744, + 539.5160064991541, + 536.5376339359522, + 531.9434538921021, + 533.4716159923753, + 537.1596300638529, + 546.2547273940405, + 537.4879022237232, + 531.2139404446549 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/linux/jpeg4py_results.json b/output/linux/jpeg4py_results.json new file mode 100644 index 0000000..8713623 --- /dev/null +++ b/output/linux/jpeg4py_results.json @@ -0,0 +1,43 @@ +{ + "library": "jpeg4py", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + }, + "jpeg4py": "0.1.4" + }, + "benchmark_results": { + "images_per_second": "698.09 \u00b1 6.87", + "raw_times": [ + 672.7528763032833, + 697.2087194162982, + 698.4113191879326, + 691.5627849080735, + 696.3421741912671, + 701.4092559804876, + 702.1707433935258, + 701.8786160342602, + 701.7915936925575, + 702.3646617205877, + 702.3502612104418, + 701.8373019644533, + 702.1618268659923, + 701.387293227451, + 700.6724765495026, + 692.8573613145545, + 690.756390865194, + 699.9028558075184, + 701.7206243281846, + 702.2271980260354 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/linux/kornia_results.json b/output/linux/kornia_results.json new file mode 100644 index 0000000..e51478e --- /dev/null +++ b/output/linux/kornia_results.json @@ -0,0 +1,43 @@ +{ + "library": "kornia", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + }, + "kornia": "0.1.8" + }, + "benchmark_results": { + "images_per_second": "711.38 \u00b1 11.16", + "raw_times": [ + 697.0033174377847, + 703.8561808167548, + 703.775749696591, + 703.3396211446284, + 703.803146786953, + 700.5217307184085, + 701.2315105887913, + 702.3881949582293, + 703.3551477397373, + 701.9920341989025, + 702.5587206284661, + 705.1785187872333, + 722.4881383660472, + 727.2041226412655, + 726.6137042797883, + 727.1905751221473, + 724.508512337426, + 722.763620788062, + 722.1669399734006, + 725.6880062604613 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/linux/opencv_results.json b/output/linux/opencv_results.json new file mode 100644 index 0000000..fe2f068 --- /dev/null +++ b/output/linux/opencv_results.json @@ -0,0 +1,43 @@ +{ + "library": "opencv", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + }, + "opencv": "4.11.0.86" + }, + "benchmark_results": { + "images_per_second": "664.83 \u00b1 2.90", + "raw_times": [ + 660.0743508061927, + 663.1252559402171, + 667.0506757291804, + 666.0791739517408, + 658.1092975827875, + 663.7787715078598, + 666.446764559172, + 667.008377686375, + 667.0506367757584, + 667.0257497843158, + 666.8665428676356, + 665.3095160032029, + 665.1992851368672, + 664.3705875265218, + 666.3775632439565, + 658.244988065647, + 662.8110244777704, + 666.9609514384617, + 667.2306544943056, + 667.5615122027385 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/linux/pillow-simd_results.json b/output/linux/pillow-simd_results.json new file mode 100644 index 0000000..a36ba25 --- /dev/null +++ b/output/linux/pillow-simd_results.json @@ -0,0 +1,42 @@ +{ + "library": "pillow-simd", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + } + }, + "benchmark_results": { + "images_per_second": "564.72 \u00b1 4.00", + "raw_times": [ + 551.0684780476361, + 568.5303518546904, + 564.4721928861869, + 564.986394573479, + 565.9241648500185, + 564.5543556296951, + 559.516444224208, + 563.101160018855, + 565.400877886764, + 562.9424356802866, + 570.2997362895217, + 566.8645911256513, + 568.6557086659257, + 566.6480598696354, + 565.3965306363618, + 566.5711038221959, + 567.1277003271592, + 565.1766800396712, + 560.7312824229764, + 566.473942217468 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/linux/pillow_results.json b/output/linux/pillow_results.json new file mode 100644 index 0000000..4b70dff --- /dev/null +++ b/output/linux/pillow_results.json @@ -0,0 +1,42 @@ +{ + "library": "pillow", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + } + }, + "benchmark_results": { + "images_per_second": "601.24 \u00b1 4.87", + "raw_times": [ + 582.1465579066207, + 601.9178695362688, + 604.5868164943024, + 605.6676420392431, + 596.9322703773919, + 599.7594494970223, + 602.8565927986988, + 602.3866678397237, + 601.1859863885969, + 600.0216123143805, + 605.0214633287203, + 604.5962910945472, + 603.8935045759823, + 601.5145407238044, + 599.6699283794105, + 599.8466931572044, + 603.0947156486524, + 602.845242127832, + 603.1874047374191, + 603.6237030933554 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/linux/skimage_results.json b/output/linux/skimage_results.json new file mode 100644 index 0000000..46c7fb5 --- /dev/null +++ b/output/linux/skimage_results.json @@ -0,0 +1,43 @@ +{ + "library": "skimage", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + }, + "skimage": "0.25.0" + }, + "benchmark_results": { + "images_per_second": "492.77 \u00b1 2.53", + "raw_times": [ + 484.71803942009865, + 493.7498817807783, + 493.32007350551254, + 493.06207269824824, + 494.01273605770893, + 493.5038059685943, + 493.942403802711, + 493.64664828688086, + 494.4857308934923, + 487.3462095159777, + 491.80117074990324, + 494.05113229582713, + 494.3202169979844, + 494.0411029959842, + 494.3706951761936, + 494.67428087909633, + 493.4428233368967, + 492.9830264633185, + 489.6761353502785, + 494.21386564222996 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/linux/tensorflow_results.json b/output/linux/tensorflow_results.json new file mode 100644 index 0000000..97b8624 --- /dev/null +++ b/output/linux/tensorflow_results.json @@ -0,0 +1,43 @@ +{ + "library": "tensorflow", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + }, + "tensorflow": "2.18.0" + }, + "benchmark_results": { + "images_per_second": "706.54 \u00b1 18.35", + "raw_times": [ + 634.5536614738515, + 710.9943200269073, + 712.147455478579, + 702.6625257705427, + 686.6894124052747, + 716.3535191199385, + 714.6014541142557, + 695.6015275114487, + 705.0791279828891, + 716.3590213321612, + 714.4437253468774, + 717.3555760137459, + 715.4004139881656, + 717.3639313697548, + 715.7141120357034, + 715.4218407084416, + 711.0033552977274, + 703.5542631628908, + 706.8857247607566, + 718.5983796746689 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/output/linux/torchvision_results.json b/output/linux/torchvision_results.json new file mode 100644 index 0000000..fe2300a --- /dev/null +++ b/output/linux/torchvision_results.json @@ -0,0 +1,43 @@ +{ + "library": "torchvision", + "system_info": { + "Python": "3.12.8", + "OS": "Linux", + "OS Version": "#135~20.04.1-Ubuntu SMP Mon Oct 7 13:56:22 UTC 2024", + "Machine": "x86_64", + "CPU": { + "brand_raw": "AMD Ryzen Threadripper 3970X 32-Core Processor", + "arch": "X86_64", + "hz_advertised_raw": "Unknown", + "count": 64 + }, + "torchvision": "0.20.1" + }, + "benchmark_results": { + "images_per_second": "655.61 \u00b1 4.88", + "raw_times": [ + 659.7269788613717, + 673.1900036482474, + 653.996734147805, + 655.5051409368823, + 655.8949824598442, + 656.0741388424619, + 655.5828172303529, + 655.681145793864, + 654.5482524006712, + 654.369946638077, + 645.0725848678959, + 650.3202969347959, + 653.7897284765922, + 654.7675405805811, + 655.54363003821, + 655.9752744990523, + 655.8893644469305, + 655.7972257021704, + 655.7748849826793, + 654.7172838350294 + ] + }, + "num_images": 2000, + "num_runs": 20 +} diff --git a/run_benchmarks.sh b/run_benchmarks.sh index 0a7d6be..f648da8 100755 --- a/run_benchmarks.sh +++ b/run_benchmarks.sh @@ -66,7 +66,14 @@ LIBRARIES=("opencv" "pillow" "jpeg4py" "skimage" "imageio" "torchvision" "tensor setup_venv() { local lib=$1 echo "Setting up environment for $lib..." - python -m venv "$VENV_DIR/$lib" + + # Get the full path to the current Python interpreter + PYTHON_PATH=$(which python) + echo "Using Python: $PYTHON_PATH" + echo "Python version: $($PYTHON_PATH --version)" + + # Create venv with the same Python version + $PYTHON_PATH -m venv "$VENV_DIR/$lib" --clear # Activate virtual environment (works on both Unix and Windows) if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" ]]; then @@ -75,9 +82,16 @@ setup_venv() { source "$VENV_DIR/$lib/bin/activate" fi - pip install uv + # Upgrade pip first using the correct Python + $PYTHON_PATH -m pip install --upgrade pip + + # Install uv using the correct Python + $PYTHON_PATH -m pip install uv + + # Set UV to use copy mode instead of hardlinks + export UV_LINK_MODE=copy - # Install requirements + # Install requirements using uv uv pip install -r requirements/base.txt uv pip install -r "requirements/$lib.txt" } From 36bbb38eb92b266686b7cb375a48211e604def74 Mon Sep 17 00:00:00 2001 From: Vladimir Iglovikov Date: Tue, 21 Jan 2025 13:51:49 -0800 Subject: [PATCH 04/10] Cleanup --- imread_benchmark/create_plot.py | 65 --------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 imread_benchmark/create_plot.py diff --git a/imread_benchmark/create_plot.py b/imread_benchmark/create_plot.py deleted file mode 100644 index 96580c2..0000000 --- a/imread_benchmark/create_plot.py +++ /dev/null @@ -1,65 +0,0 @@ -import argparse - -import matplotlib.pyplot as plt -import pandas as pd -import seaborn as sns - - -def create_plot(df_path, output_path): - sns.set_theme(style="whitegrid", context="talk") - df = pd.read_csv(df_path) - - # Processing the DataFrame - performance_split = df["Performance (images/sec)"].str.split(" ± ", expand=True) - df["Mean Performance"] = performance_split[0].astype(float) - df["Std Dev"] = performance_split[1].astype(float) - df["Library with Version"] = df["Library"] + ", " + df["Version"] - df_sorted = df.sort_values("Mean Performance", ascending=True) - - # Create the bar plot - plt.figure(figsize=(14, 8)) - barplot = sns.barplot(x="Mean Performance", y="Library with Version", data=df_sorted, palette="viridis") - - # Manually add error bars - # The positions of bars (center) are usually at half-integers (0.5, 1.5, ...) in seaborn's horizontal barplot - # But we'll calculate directly from the generated plot to be more precise - y_positions = [p.get_y() + p.get_height() / 2 for p in barplot.patches] - error_values = df_sorted["Std Dev"].to_numpy() - - for y_pos, x_val, error_val in zip(y_positions, df_sorted["Mean Performance"], error_values, strict=False): - plt.errorbar( - x=x_val, - y=y_pos, - xerr=error_val, # Horizontal error for horizontal bar plot - fmt="none", # No connecting lines - capsize=5, # Cap size - color="black", # Color of the error bars - ) - - # Plot customization - plt.xlabel("Mean Performance (images/sec)") - plt.ylabel("") - plt.title("Library Performance Comparison") - plt.tight_layout() - - # Save the plot - plt.savefig(output_path) - plt.close() - - -def parse_args(): - parser = argparse.ArgumentParser(description="Create a plot from benchmark results DataFrame") - parser.add_argument( - "-f", "--file_path", required=True, help="Path to the CSV file containing the benchmark results" - ) - parser.add_argument("-o", "--output_path", required=True, help="Path where the plot image will be saved") - return parser.parse_args() - - -def main(): - args = parse_args() - create_plot(args.file_path, args.output_path) - - -if __name__ == "__main__": - main() From 9b40ebb619f447e412f58796d9ee1db81f4706d8 Mon Sep 17 00:00:00 2001 From: Vladimir Iglovikov Date: Tue, 21 Jan 2025 13:59:36 -0800 Subject: [PATCH 05/10] better folder structure for the test --- imread_benchmark/benchmark_single.py | 30 +++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/imread_benchmark/benchmark_single.py b/imread_benchmark/benchmark_single.py index 4506d98..f115f00 100644 --- a/imread_benchmark/benchmark_single.py +++ b/imread_benchmark/benchmark_single.py @@ -8,6 +8,7 @@ from importlib.metadata import version from pathlib import Path +import cpuinfo import numpy as np from tqdm import tqdm @@ -65,6 +66,29 @@ def get_package_versions(): return versions +def get_system_identifier() -> str: + """Get a detailed system identifier including OS and CPU. + + Returns: + str: A string combining OS and CPU model, formatted as 'os_cpu-model' + + """ + try: + cpu_info = cpuinfo.get_cpu_info() + cpu_brand = cpu_info.get("brand_raw", "Unknown") + + # Simple OS identification + os_id = "darwin" if platform.system().lower() == "darwin" else "linux" + + # Replace spaces with hyphens but keep full names + cpu_id = cpu_brand.replace(" ", "-") + except Exception as e: + logger.warning(f"Failed to get system info: {e}") + return "unknown-system" + else: + return f"{os_id}_{cpu_id}" + + def setup_library(): """Set up the image reading function based on the specified library.""" library = os.environ.get("BENCHMARK_LIBRARY") @@ -162,9 +186,9 @@ def main(): # Set up library and get read function once at startup library, read_image = setup_library() - # Create output directory - os_name = platform.system().lower() - output_dir = args.output_dir / os_name + # Create output directory with detailed system info + system_id = get_system_identifier() + output_dir = args.output_dir / system_id output_dir.mkdir(parents=True, exist_ok=True) # Get image paths From 43890afae90f9824032487373b8eebe0d2142a3d Mon Sep 17 00:00:00 2001 From: Vladimir Iglovikov Date: Tue, 21 Jan 2025 17:58:49 -0800 Subject: [PATCH 06/10] Updated --- .pre-commit-config.yaml | 4 - README.md | 82 ++++++++++++------ images/2024-02-26.png | Bin 175801 -> 0 bytes images/2024-03-11.png | Bin 60657 -> 0 bytes images/2024-06-05.png | Bin 63960 -> 0 bytes images/performance_darwin.png | Bin 0 -> 265357 bytes images/performance_linux.png | Bin 0 -> 323121 bytes pyproject.toml | 7 ++ tools/__init__.py | 0 tools/analyze_images.py | 59 +++++++++++++ tools/create_plots.py | 157 ++++++++++++++++++++++++++++++++++ 11 files changed, 280 insertions(+), 29 deletions(-) delete mode 100644 images/2024-02-26.png delete mode 100644 images/2024-03-11.png delete mode 100644 images/2024-06-05.png create mode 100644 images/performance_darwin.png create mode 100644 images/performance_linux.png create mode 100644 tools/__init__.py create mode 100644 tools/analyze_images.py create mode 100644 tools/create_plots.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e8bfb78..0435884 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -59,10 +59,6 @@ repos: hooks: - id: codespell additional_dependencies: ["tomli"] - - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.43.0 - hooks: - - id: markdownlint - repo: https://github.com/tox-dev/pyproject-fmt rev: "v2.5.0" hooks: diff --git a/README.md b/README.md index 879e5a9..5fa1761 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,22 @@ # Image Loading Benchmark - -[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) - ## Overview This benchmark evaluates the efficiency of different libraries in loading JPG images and converting them into RGB numpy arrays, essential for neural network training -data preparation. Inspired by the [Albumentations library]( -https://github.com/albumentations-team/albumentations/). - -![Benchmark-2024-06-05](images/2024-06-05.png) +data preparation. The study compares traditional image processing libraries (Pillow, OpenCV), +machine learning frameworks (TensorFlow, PyTorch), and specialized decoders (jpeg4py, kornia-rs) +across different computing architectures. + + + + + + + + + + +
Darwin PerformanceLinux Performance
Performance on Apple Silicon (M4 Max)Performance on Linux (AMD Threadripper)
## Important Note on Image Conversion @@ -88,33 +95,58 @@ output/ ## Libraries Being Benchmarked +Each library uses different underlying JPEG decoders and implementation approaches: + +### Direct libjpeg-turbo Users (Fastest) +- jpeg4py (Linux only) - Direct libjpeg-turbo binding +- kornia-rs - Modern Rust-based implementation - OpenCV (opencv-python-headless) +- torchvision + +### Standard libjpeg Users - PIL (Pillow) -- Pillow-SIMD (pillow-simd) +- Pillow-SIMD (Linux only) - scikit-image - imageio -- torchvision + +### Machine Learning Framework Components - tensorflow +- torchvision - kornia-rs -- jpeg4py -## Hardware and Software Specifications -**CPU**: AMD Ryzen Threadripper 3970X 32-Core Processor +## Performance Considerations + +Several factors influence real-world performance beyond raw decoding speed: + +### Memory Usage +- Memory utilization varies significantly across libraries +- Some implementations (like kornia-rs) have specific memory allocation optimizations +- Consider available system resources when scaling to batch processing + +### System Integration +- All benchmarks performed on NVMe SSDs to minimize I/O variance +- Single-threaded performance reported +- Multi-threading capabilities vary between libraries + +### Image Characteristics +- Results based on typical ImageNet JPEG images (~500x400 pixels) +- Performance scaling with image size varies between implementations +- Compression ratio and JPEG encoding parameters can influence decoding speed -## Latest Results +## Recommendations -| | Library | Version | Performance (images/sec) | -|---:|:-----------------------|:----------|:---------------------------| -| 0 | scikit-image | 0.23.2 | 538.48 ± 6.86 | -| 1 | imageio | 2.34.1 | 538.58 ± 6.84 | -| 2 | opencv-python-headless| 4.10.0.82 | 631.46 ± 0.43 | -| 3 | pillow | 10.3.0 | 589.56 ± 8.79 | -| 4 | jpeg4py | 0.1.4 | 700.60 ± 0.88 | -| 5 | torchvision | 0.18.1 | 658.68 ± 0.78 | -| 6 | tensorflow | 2.16.1 | 704.43 ± 1.10 | -| 7 | kornia-rs | 0.1.1 | 682.95 ± 1.21 | +### High-Performance Applications +- Use kornia-rs or OpenCV for consistent cross-platform performance +- On Linux, consider jpeg4py for maximum performance +- Consider memory usage if processing many images simultaneously -## Contributing +### Cross-Platform Development +- kornia-rs provides the most consistent performance +- OpenCV and torchvision offer good balance of features and speed +- Test with representative image sizes and batching patterns -Feel free to submit issues and enhancement requests! +### Feature-Rich Applications +- When needing extensive image processing features, OpenCV remains a strong choice +- Consider dependency size and installation complexity +- Evaluate the full image processing pipeline, not just JPEG decoding diff --git a/images/2024-02-26.png b/images/2024-02-26.png deleted file mode 100644 index 166ff37ad66caaf27747926b910b26c464635cd9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 175801 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�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

o`39e6Vq^6J>$ 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%0DllV#V}kwYUXAQi-Q&_J7m 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