The hardware and bandwidth for this mirror is donated by METANET, the Webhosting and Full Service-Cloud Provider.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]metanet.ch.

Odiffr Odiffr logo

CRAN status R-CMD-check Codecov

Fast pixel-by-pixel image comparison for R, powered by odiff.

Features

Installation

System Requirements

Odiffr requires the Odiff binary to be installed on your system:

# npm (cross-platform, recommended)
npm install -g odiff-bin

# Or download binaries from GitHub releases
# https://github.com/dmtrKovalenko/odiff/releases

Install Odiffr

# Install from CRAN (when available)
install.packages("odiffr")

# Or install the development version from GitHub
# install.packages("pak")
pak::pak("BenWolst/odiffr")

Alternative: Download via R

If you cannot install Odiff system-wide:

odiffr::odiffr_update()  # Downloads to user cache

Quick Start

library(odiffr)

# Compare two images
result <- compare_images("baseline.png", "current.png")
result$match
#> [1] FALSE

result$diff_percentage
#> [1] 2.45

# Generate a diff image
result <- compare_images("baseline.png", "current.png", diff_output = "diff.png")

Usage

Basic Comparison

# High-level API (returns tibble if available)
result <- compare_images("img1.png", "img2.png")

# Low-level API (returns detailed list)
result <- odiff_run("img1.png", "img2.png")

With Options

# Adjust sensitivity threshold (0-1, lower = more precise)
result <- compare_images("img1.png", "img2.png", threshold = 0.05)

# Ignore antialiased pixels
result <- compare_images("img1.png", "img2.png", antialiasing = TRUE)

# Fail immediately if dimensions differ
result <- compare_images("img1.png", "img2.png", fail_on_layout = TRUE)

Ignore Regions

# Ignore specific areas (e.g., timestamps, dynamic content)
result <- compare_images("img1.png", "img2.png",
  ignore_regions = list(
    ignore_region(0, 0, 200, 50),     # Header
    ignore_region(0, 500, 800, 600)   # Footer
  )
)

Batch Comparison

# Compare multiple image pairs
pairs <- data.frame(
  img1 = c("baseline/page1.png", "baseline/page2.png"),
  img2 = c("current/page1.png", "current/page2.png")
)

results <- compare_images_batch(pairs, diff_dir = "diffs/")

# Extract failures or passes
failed_pairs(results)
passed_pairs(results)

# Compare entire directories
results <- compare_image_dirs("baseline/", "current/", recursive = TRUE)

# Get summary statistics
summary(results)
#> odiffr batch comparison: 50 pairs
#> Passed: 42 (84.0%)
#> Failed: 8 (16.0%)

# Use parallel processing (Unix only)
results <- compare_images_batch(pairs, parallel = TRUE)

HTML Reports

# One-liner: compare directories and generate HTML report
compare_dirs_report("baseline/", "current/")
# -> Creates diffs/ directory with diff images and report.html

# Or step-by-step for more control
results <- compare_image_dirs("baseline/", "current/", diff_dir = "diffs/")
batch_report(results, output_file = "qa-report.html")

# Self-contained report with embedded images
batch_report(results, output_file = "qa-report.html", embed = TRUE)

# Portable report with relative image paths
batch_report(results, output_file = "output/report.html", relative_paths = TRUE)

CI Integration

Run visual regression tests in GitHub Actions and upload diff artifacts:

# .github/workflows/visual-regression.yaml
name: Visual Regression

on: [push, pull_request]

jobs:
  visual-test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: r-lib/actions/setup-r@v2

      - name: Install dependencies
        run: |
          install.packages(c("odiffr", "webshot2"))
          odiffr::odiffr_update()
        shell: Rscript {0}

      - name: Generate screenshots
        run: Rscript scripts/generate-screenshots.R

      - name: Compare images
        run: |
          library(odiffr)
          results <- compare_dirs_report("baseline/", "current/")
          if (any(!results$match)) stop("Visual regression detected!")
        shell: Rscript {0}

      - name: Upload diffs
        if: failure()
        uses: actions/upload-artifact@v4
        with:
          name: visual-diffs
          path: diffs/

With magick Package

library(magick)

# Compare magick-image objects directly
img1 <- image_read("baseline.png") |> image_resize("800x600")
img2 <- image_read("current.png") |> image_resize("800x600")

result <- compare_images(img1, img2)

Visual Regression Testing

library(testthat)
library(odiffr)

test_that("dashboard renders correctly", {

  expect_images_match(
    "screenshots/current.png",
    "screenshots/baseline.png",
    threshold = 0.1
  )
})

test_that("button changes on hover", {
  expect_images_differ(
    "button_normal.png",
    "button_hover.png"
  )
})

On failure, diff images are automatically saved to tests/testthat/_odiffr/.

Binary Management

# Check if Odiff is available
odiff_available()

# Get version and configuration info
odiff_info()

# Update to latest version (downloads to user cache)
odiffr_update()

# Use a specific binary
options(odiffr.path = "/path/to/odiff")

Binary Detection Priority

  1. options(odiffr.path = "...") - User override
  2. System PATH (Sys.which("odiff"))
  3. Cached binary from odiffr_update()

Supported Formats

Type Formats
Input PNG, JPEG, WEBP, TIFF
Output PNG only

Cross-format comparison is supported (e.g., compare JPEG to PNG).

For Validated Environments

Odiffr is designed for use in validated pharmaceutical and clinical research:

# Pin to a specific validated binary
options(odiffr.path = "/validated/bin/odiff-4.1.2")

# Document in validation scripts
info <- odiff_info()
sprintf("Using odiff %s from %s", info$version, info$source)

Performance

Odiff is approximately 6x faster than ImageMagick for pixel comparison, thanks to SIMD optimizations. Performance scales well with image size.

License

MIT

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.