ID photo sizes, crop frame, and file output

The app does not judge whether a photo will be accepted. Face detection only decides whether editing may start, the user drags the photo edges and confirms the framing and the rules, and the app checks what it can measure: aspect ratio, resolution, pixel size, and file bytes.

Source baseline: app 1.0.0 (1), commit a016ecd · 23 September 2026

Scope and stack

Version 1 turns one portrait at a time into a Korean passport, driver's license, or resident registration card photo, or a general 35×45 mm or 30×40 mm photo. It saves a single JPEG or a 4×6 inch print sheet to the gallery and can share a saved file. There is no account, server, or photo archive; one job is edited at a time and nothing is kept between jobs.

UIKotlin and Jetpack Compose, minSdk 29, targetSdk 36
InputCameraX capture (front or back camera, optional 3-second timer) or Android Photo Picker, images only
Face checkBundled ML Kit Face Detection, fast mode with eye-open classification
BackgroundBundled ML Kit Selfie Segmentation, single-image mode, general photos only
Output300 dpi JPEG through MediaStore to Pictures/DotoriIdPhoto, share sheet for saved files
AdsAdMob banner on the home screen only, with UMP consent

The two ML Kit wrappers live in the shared libs/ai tree as face-detection and selfie-segmentation. They own only the SDK call, its release, and a neutral result: a face box normalized to the upright image with roll, yaw, and eye-open probabilities, or a foreground confidence mask. Sizes, cropping, confirmations, and output belong to the app. The app does not depend on the OCR-side libs/ai/core used by Dotori Business Card Scanner.

Official sizes and what the app checks

The presets come from the Korean government guidance checked on 23 September 2026. The app treats the physical size and file limits as rules it can enforce and everything about the person in the photo as rules the user checks.

Passport35×45 mm; online file JPEG up to 500 KB, 413×531 px recommended; white original background; crown (excluding hair) to chin 32–36 mm. Source: Ministry of Foreign Affairs photo rules and online application.
Driver's license35×45 mm; current guidance for online files is JPG up to 500 KB at 413×531 px; the photo must not be edited or composited. Source: Korea Road Traffic Authority.
Resident registration card35×45 mm; white background recommended, a pale single color allowed. A separate online file rule was not confirmed, so the app offers the single file and the print sheet without a byte limit. Source: Ministry of the Interior and Safety.
General35×45 mm or 30×40 mm. App convenience presets, not an official standard.

Input and the face check

The purpose is chosen first, because it decides the aspect ratio, the initial frame, and whether background tools exist. Changing the purpose starts a new job.

  1. A picked image is copied to a temporary file in the app cache, with a 32 MiB limit, and the copy is deleted right after decoding. A camera capture is written to the same cache folder and deleted after decoding. The picker URI is not stored.
  2. ImageDecoder applies the EXIF rotation and mirroring to the pixels, converts to sRGB, and limits the long side to 2048 px. Images with a side over 30,000 px are rejected before decoding.
  3. Face detection runs on a copy whose long side is at most 768 px. The detector does not use contour mode, because contour detection reports only the most prominent face and would hide a second person.
  4. Editing starts only when exactly one face is found. No face, several faces, or a detector failure all keep the user on the home screen with a message; a photo whose face could not be checked is treated as unchecked, not as passing.
  5. A result that arrives after the user cancelled is not applied, and a second operation cannot start while one is running.

The camera screen also runs the detector on a preview frame at most every 500 ms and shows guidance such as “Only one person in the frame” or “Face forward and center your head”. That guidance and a pose or closed-eye warning on the edit screen are advice only; they never block capture or saving.

The camera permission is requested only when the user chooses to take a photo, and choosing a photo needs no storage or media permission. The manifest's only app-declared permission is CAMERA, with the camera marked as not required.

Crop frame and initial framing

The top and bottom lines on the preview are the edges of the photo that will be saved, not measurement lines. Earlier builds drew estimated crown and chin lines and offered an automatic framing button; both were removed after user feedback.

The face box is used for one thing only: where the handles start. The head is estimated as 1.3 times the face box height, because ML Kit's box runs from roughly the forehead to the chin. The head is placed to fill a purpose-specific share of the photo height: 34/45 (about 76%) for the three ID purposes, the middle of the passport 32–36 mm rule, and 60% for general photos, which leaves room for the shoulders. The remaining height is split one part above the crown to two parts below the chin, and the frame is centered horizontally on the face.

The 1.3 factor is an unverified estimate. It has not been checked against real portraits, and clamping to the source trims one margin when the photo is short or the face is near an edge. That is why the face box is never used to draw guide lines, decide pass or fail, or block saving. The “one fist above, two fists below” rule is used only as a ratio; using a real fist size would make the head about 45% of the height, well under the passport rule.

The edge handles expose a progress value and a label for accessibility services. Moving the frame sideways has no separate accessible control yet.

Backgrounds

The passport rules forbid removing the background or compositing a white one, and the driver’s license guidance forbids edited or composited photos; the app applies the same rule to the resident registration card as its own policy. For all three ID purposes, the background tools are not shown and the saved file is always rendered from the original pixels.

Save gate and output files

The save button and the message above it come from one function, saveBlocker, so the button is never enabled for a reason the message does not explain. It checks, in order: no photo, an operation in progress, framing not confirmed, rules or background edges not confirmed, and a crop smaller than the output size. Several faces are not a save reason, because such photos never reach editing. Moving an edge or the frame clears the framing confirmation; the rules confirmation stays.

Pixel sizemm ÷ 25.4 × 300, rounded: 413×531 px for 35×45 mm and 354×472 px for 30×40 mm
ScalingDownscale only. A crop with fewer pixels than the output is blocked as low resolution instead of being enlarged to hide it.
JPEGQuality 95 down to 60 in steps of 5, checking the real encoded size. Passport and license single files must be 500,000 bytes or less; if even quality 60 is too large, saving fails.
MetadataA new EXIF block with only 300 dpi resolution and upright orientation. Nothing is copied from the source, so GPS and camera metadata never reach the output.
Print sheet1200×1800 px (4×6 inch at 300 dpi): six 35×45 mm photos or nine 30×40 mm photos, 24 px apart, with gray corner marks and a light gray cut line one pixel outside each photo. The photo pixels are unchanged, and single files have no border.

Storage, privacy, and backup

A portrait is personal data about whoever is in it, so the app keeps no copy of its own.

Verification and known limits

Crop geometry (portrait and landscape sources, edge drags, frame moves, print layout, and initial framing near image edges) and the save gate are covered by JVM unit tests. Three instrumented tests on an Android 11 test phone check output pixel size, dpi, and byte limits, EXIF rotation with GPS removal, MediaStore publishing, and that background composition leaves the foreground and the original unchanged.

Measured on the device. Saved 35×45 mm single files read back as 413×531 px at 300 dpi, at 15,042 and 43,355 bytes. On the same phone, dragging the top handle kept the bottom edge fixed and the reverse, the save button stayed at the same position while the adjustment area scrolled, and the gallery button opened the Samsung gallery. The test captures had no person in them, so they say nothing about recognition rates or acceptance.