People who make a Hit the Road Dude AI video, the State Property pickup-window meme where the driver repeats "State?" and sends the visitor off, sometimes ask a chatbot a version of this: "Why did the AI crop my face?" or "Why is there so much background around me?" Usually the answer isn't the video model at all. It's simple geometry that happens before any generation: a landscape or square photo has to become the first frame of a vertical 9:16 video, and there are only a few ways to do that.

I build hittheroaddude.app, an independent studio that turns one photo of an adult or a pet into a 15-second, vertical 9:16 remake of the clip at 480p or 720p (FAQ). The site doesn't publish its internal preprocessing, so I'm not going to describe "what my studio does to your photo" step by step. This post explains the general options any image-to-video pipeline has, and gives you a small script that previews them on your own photo before you upload it anywhere.

The problem in one number

A phone photo held sideways is 4:3, an aspect ratio of 1.333. A 9:16 frame is 0.5625. To turn one into the other you have three choices:

  1. Crop: cut a 9:16 window out of the photo. Everything outside it is gone.
  2. Pad: shrink the whole photo until it fits inside 9:16 and fill the rest (black bars, a blurred copy, a colour).
  3. Outpaint: let a model invent the missing pixels above and below.

For a 4:3 landscape photo, a full-height 9:16 crop keeps 0.5625 / 1.333 = 42% of the photo. Padding keeps 100% of the photo but the photo then fills only 42% of the frame, so your face is less than half the size. Same number, two different costs.

Outpainting avoids both, at the price of invented scenery that may not match the scene the video is supposed to show. For a fixed scene like the pickup window, where the setting comes from a reference clip and not from your photo, the photo's background matters less than your face does.

Why the face is what matters

An image-to-video model keeps resembling what it can see. If a centre crop cuts through your head, or padding leaves your face 80 pixels wide, the model has less to go on and the driver looks less like you. That's why the site's photo guide asks for "one clear photo of one adult or one pet with the face visible", face and shoulders in frame, JPEG, PNG or WebP up to 10 MB. The help page puts the common failure plainly: "Use a close photo, not a wide shot of a truck."

The practical rule: if the subject is off-centre in a landscape photo, crop it yourself to a portrait shape around the face before you upload. Then no pipeline has to guess.

The hidden sideways photo

There's a second trap. Many phones store the pixels in the sensor's orientation and add an EXIF tag that says "rotate 90° to view". Your gallery app obeys the tag. A tool that reads only the pixels sees a different shape entirely.

Here's a test photo built that way: a 4:3 landscape scene with the subject left of centre, stored sideways with EXIF orientation 6:

#!/usr/bin/env python3 """Make a 4:3 landscape test photo with a 'subject' left of centre, saved sideways with an EXIF orientation tag, the way many phones store pictures.""" from PIL import Image, ImageDraw W, H = 1600, 1200 img = Image.new("RGB", (W, H), (70, 110, 150)) d = ImageDraw.Draw(img) d.rectangle([0, 820, W, H], fill=(90, 90, 90)) # ground cx, cy = 480, 520 # subject left of centre d.ellipse([cx - 150, cy - 190, cx + 150, cy + 190], fill=(230, 190, 160)) # head d.rectangle([cx - 260, cy + 190, cx + 260, 1200], fill=(40, 60, 90)) # shoulders d.text((1250, 300), "sign", fill=(255, 255, 255)) side = img.rotate(90, expand=True) # store the pixels sideways ... exif = Image.Exif(); exif[0x0112] = 6 # ... and say "rotate 90 CW to view" side.save("test_photo.jpg", quality=90, exif=exif) print("wrote test_photo.jpg (stored 1200x1600, EXIF orientation 6, displays as 1600x1200)") 

Opened naively with Pillow, Image.open("test_photo.jpg").size is (1200, 1600), aspect 0.75, a portrait. Any crop maths done on that is wrong before it starts. ImageOps.exif_transpose() applies the tag and gives you the 1600×1200 picture you actually see.

A preview script

fit_preview.py applies the EXIF rotation, then shows a plain centre crop, a crop centred on a subject box you give it, and a padded fit with a blurred background, side by side in one PNG. It also reports how much of the photo and how much of the subject each crop keeps.

