Sign In

Camera Speed Is a Quality Setting: Measuring Motion Blur in HappyHorse 1.1

0

Camera Speed Is a Quality Setting: Measuring Motion Blur in HappyHorse 1.1

TL;DR: in HappyHorse 1.1, how fast things move in your clip measurably degrades image sharpness. How long they move fast does not. Slow your camera moves down and you get a sharper render for free - no prompt tricks, no extra generations - sure, if that is what you are looking for.

I felt like I am noticing this and didn't initially trust myself, so I decided to measure it. Below you can find the method and the numbers, oh and also the things that turned out to be wrong, and also the four ways the metric will lie to you if you're not careful - I hope that might be useful for someone and save some time and/or budget.

---

Why measure at all

Generations cost some money and roughly two out of three of mine were unusable (and it is best case scenario actually). That ratio makes eyeballing kinda dangerous: when you're staring at a clip you paid for you see what you want to see - e.g. "all looks good". And each next run in this engine actually show that two clips from the same prompt can look meaningfully different... really different. Without a measurement you cannot tell/differentiate "my prompt change worked" from "this time I just got luckier".

So: a number. Not a perfect one (caveats described as well) but quite a consistent one.

The method

Per-frame Laplacian variance. It's the standard "cheap sharpness proxy": run a Laplacian kernel over the grayscale frame and take the variance of the result. Sharp edges produce a big spread, soft mush produces a small one.

import cv2, sys, statistics
from collections import defaultdict
cap = cv2.VideoCapture(sys.argv[1])
fps = cap.get(cv2.CAP_PROP_FPS) or 25
buckets = defaultdict(list)
i = 0
while True:
    ok, frame = cap.read()
    if not ok:
        break
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    buckets[int(i / fps)].append(cv2.Laplacian(gray, cv2.CV_64F).var())
    i += 1
cap.release()
for sec in sorted(buckets):
    print(f"{sec:3d}s  {statistics.mean(buckets[sec]):8.1f}")

Run this script against your generated clip and you get one sharpness figure per second. That appears to be enough to see where in the clip quality falls apart which is the whole point - an average over the whole clip tells you nothing useful (I tried).

The data

The clearest case was a 15-second action clip structured deliberately this way: a slow-motion close-up, a slow-motion pull-back, a ramp to real time, fast action, then the action settling.

| Phase | Sharpness |

|---|---|

| Slow-motion close-up | 322–354 |

| Slow-motion pull-out | 737–904 |

| Full-speed action | 166–236 |

| Action settling | 383–446 |

That's roughly a four-to-five-fold collapse between the slow pull-out and the full-speed section, inside a single generation, with no other variable changing. The recovery as the action settles is the part that convinced me it's motion and not something else - quality comes back on its own the moment things slow down.

What actually costs you

Not all motion is equally expensive. In descending order of damage:

1. A subject tumbling or spinning. This is the worst case by a distance 'cause every pixel of the subject changes at once. There is no static region for the engine to hold onto.

2. Fast camera movement. Whole-frame change, same problem, slightly better behaved.

3. A fast subject against a locked camera. Cheapest of the three - the background stays put and anchors the frame.

If you have an expensive shot, the cheapest structural fix is to lock the camera during the fast phase and let the subject carry the motion.

The correction: duration is free

My first working theory was that fast sections should be kept short - get in, get out, minimise the damage. That turned out to be wrong - surprise.

So what I did was I lengthened the fast phase of an otherwise comparable clip from four seconds to seven. The sharpness band during the fast section barely moved: 148–324 in the longer version against 126–334 in the shorter one. Well inside noise.

Thus the cost is paid the moment you accept the speed and it does not accumulate. If you want seven seconds of fast action, just take them - you are not paying more per second than you did for four. Budget the speed, not the runtime.

The experiment that failed, and why

I also ran a version testing three mitigations at once: locked camera, no whole-body rotation, shorter fast phase. The results came back inconclusive, because in the same generation I had also redesigned the creature in the shot - bummer.

Any difference in the output could have come from either change so unfortunately it proved nothing. It's an obvious mistake and I still keep making it tbh - worth stating plainly, because this engine's variance punishes multi-variable changes harder than most. One variable per generation, or you have burned the credits for nothing.

Four ways this metric will lie to you

Laplacian variance measures detail, not focus. Everything below follows from that.

1. Darker and lower-contrast scenes score lower even when perfectly sharp. The same shot composition scored 84–136 in a dim version and 322–354 in a brightly lit one. Both were fine.

2. Therefore cross-clip comparison is close to meaningless. Only compare within a single clip, where lighting and palette are constant. This is the important one.

3. Heavy particulate destroys the score without hurting quality. Dust, smoke and fog are inherently low-contrast. One clip which I generated scored 57 at second 13 - that was a large dust cloud rendering beautifully, not a failure as it appears to be - thus never judge an atmospheric clip on the number alone - look at it first...

4. Run-to-run variance is high. Two generated clips differed only in their audio block and still looked visibly different. A single pair of clips is not evidence of anything. If a difference matters to you, just repeat it.

My recommendations and conclusions (at the time of writing)

- Write camera moves long, slow and decelerating rather than quick. A slow move looks more expensive anyway so this rarely costs you anything creatively.

- Reserve a static tail at the end of any clip containing a long camera move. An eight-second clip I ran cut off abruptly mid-move because the move ran all the way to the last frame with nothing held - give the move somewhere to land.

- Control move timing explicitly. Loose narrative description alone did not reliably pace a long move for me; adding a coarse block of beat markers alongside the prose - not replacing it - did. This isn't in any HappyHorse tutorial I've found, but it worked consistently.

- Avoid whole-body rotation in any shot where sharpness matters.

- Lock the camera during the fastest phase of the action.

None of this is a workaround for a bad prompt. It's a budget. You have a fixed amount of image quality per clip, motion spends it, and you get to choose where.

Example clip

Example of the generated clip:

---

Measurements are from my own generations on HappyHorse 1.1, using the script above. I'd genuinely like to know whether the numbers hold on other people's clips - if you run it, post what you get.

0