Metadata-Version: 2.5
Name: livephoto-motion
Version: 0.1.0
Summary: Measure how a short video clip moves: camera movement versus subject movement, and how much of the frame is in motion.
Project-URL: Homepage, https://lockimate.com/tools/live-photo-check/
Project-URL: Source, https://github.com/stoffis-git/livephoto-motion
Project-URL: Issues, https://github.com/stoffis-git/livephoto-motion/issues
Author: Christoph Wackher
License-Expression: MIT
License-File: LICENSE
Keywords: analysis,ffmpeg,frame-differencing,ios,live-photo,motion,video,wallpaper
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Multimedia :: Video
Classifier: Topic :: Scientific/Engineering :: Image Processing
Requires-Python: >=3.9
Provides-Extra: dev
Requires-Dist: pytest>=7; extra == 'dev'
Description-Content-Type: text/markdown

# livephoto-motion

Measure how a short video clip moves — not just how much.

Two numbers, from plain frame differencing:

**MVI — Motion Volatility Index.** Overall change between frames, split into a
**global** component (the whole frame shifting, which is what camera movement
looks like) and a **local** component (a subject moving inside a frame that
stays put).

**MSI — Motion Surface Index.** How much of the frame is moving, plus an edge
ratio flagging movement concentrated at the borders rather than the middle.

The split is the useful part. Two clips can score identically for total
movement while one is a locked camera watching someone wave and the other is a
handheld shot of nothing in particular.

## Install

```sh
pip install livephoto-motion
```

`ffmpeg` must be on your PATH for file decoding. The index maths itself has no
dependencies at all.

## Use

```python
from livephoto_motion import measure

r = measure("clip.mov")
print(r.mvi.score, r.mvi.global_, r.mvi.local)   # 7.85  0.91  8.27
print(r.msi.score, r.msi.edge_ratio)             # 5.83  0.28
```

Or from the shell:

```sh
livephoto-motion clip.mov
livephoto-motion clip.mov --json
```

Already have frames? Skip ffmpeg entirely — `compute_indices` takes any
sequence of 8-bit grayscale frames, from PyAV, OpenCV, or a fixture:

```python
from livephoto_motion import compute_indices
indices = compute_indices(frames, width=96, height=160)
```

## Where this came from

It was written to answer a narrow question: which Live Photos will iOS accept
as an animated iPhone Lock Screen wallpaper. Measuring clips confirmed to
animate on a device, movement *within* the frame runs far higher than movement
*of* the frame — a locked camera with an active subject, rather than a little
movement overall.

Two honest limits on that observation. The sample is small, and it contains no
confirmed *failures*, so it describes a known-good range rather than a measured
threshold. This package therefore reports measurements and does not grade them.

None of the maths is iOS-specific and it applies to any short clip. If you want
the format side of that question — the seven things iOS checks before it will
animate a Live Photo — that lives in a separate library,
[livephoto-check](https://github.com/stoffis-git/livephoto-check), with a
hosted version at
[lockimate.com/tools/live-photo-check](https://lockimate.com/tools/live-photo-check/).

## How it works

Frames are decoded to 96×160 grayscale and sampled evenly across the clip —
twelve samples capture the shape of the movement at a fraction of the work of
every frame. For each consecutive pair:

- the **median** absolute difference tracks whole-frame change, so it rises
  when the camera moves and stays near zero when it does not
- the spread between the **95th percentile** and the median tracks
  concentrated change, which is a subject moving
- pixels differing by more than 8 levels count as moving area, which keeps
  encoder noise out of the measurement
- the outer 15% of the frame is treated as the edge band for the edge ratio

## Tests

```sh
pip install -e ".[dev]"
pytest
```

The maths is pure, so the suite uses synthetic frames and ships no fixtures.

## Licence

MIT.
