Skip to content
C.W.K.
Lesson 03 of 05 · published

720p, and the Crop

~15 min · resolution, ocr, apple-vision, measurement, crop

Level 0Closed Lens
0 XP0/45 lessons0/13 achievements
0/100 XP to next level100 XP to go0% complete

The Question Behind the Tier

Every look has to choose how large a picture to send. Bigger reads more detail and costs more tokens; smaller is cheap and loses small text. Game screens are full of small text: menu labels, item counts, subtitles, a health number in a corner. So "which resolution" is really "how much small text can a model read at each size, and what does each size cost?" Oriel answered it the way it answers most questions, by measuring.

The Measurement

Thirty frames of the console's home screen were taken from the 4K game card, one a second. Each was fitted to 1280×720 and to 1920×1080 with exactly the resampling and JPEG settings a look uses, and read with Apple's Vision text recognizer. The reference was the same recognizer on the full 4K frame: 459 lines of text, from 27.9 px to about 45 px tall at 4K, so 9 to 15 px at 720p. Two numbers were measured for each tier: how many reference lines came out exactly, and how similar the characters were overall.

TierLines read exactlyCharacter similaritySmallest quarter of the text: exact / similarityImage tokens (dearest vendor)
1280×72068.8%0.94476.5% / 0.8651,229
1920×108094.6%0.99076.5% / 0.9501,844

1080p reads clearly more of the small labels on game art. It also costs 1,844 tokens for the picture alone, which is over the whole 1,600-token budget of a look before a single character of text. The tempting move is to raise the budget. The architecture document records the decision instead: the look stays 720p, and small text is read another way.

The Other Way: Crop the Original

The insight is that a look and a reading are different jobs. A look answers "what is happening?", and 720p is enough for that. Reading a label answers "what does this say?", and for that you do not need the whole screen sharper; you need one region at full detail. That is the crop action. It takes a region in normalized coordinates (x, y, w, h between 0 and 1) and cuts it from the original frame: the full-resolution 4K frame from the recording, or a full-resolution peek when nothing is recording. The piece is fitted inside 1024×1024 (a large region is scaled down to fit, so a full-frame 4K crop comes back as 1024×576), and a small region is enlarged two times with the same Lanczos resampling so the recognizer has something to work with.

A 1024×1024 crop costs 1,399 tokens at most, inside the same 1,600 budget, and for the small regions it exists for, it is sharper than either tier, because a region that already fits is never scaled down. So the pattern a brain learns is two steps: look at 720p to see where the interesting text is, then crop that region from the original. Two cheap calls replace one expensive one, and the second call is better at its job than the expensive one would have been.

Small Honesty in the Crop

Two details keep the crop honest. A region too small to read (under eight pixels a side) is refused with that reason rather than returning a smear. And a crop from a live peek that turns out to be old is refused with the frame's time, so the caller can ask the recording for that moment explicitly; a crop exists to read fine detail as it is, and an old frame is not that.

There is one more quiet step before any look: some devices send their picture inside black bars (one pocket camera always sends a portrait buffer with the 16:9 picture in a band across the middle). Looks cut bars that take at least a tenth of the frame, so the model is not charged for black. Never on the game feed, though, where black is part of the game.

Code

A region in normalized coordinates, cut from the original (trimmed excerpts, backend/frames.py and backend/looks.py)·python
@dataclass(frozen=True)
class Crop:
    """A region in normalized coordinates (0..1) of the original frame."""
    x: float
    y: float
    w: float
    h: float


def crop(image, region: Crop):
    w, h = image.size
    box = (int(region.x * w), int(region.y * h), int(min(w, (region.x + region.w) * w)),
           int(min(h, (region.y + region.h) * h)))
    if box[2] - box[0] < 8 or box[3] - box[1] < 8:
        raise FrameError("the crop is too small to read")
    return image.crop(box)


# In the crop look: enlarge a small piece once, then fit the 1024x1024 ceiling
piece = frames.crop(original, region)
limit_w, limit_h = CROP_MAX
if piece.width < limit_w // 2 and piece.height < limit_h // 2:
    piece = piece.resize((piece.width * 2, piece.height * 2), Image.Resampling.LANCZOS)
piece = frames.fit(piece, *CROP_MAX)
Measure text recognition at two sizes yourself (save as ocr.swift; run: swift ocr.swift big.png small.png)·swift
import Foundation
import Vision

func lines(_ path: String) throws -> [String] {
    let request = VNRecognizeTextRequest()
    request.recognitionLevel = .accurate
    let handler = VNImageRequestHandler(url: URL(fileURLWithPath: path))
    try handler.perform([request])
    return (request.results ?? []).compactMap { $0.topCandidates(1).first?.string }
}

let args = CommandLine.arguments
let reference = Set(try lines(args[1]))     // the full-resolution frame
let scaled = Set(try lines(args[2]))        // the same frame, fitted smaller
let exact = reference.intersection(scaled).count
print("reference lines: \(reference.count), read exactly at the smaller size: \(exact)")
print(String(format: "%.1f%%", 100 * Double(exact) / Double(max(1, reference.count))))
Make the two test images from one full-resolution screenshot·bash
# Lanczos down to the two tiers a look could use
ffmpeg -hide_banner -loglevel error -i screen-4k.png \
  -vf scale=1280:720:flags=lanczos small-720.png
ffmpeg -hide_banner -loglevel error -i screen-4k.png \
  -vf scale=1920:1080:flags=lanczos small-1080.png
swift ocr.swift screen-4k.png small-720.png
swift ocr.swift screen-4k.png small-1080.png

External links

Exercise

Take one full-resolution screenshot with plenty of small text (a settings page, a game menu, a dashboard). Make the 720p and 1080p versions with the ffmpeg commands, run the Swift script on both, and record the percentage of lines read exactly. Then crop one small region from the original and run the script on the crop against the original's lines inside that region.
Hint
The crop should read its region at least as well as the 1080p version does, at a fraction of the area. If it does not, check that you cropped from the original and not from one of the scaled copies; a crop of a downscaled frame only enlarges the blur.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign in — Please sign in to comment.

No comments yet — be the first.