image-comparison — architectural layout and system interface preview
Open SourceEngineering Case Study

image-comparison

Contributed an internal engine refactor to romankh3/image-comparison (PR #255), an open-source visual QA testing library on Maven Central. Decoupled comparison state into a local DifferenceMatrix, replaced recursive region traversal with an iterative ArrayDeque to avoid stack overflow risks, and added primitive coordinate checks to avoid per-pixel Point allocations.

Open-Source Java QA Testing Library
Role & ScopeOpen-Source Contributor
Target DomainOpen Source · Production
Core RuntimeJava, Open Source, Image Processing
Operational & Architectural Invariants:
Preserve public API and identical diff outputZero call-stack overflow on large difference regionsUpstream test suite passing (66/66 JUnit tests)

The Problem

The comparison engine had maintainability and stack safety concerns: temporary difference matrix and counter state were stored directly on the ImageComparison instance, region grouping used deep recursion that risked StackOverflowError on large connected diffs, and excluded-area checks instantiated a new java.awt.Point object for every pixel checked.

Technical Approach

Encapsulated comparison-specific state into a private DifferenceMatrix class. Refactored recursive flood-fill into an iterative ArrayDeque traversal using index arithmetic (y * width + x), added primitive int coordinate overloads to ExcludedAreas and Rectangle, replaced magic numbers with named constants, and introduced regression unit tests.

Interactive Architecture Explorer

PR #255: Recursive Flood-Fill vs Iterative ArrayDeque

Memory & Call Stack Metric:
Call Stack Depth:O(1) Flat Call Stack
Coordinate Checks:Primitive containsPoint(x, y)
20k-Pixel Diff Test:100% Passes (66/66 JUnit)
// ITERATIVE ARRAYDEQUE (PR #255)
ArrayDeque<Integer> positions = new ArrayDeque<>();
addToRegion(matrix, regionMarker, positions, x, y);
while (!positions.isEmpty()) {
    int pos = positions.removeLast();
    int curX = pos % matrix[0].length;
    int curY = pos / matrix[0].length;
    // Flat stack; index arithmetic y * w + x
}

Key Highlights & Architecture

  • Decoupled comparison state from ImageComparison instance fields into an isolated DifferenceMatrix record
  • Replaced recursive region grouping with an iterative ArrayDeque traversal storing flattened pixel indices (y * width + x) to prevent stack overflow on deep difference regions
  • Added primitive containsPoint(int x, int y) overloads on Rectangle and ExcludedAreas, eliminating per-pixel Point allocations
  • Added regression unit tests for large 20,000-pixel difference regions and primitive coordinate checks, passing all 66 unit tests

Engineering Challenges

  • Ensuring the iterative traversal produced pixel-for-pixel identical difference bounding boxes as the original recursive approach without regressions.
  • Adding regression tests for large diff regions without introducing long test execution times in CI.

Key Engineering Decisions

  • Iterative ArrayDeque Traversal: Replaced recursive calls with an in-memory deque processing neighbor offsets, eliminating recursion depth limits.
  • Primitive Coordinate Overloads: Added containsPoint(int x, int y) to evaluate coordinates directly, avoiding new Point(x, y) allocations during pixel loops.
  • Encapsulated DifferenceMatrix: Moved matrix and counters out of instance fields, making the comparison flow easier to maintain and reason about.
Architecture Decision Records (ADRs)

Trade-Off Analysis & Architectural Rationale

✓
Decision Made

Iterative ArrayDeque Region Traversal

✕
Alternative Rejected

Recursive joinToRegion Method

Why It Won (Technical Rationale)

Prevents JVM StackOverflowError when difference clusters span tens of thousands of connected pixels.

Trade-Off Accepted

Requires managing an in-memory deque and index math (y * width + x) instead of implicit call-stack frames.

✓
Decision Made

Primitive Coordinate Method Overloads

✕
Alternative Rejected

Allocating java.awt.Point per pixel check

Why It Won (Technical Rationale)

Checks coordinates directly with primitive ints in ExcludedAreas, avoiding heap allocations inside the hot pixel loop.

Trade-Off Accepted

Adds overloaded methods to Rectangle and ExcludedAreas.

Result & Outcome

Submitted upstream as Pull Request #255, passing all 66 unit tests and PMD check, resolving maintainability and recursion limits as part of a university Software Engineering assignment.

Technologies & Tools

JavaOpen SourceImage ProcessingJUnit 5AlgorithmsMaven