/passportpix

Take a passport photo of proper dimensions using computer vision

Primary LanguagePython

passportpix

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.

How to run

apt install python-opencv opencv-data
git clone https://github.com/hackerb9/passportpix
cd passportpix
./passport.py

Notes

Resolution

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.

TIP

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.

TIP

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).

Customization

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

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.

Current keys

  • 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

Experimental keys for resolution

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.

Current Assumptions

  • I assume you always want the highest resolution possible from your camera.

  • You'll need to have 'python-opencv' installed. (apt install python-opencv)

Bugs and Future Features.

  • 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.

Debugging & such

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' or camera_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 be WINDOW_NORMAL or WINDOW_FULLSCREEN).
      Boolean NORMAL is 0 and FULLSCREEN is 1

    • WND_PROP_AUTOSIZE
      autosize property (can be WINDOW_NORMAL, 0, or WINDOW_AUTOSIZE, 1).
      Boolean NORMAL is 0 and AUTOSIZE is 1

    • WND_PROP_ASPECT_RATIO
      window's aspect ration (can be WINDOW_FREERATIO or WINDOW_KEEPRATIO).
      Boolean KEEPRATIO is 0 and FREERATIO 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.

Bonus: Harpy.py

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.