Why Does the AI Cut Off My Head? How a Photo Gets Fitted Into a 9:16 Hit the Road Dude Video, With a Pillow Preview A developer who runs hittheroaddude.app, an independent studio that turns a single photo into a 15-second vertical 9:16 meme video, explained that most "AI cropped my face" complaints stem from pre-generation geometry rather than the video model: converting a 4:3 landscape photo (aspect 1.333) to a 9:16 frame (0.5625) via full-height crop retains only 42% of the image, while padding keeps the whole photo but shrinks the face to under half the frame. The developer also flagged EXIF orientation as a second trap, since phones often store pixels sideways with a rotation tag that naive readers ignore, and provided a Python/Pillow script that generates a test photo stored at 1200x1600 with EXIF orientation 6 to preview crop and padding options before uploading. 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 https://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 https://hittheroaddude.app/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. 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: 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. 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 https://hittheroaddude.app/guides/driver-photo/ 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 https://hittheroaddude.app/help/ 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. 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: bash /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. 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. bash /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. bash $ 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. 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. This is geometry only, a preview, not anyone's production code. Pair it with the pre-upload check for format, size, blur and exposure https://dev.to/lpgit/what-photo-works-best-for-a-hit-the-road-dude-ai-video-a-python-pre-upload-check-for-format-size-4nk3 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 https://hittheroaddude.app/pricing/ . Upload yourself, an adult who agreed, or your own pet, and label the result as an AI remake.