#!/usr/bin/env python3 """Preview how a photo fits a 9:16 video frame: centre crop vs. padding. usage: fit_preview.py photo.jpg [--subject x0,y0,x1,y1] [--out preview.png] --subject the face-and-shoulders box, as fractions of the displayed width/height. The crop is then centred on it, and the script reports how much of it survives. Writes a side-by-side PNG: original with the crop box, the crop, and the padded fit. Needs Pillow. This shows the geometry only; it is not any particular generator's code. """ import argparse from PIL import Image, ImageDraw, ImageFilter, ImageOps TW, TH = 9, 16 def crop_box(w, h, fx, fy): """Largest 9:16 box inside w x h, centred on the focus point but kept inside the image.""" if w / h > TW / TH: # too wide: keep full height ch, cw = h, round(h * TW / TH) else: # too tall: keep full width cw, ch = w, round(w * TH / TW) x = min(max(round(fx * w - cw / 2), 0), w - cw) y = min(max(round(fy * h - ch / 2), 0), h - ch) return x, y, x + cw, y + ch def padded(img, H): """Fit the whole photo inside 9:16, filling the rest with a blurred copy.""" W = round(H * TW / TH) bg = ImageOps.fit(img, (W, H)).filter(ImageFilter.GaussianBlur(25)) fg = ImageOps.contain(img, (W, H)) bg.paste(fg, ((W - fg.width) // 2, (H - fg.height) // 2)) return bg, fg.width * fg.height / (W * H) def main(): ap = argparse.ArgumentParser() ap.add_argument("photo"); ap.add_argument("--subject"); ap.add_argument("--out", default="preview.png") a = ap.parse_args() raw = Image.open(a.photo) orient = raw.getexif().get(0x0112, 1) img = ImageOps.exif_transpose(raw).convert("RGB") # apply the phone's rotation tag first w, h = img.size print(f"{a.photo}: stored {raw.width}x{raw.height}, EXIF orientation {orient}, displays as {w}x{h}" f" (aspect {w / h:.3f}; 9:16 is {TW / TH:.3f})") subj = [float(v) for v in a.subject.split(",")] if a.subject else None sb = [round(subj[0] * w), round(subj[1] * h), round(subj[2] * w), round(subj[3] * h)] if subj else None for name, (fx, fy) in [("plain centre crop", (0.5, 0.5))] + ( [("crop centred on subject", ((subj[0] + subj[2]) / 2, (subj[1] + subj[3]) / 2))] if subj else []): b = crop_box(w, h, fx, fy) kept = (b[2] - b[0]) * (b[3] - b[1]) / (w * h) line = f"{name}: box {b}, keeps {kept:.0%} of the photo" if sb: ix = max(0, min(b[2], sb[2]) - max(b[0], sb[0])); iy = max(0, min(b[3], sb[3]) - max(b[1], sb[1])) line += f", {ix * iy / ((sb[2] - sb[0]) * (sb[3] - sb[1])):.0%} of the subject" print(line) box = b # the last one is used for the preview H = 640 crop = img.crop(box).resize((round(H * TW / TH), H)) pad, used = padded(img, H) print(f"padded fit: photo fills {used:.0%} of the 9:16 frame, the rest is blurred fill") thumb = ImageOps.contain(img, (round(H * w / h), H)) s = thumb.width / w ImageDraw.Draw(thumb).rectangle([round(c * s) for c in box], outline=(255, 40, 40), width=4) sheet = Image.new("RGB", (thumb.width + crop.width + pad.width + 40, H), (20, 20, 20)) sheet.paste(thumb, (0, 0)); sheet.paste(crop, (thumb.width + 20, 0)); sheet.paste(pad, (thumb.width + crop.width + 40, 0)) sheet.save(a.out) print(f"wrote {a.out}") if __name__ == "__main__": main() 

The subject box is four fractions of the displayed width and height (left, top, right, bottom). Eyeball it from your gallery: in the test photo the head and shoulders run from about 14% to 46% across and from 27% down to the bottom.

$ python3 make_test_photo.py wrote test_photo.jpg (stored 1200x1600, EXIF orientation 6, displays as 1600x1200) $ python3 fit_preview.py test_photo.jpg --subject 0.14,0.27,0.46,1.0 test_photo.jpg: stored 1200x1600, EXIF orientation 6, displays as 1600x1200 (aspect 1.333; 9:16 is 0.562) plain centre crop: box (462, 0, 1137, 1200), keeps 42% of the photo, 54% of the subject crop centred on subject: box (143, 0, 818, 1200), keeps 42% of the photo, 100% of the subject padded fit: photo fills 42% of the 9:16 frame, the rest is blurred fill wrote preview.png 

Both crops keep the same 42% of the photo. Only one keeps the whole subject: the plain centre crop slices the person in half (54%). The padded version keeps everyone but shows the face at well under half the frame height.

What to do with this

  • Landscape photo, subject off-centre: crop it to portrait yourself, face and shoulders filling most of it. Any phone's crop tool works; pick the 9:16 or "story" preset if it has one.
  • Landscape photo, subject centred: probably fine, but run the preview if the head is near an edge.
  • Group photo: crop down to one person. The help page warns that group photos "can pick the wrong face".
  • Pet: the same rules apply. The pet guide covers framing a dog or cat for the driver seat.
  • Photo from a messaging app or the web: check it's large enough after cropping. A 9:16 crop of a small image leaves very few pixels for the face.

None of this guarantees a perfect likeness. Generated video can still get a face, a mouth or a hand wrong, and the site says an imperfect result is not a failed order. But it removes the one failure that's entirely under your control.

Where the script fits

This is geometry only, a preview, not anyone's production code. Pair it with the pre-upload check for format, size, blur and exposure I posted earlier and you've covered most "bad photo" problems before paying for anything.

For reference, checked on November 12, 2026 at 08:57 (UTC+8): one clip at hittheroaddude.app is $9.99 at 480p (6 credits) or $12.99 at 720p (14 credits), with credit packs for signed-in accounts at $27.99 for 30 credits, $71.99 for 100 and $129.99 for 200 (pricing). Upload yourself, an adult who agreed, or your own pet, and label the result as an AI remake.