Metadata-Version: 2.4
Name: heatmatch
Version: 1.0.0
Summary: Orientation-aware visualization and comparison of very dense saccade patterns (scanpath comparison for long free viewing)
Author: Xingyu Long
License-Expression: MIT
Project-URL: Homepage, https://github.com/xysLong/HeatMatch
Project-URL: Paper, https://doi.org/10.1145/3803539
Project-URL: Data, https://osf.io/f2xhj/
Keywords: eye-tracking,saccades,scanpath,scanpath-comparison,gaze,heatmap,visualization,orientation-field,empirical-aesthetics,art-perception
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Science/Research
Classifier: Topic :: Scientific/Engineering :: Visualization
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: matplotlib
Requires-Dist: Pillow
Dynamic: license-file

# HeatMatch

**Visualize and compare very dense saccade patterns from long free-viewing eye-tracking data.**

HeatMatch turns hundreds or thousands of saccades into continuous *orientation fields*: at every location it estimates saccade density, mean orientation, and how consistently the saccades agree in orientation. From these fields it draws orientation-coded heatmaps that show *where* gaze moves and *in which direction*. It also computes a similarity score for comparing saccade patterns across participants, stimuli, or conditions, without AOIs, scanpath alignment, or manual segmentation.

It was developed for 60-second viewing of paintings (empirical aesthetics, art history) and works for any static stimulus. Python implementation accompanying the paper:

<table border="0" cellspacing="0" cellpadding="0"><tr>
<td width="72%" valign="top">

<blockquote>
Xingyu Long, Jozsef Arato, Sophia Kury, Anna Miscena, and Raphael Rosenberg. 2026.<br/>
<strong>HeatMatch: Orientation-Aware Visualization and Comparison of Very Dense Saccade Patterns.</strong><br/>
<em>Proceedings of the ACM on Computer Graphics and Interactive Techniques</em> (PACMCGIT), 9(2), Article 18.<br/>
<a href="https://doi.org/10.1145/3803539">https://doi.org/10.1145/3803539</a>
</blockquote>

</td>
<td width="28%" align="center" valign="middle">
<img src="https://raw.githubusercontent.com/xysLong/HeatMatch/main/assets/logos.png" width="200" alt="CReA Lab and Vienna Cognitive Science Hub"/>
</td>
</tr></table>

---

## Overview

1. **OOI-coded heatmaps** — orientation fields visualized with researcher-defined color anchors and confidence-weighted opacity.
2. **HeatMatch similarity** — comparison based on saccade density and orientation.

![HeatMatch heatmap example](https://raw.githubusercontent.com/xysLong/HeatMatch/main/assets/example_heatmaps.jpg)
<sub>Per-participant (columns 1–5) and aggregated (column 6) heatmaps for three paintings. Hue = mean saccade orientation relative to OOI; opacity = local confidence. Stimulus images: public domain via [Wikimedia Commons](https://commons.wikimedia.org) — see paper for details.</sub>

See the [paper](https://doi.org/10.1145/3803539) for methodology.

### When to use it

- **Long, dense viewing.** Scanpath comparison methods such as MultiMatch or ScanMatch align scanpaths sequence by sequence. With hundreds of saccades per trial and strongly diverging paths, they become slow and hard to interpret. HeatMatch aggregates instead of aligning, so it scales to arbitrarily dense data.
- **Orientation matters.** Fixation heatmaps and density-based metrics ignore saccade direction. HeatMatch keeps orientation, so you can ask whether gaze follows horizontal, vertical, diagonal, or symmetric structure in an image, e.g. composition lines in a painting.
- **Group, stimulus, and individual comparisons.** In the paper, the similarity score recovers the abstract vs. still-life distinction and differences in within-painting agreement. It also reveals stable individual "saccadic signatures" that hold across paintings.

It is **not** a replacement for sequence-based methods. HeatMatch is permutation-invariant (it ignores saccade order) and treats orientations as unsigned (left-to-right = right-to-left). Use it alongside MultiMatch, ScanMatch, or RQA when order or direction matters.

---

## Installation

```bash
pip install heatmatch
```

Dependencies: `numpy`, `matplotlib`, `Pillow`. For development, clone the repository and run `pip install -e .`.

---

## Quick Start

```python
import matplotlib.pyplot as plt
from heatmatch import make_reference_grid, make_orientation_field, Heatmap, compute_similarity

W, H = 2880, 2160
onset  = ...  # (J, 2) saccade start coordinates
offset = ...  # (J, 2) saccade end coordinates

pts, xx, yy = make_reference_grid(W, H, grid_resolution=200)
field = make_orientation_field(pts, onset, offset, sigma=50.0, grid_shape=yy.shape)

fig, ax = plt.subplots()
Heatmap(field, W, H).draw(ax, ooi=0.0)  # ooi=0 → horizontal, ooi=90 → vertical
plt.show()

# Compare two patterns (fields built on the same grid, e.g. two participants)
result = compute_similarity(field_a, field_b)
s = (result.s_loc + result.s_dir) / 2  # composite HeatMatch score S in [0, 1]
print(result.s_loc, result.s_dir, s)
```

A full worked example is in [`demo.ipynb`](https://github.com/xysLong/HeatMatch/blob/main/demo.ipynb). The dataset is not included — download `data_anonymized.csv` from **[osf.io/f2xhj](https://osf.io/f2xhj/)** and place it in `tests/`.

---

## API

### `heatmatch.fields`

| | |
|---|---|
| `make_reference_grid(w, h, grid_resolution)` | Returns `(pts, xx, yy)`; int → square grid, `(ny, nx)` tuple → rectangular |
| `make_orientation_field(pts, onset, offset, sigma, grid_shape, ...)` | Returns `OrientationField` with `omega_mean`, `R`, `rho` |

### `heatmatch.heatmapping`

| | |
|---|---|
| `Heatmap(field, w, h, image=None)` | Caches colormap, opacity, and (optionally) grayscale background image |
| `Heatmap.draw(ax, ooi, opacity_density_weight, base_opacity, cmap, ...)` | Renders onto `ax` |

OOI in degrees: 0 = east, 90 = north, 180 = west, 270 = south. Unsigned, so `ooi` and `ooi ± 180` are equivalent.

### `heatmatch.matching`

| | |
|---|---|
| `compute_similarity(field_a, field_b, density_coherence_tradeoff)` | Returns `SimilarityResult(s_loc, s_dir)` |

---

## Key Parameters

| Parameter | Default | Notes |
|---|---|---|
| `sigma` | `50.0` | Gaussian bandwidth in px. Paper evaluates {50, 100, 150}. |
| `grid_resolution` | `200` | Int or `(ny, nx)`. Paper uses 200. |
| `ooi` | `0.0` | Orientation of Interest in degrees. |
| `opacity_density_weight` | `1.0` | Density vs. coherence for opacity. Recommended 0.5–1.0. |
| `base_opacity` | `0.85` | Global opacity ceiling; useful with a background image. |
| `density_coherence_tradeoff` | `0.5` | Density vs. coherence for *S*_dir. At 1.0, angular information is discarded. |

---

## Citation

```bibtex
@article{long2026heatmatch,
  author    = {Long, Xingyu and Arato, Jozsef and Kury, Sophia and Miscena, Anna and Rosenberg, Raphael},
  title     = {HeatMatch: Orientation-Aware Visualization and Comparison of Very Dense Saccade Patterns},
  journal   = {Proc. ACM Comput. Graph. Interact. Tech.},
  year      = {2026},
  volume    = {9},
  number    = {2},
  articleno = {18},
  doi       = {10.1145/3803539},
}
```
