Take a passport photo of precisely the proper dimensions using computer vision to pan and zoom on the face.
Defaults to US Passport requirements, but can be modified for others.
Shows live camera view. Automatically centers and zooms image to be precisely correct for passport photos. Hit spacebar to snap the photo to "passport.jpg" and q to quit.
apt install python-opencv opencv-data
git clone https://github.com/hackerb9/passportpix
cd passportpix
./passport.py
The image saved to a file is higher resolution than the preview shown on screen. The face detection routine is a bit CPU hungry so the image is downscaled to fit in a 640x640 square before processing to get a better frame rate. Likewise, some low-end computers, such as the Raspberry Pi, struggle with displaying large images rapidly, so the downscaled image is used for screen display.
If you leave an image viewer, such as eog passport.jpg
, running
in the background, you'll see the picture update immediately so you
can tell if you like it or not.
If you need a "portrait" photo (taller than it is wide), you can
improve the image resolution by rotating your camera sideways and edit
the camera_rotation
variable at the top of the file. (Or hit the 'r'
key).
The default setup is correct for US Passport photos:
Variable | Description | Setting for US Passport |
---|---|---|
photo_aspect | Ratio of width to height | 1 (square) |
eye_distance | Distance between eyes, expressed as a fraction of the picture width | 2/12 (= ⅓" on a 2" width) |
eye_height | Distance of eyes from bottom of picture, as a fraction of picture height | 7/12 (= 1⅙" on a 2" height) |
These variables are listed at the top of passport.py and can be changed there.
Click to see an example customization
If one need a visa to visit China, the photo requirements as of 2020 are:
Variable | Description | Setting for CN Visa |
---|---|---|
photo_width | Width of printed photo | 33 |
photo_height | Height of printed photo | 48 |
photo_units | Unit of measurement of width and height | mm |
photo_aspect | Ratio of width to height | 33/48 |
eye_height | Distance of eyes from bottom of picture, as a fraction of picture height | 24/48 (precisely in middle) |
chin_height | Distance of chin from bottom of picture, as a fraction of picture height | 7/48 |
So, one would set:
# Setting for Chinese Visa photo
photo_width = 33.0
photo_height = 48.0
photo_units = "mm"
photo_aspect = photo_width/photo_height
eye_height = 24.0 / 48.0
chin_height = 7.0 / 48.0
use_chin_height = True
Note that by default use_chin_height
is False, which causes
eye_distance
to be used for calculating the scaling instead of
chin_height
. You can also hit the Tab key to switch
between those two methods.
- Space: Save snapshot to passport.jpg
- q or Esc: Quit
- f: Toggle fullscreen
Less useful keys:
- m: Toggle mirroring
- r: Increment camera rotation by 90°
- Tab: Toggle between US (eye distance) and Chinese (chin distance) photo requirements. Does not change photo dimensions.
- Enter: Show debugging info, save debugging images
Click to see more keys
These keys change the downscale resolution which is used for both the computer vision processing and for display on the screen. They do not affect the output resolution in the saved file.
Key Maximum height or width 1 160 2 320 3 640 (default) 4 960 5 1280 0 Native resolution Note that OpenCV's builtin face detection algorithms failed for me on 160×160 images.
-
I assume you always want the highest resolution possible from your camera.
-
You'll need to have 'python-opencv' installed. (
apt install python-opencv
)
-
Cropped image jitters as eye locations are approximated. Ought to show uncropped camera view either in a second window or alone with a rectangle showing how the image will be cropped.
-
OpenCV cannot write the proper DPI to the JPEG file, so it will not print out at the correct size without massaging. This is annoying, but is easy enough to work around that I'm not fixing it before the release.
-
You can only take one photo. Every photo you take overwrites the previous 'passport.jpg' file.
-
Only the first camera is opened. You can change that by editing
camera_device
at the beginning of the file. -
When recentering the face, especially if it is very close to the camera, there may not be enough picture from the camera to extend all the way to the edge of the final photo. The program should warn about this and give a visual indication of which way to move to fix it.
-
If your face is too far away, the camera will zoom in too much, causing a blurry picture. This program ought to detect if height or width is less than 600 and ask the user to step closer.
Click to see info about debugging
Mostly reminders to myself.
To list all resolutions a camera is capable of:
v4l2-ctl --list-formats-ext
OpenCV appears to prefer uncompressed video, even if a camera can provide a higher resolution & frame rate with compression turned on.
This may not be a problem. I've added functionality to allow for compression and lower FPS, but even though the nominal resolution was higher, the quality was noticeably worse than simply scaling the uncompressed video. (At least on the cameras I tried). If you want to give it a shot, change the variables
camera_codec='MJPG'
orcamera_fps=15
.To print out the current GUI properties and other debugging info, hit Enter.
OpenCV has unnecessarily confusing GUI window properties. In particular, it appears OpenCV was originally written using simple integer Booleans (0 or 1), but someone later came along and decided that was too sloppy and renamed them all. However, instead of using the typical True or False, they came up with new names for truth values for each variable so each has its own nearly-unique way of being used.
I found this silly and hard to read, so I do not follow that practice. For example, I have replaced the following code:
isFull = (cv2.getWindowProperty(title, cv2.WND_PROP_FULLSCREEN) == cv2.WINDOW_FULLSCREEN)a cv2.setWindowProperty(title, cv2.WND_PROP_FULLSCREEN, cv2.WINDOW_NORMAL if isFull else cv2.WINDOW_FULLSCREEN)with:
isFull = cv2.getWindowProperty(title, cv2.WND_PROP_FULLSCREEN) cv2.setWindowProperty(title, cv2.WND_PROP_FULLSCREEN, 1 - isFull)Here are the OpenCV window properties and their official documentation (as of 2023). (Italics mine.)
WND_PROP_FULLSCREEN
fullscreen property (can beWINDOW_NORMAL
orWINDOW_FULLSCREEN
).
BooleanNORMAL
is 0 andFULLSCREEN
is 1
WND_PROP_AUTOSIZE
autosize property (can beWINDOW_NORMAL
, 0, orWINDOW_AUTOSIZE
, 1).
BooleanNORMAL
is 0 andAUTOSIZE
is 1
WND_PROP_ASPECT_RATIO
window's aspect ration (can beWINDOW_FREERATIO
orWINDOW_KEEPRATIO
).
BooleanKEEPRATIO
is 0 andFREERATIO
is 256
WND_PROP_OPENGL
opengl support.
Presumed to be Boolean, but documentation does not specify.
WND_PROP_VISIBLE
checks whether the window exists and is visible.
Presumed to be Boolean, but documentation does not specify.
WND_PROP_TOPMOST
property to toggle normal window being topmost or not.
Presumed to be Boolean, but documentation does not specify.
Here is a simple Python script for face detection in live video using OpenCV. harpy.py. Short and easy to modify if you want to start another project like this.