diff --git a/.vscode/settings.json b/.vscode/settings.json index 44c0ea7..be6b7a3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -50,6 +50,9 @@ "randr", "raspberrypi", "rclone", + "subdev", + "subdevice", + "subdevices", "Tailscale", "tflite", "Twidec", diff --git a/README.adoc b/README.adoc index d15a557..d499ce1 100644 --- a/README.adoc +++ b/README.adoc @@ -198,6 +198,41 @@ ln --force --relative --symbolic systemd/user/* ~/.config/systemd/user/ systemctl --user enable --now detectionator.service ---- +== HDR + +The Raspberry Pi Camera Module 3 supports HDR, but only at a lower resolution. +HDR support has to toggled when `detectionator.py` isn't running and requires passing a special flag to `detectionator.py`. + +. Show the available V4L subdevices. ++ +[,sh] +---- +ls /dev/v4l-subdev* +/dev/v4l-subdev0 /dev/v4l-subdev1 /dev/v4l-subdev2 /dev/v4l-subdev3 +---- + +. To enable HDR support for the Raspberry Pi Camera Module 3, use the following command on one of the V4L subdevices. +In my case, this ended up being `/dev/v4l-subdev2`. ++ +[,sh] +---- +v4l2-ctl --set-ctrl wide_dynamic_range=1 --device /dev/v4l-subdev2 +---- + +. Now that the Camera Module 3 is configured, just run `detectionator.py` with the `--hdr` flag to default to the proper resolution sizes when HDR is enabled. ++ +[,sh] +---- +./detectionator.py --hdr +---- + +. To disable HDR support for the Raspberry Pi Camera Module 3, use this command with the corresponding V4L subdevice. ++ +[,sh] +---- +v4l2-ctl --set-ctrl wide_dynamic_range=0 --device /dev/v4l-subdev2 +---- + == Development It's recommended to use the provided {pre-commit} checks when developing. diff --git a/detectionator.py b/detectionator.py index cac1d4b..e9a8bdf 100644 --- a/detectionator.py +++ b/detectionator.py @@ -21,23 +21,14 @@ # todo Figure out the best values for the normal and low resolution sizes. -# normal_size = (640, 480) -# low_resolution_size = (320, 240) - # Camera Module 3: # HDR disabled: 4608 x 2592 # HDR enabled: 2304 x 1296 # -# Enable: -# v4l2-ctl --set-ctrl wide_dynamic_range=1 -d /dev/v4l-subdev2 -# -# Disable: -# v4l2-ctl --set-ctrl wide_dynamic_range=0 -d /dev/v4l-subdev2 -# -normal_size = (4608, 2592) -# low_resolution_size = (576, 324) -# low_resolution_size = (1152, 648) +default_normal_resolution_size = (4608, 2592) default_low_resolution_size = (2304, 1296) +default_normal_resolution_size_hdr = (2304, 1296) +default_low_resolution_size_hdr = (1152, 648) def ReadLabelFile(file_path): @@ -99,16 +90,27 @@ def main(): help="The device path for the GPS serial device.", default="/dev/ttyUSBAdafruitUltimateGps", ) + parser.add_argument( + "--hdr", + action=argparse.BooleanOptionalAction, + help="This option uses the default resolution for the Camera Module 3 when in HDR mode.", + ) parser.add_argument("--label", help="Path of the labels file.") parser.add_argument( "--low-resolution-width", help="The width to use for the low resolution size.", - default=default_low_resolution_size[0], ) parser.add_argument( "--low-resolution-height", help="The height to use for the low resolution size.", - default=default_low_resolution_size[1], + ) + parser.add_argument( + "--normal-resolution-width", + help="The width to use for the normal resolution size.", + ) + parser.add_argument( + "--normal-resolution-height", + help="The height to use for the normal resolution size.", ) parser.add_argument( "--match", help="The labels for which to capture photographs", nargs="*" @@ -136,12 +138,32 @@ def main(): if label_file: labels = ReadLabelFile(label_file) + normal_resolution_size = default_normal_resolution_size + low_resolution_size = default_low_resolution_size + if args.hdr: + normal_resolution_size = default_normal_resolution_size_hdr + low_resolution_size = default_low_resolution_size_hdr + normal_resolution_width = normal_resolution_size[0] + normal_resolution_height = normal_resolution_size[1] + low_resolution_width = low_resolution_size[0] + low_resolution_height = low_resolution_size[1] + + if args.normal_resolution_width: + normal_resolution_width = args.normal_resolution_width + if args.normal_resolution_height: + normal_resolution_height = args.normal_resolution_height + + if args.low_resolution_width: + low_resolution_width = args.low_resolution_width + if args.low_resolution_height: + low_resolution_height = args.low_resolution_height + if ( - normal_size[0] / args.low_resolution_width - != normal_size[1] / args.low_resolution_height + normal_resolution_width / low_resolution_width + != normal_resolution_height / low_resolution_height ): print( - f"The low resolution width, '{args.low_resolution_width}', and low resolution height, '{args.low_resolution_height}' must be a fraction of the normal size, '{normal_size[0]}x{normal_size[1]}'" + f"The low resolution width, '{low_resolution_width}', and low resolution height, '{low_resolution_height}' must be a fraction of the normal size, '{normal_resolution_width}x{normal_resolution_height}'" ) sys.exit(1) @@ -175,12 +197,12 @@ def main(): picam2.options["quality"] = 95 picam2.options["compress_level"] = 9 config = picam2.create_still_configuration( - main={"size": normal_size}, + main={"size": (normal_resolution_width, normal_resolution_height)}, lores={ # Only Pi 5 and newer can use formats besides YUV here. # This avoids having to convert the image format for OpenCV later. "format": "RGB888", - "size": (args.low_resolution_width, args.low_resolution_height), + "size": (low_resolution_width, low_resolution_height), }, buffer_count=4, # Don't display anything in the preview window since the system is running headless.