raspberrypi / picamera2

New libcamera based python library

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

[HOW-TO] Set exposure measurement area

mmoollllee opened this issue · comments

I'm captureing images every couple minutes to produce timelapse videos.
My current configuration is not yet achieving the optimal results, as exposure varies much (e.g. if cloudy or not).
I'd like to define a specifiy area to measure Exposure. Is that possible, or is there other way to get more even exposure?

My current setup looks like this:

dir = 'some/path'
timestamp = '2024-02-28_15-57'
with Picamera2() as picam2:
      picam2.start(show_preview=False)
      picam2.set_controls({
         "AeMeteringMode": controls.AeMeteringModeEnum.Spot,
         "AeExposureMode": controls.AeExposureModeEnum.Long,
         "Brightness": 0,
         "ExposureValue": 0,
         "FrameDurationLimits": (100, 10000000)
      })
      ctrl={}
      capture_config = picam2.create_still_configuration(ctrl)

      picam2.switch_mode_and_capture_file(capture_config, os.path.join(dir, timestamp+".jpg"))

      picam2.stop()

Thank you for your work!!!

Hi, thanks for attaching the helpful test script. Looking at it, I would recommend adding something like time.sleep(0.5) after calling set_controls, as the auto exposure will take a few moments settle.

In your question, you say the the exposure varies too much. To some extent this is unavoidable when changing between cloudy or sunny conditions. Can you say how you would like this to change?

For example, you could restrict the available range of exposure times, but you may simply find that some of your images are over or under exposed. Or you could change the metering mode (for example, from "spot" to "matrix"), but this may or may not make much difference. You can get quite precise control of these behaviours in the camera tuning file, but it would be helpful to know what you would like to achieve. Thanks!

Thank you for you fast answer and the tipp with time.sleep(0.5). I will implement this right away and see if it helps!

To clarify the case see the following two pictures taken in 5 minutes.
I thought to solve this behaviour it would help to define a area on the sensor that should be used for exposure calculation (see red rectangle area).
Is that possible?
From documentation I know there is a "AeMeteringMode": controls.AeMeteringModeEnum.Custom, but I have no idea how to set this up... Sorry for my missing knowledge :(

2024-02-28_12-15-21
2024-02-28_15-20-06

Yes, you can customise the metering area. You will need to edit the camera tuning file to do this.

Normally, you should find this in the folder /usr/share/libcamera/ipa/rpi/vc4 for Pi 4 or earlier devices. Look for a file named <your-camera>.json. For example, the camera module 3 would be imx708.json (though note that there is a wide angle version). Anyway, the easiest way to check you've found the right file is to insert a few characters of garbage at the top and check that the camera doesn't start!

Once you have this file, save a backup (just in case!), then open it and search for "metering_modes". There you will find a list of 15 weights for each mode that are applied to different parts of the image. You can set to zero any regions that you don't care about. The map of where the regions lie can be found in section 5.9.2 of the camera tuning guide (page 31).

For Pi 5 the arrangements are similar, but slightly different. Let me know if you are using a Pi 5.

Awesome!! I understand.
So for my case I came up with something like [5,4,4,1,4,3,3,1,1,0,2,0,0,1,1].

I'll try out and see what the results look like tomorrow!

From checking examples/tuning_file.py I came up with the following to set custom weights in python before starting the camera.

dir = 'some/path'
timestamp = '2024-02-28_15-57'

# load tuning file
tuning = Picamera2.load_tuning_file("imx477.json")
algo = Picamera2.find_tuning_algo(tuning, "rpi.agc")
algo["metering_modes"]["custom"] = {"weights": [5,4,4,1,4,3,3,1,1,0,2,0,0,1,1]}

with Picamera2(tuning=tuning) as picam2:
      picam2.start(show_preview=False)
      picam2.set_controls({
         "AeMeteringMode": controls.AeMeteringModeEnum.Custom,
         "AeExposureMode": controls.AeExposureModeEnum.Long,
         "Brightness": 0,
         "ExposureValue": 0,
         "FrameDurationLimits": (100, 10000000)
      })
      
      # auto exposure might take a few moments settle
      time.sleep(0.5)
      
      ctrl={}
      capture_config = picam2.create_still_configuration(ctrl)

      picam2.switch_mode_and_capture_file(capture_config, os.path.join(dir, timestamp+".jpg"))

      picam2.stop()

From what I understand I can't set the metering areas without restarting the camera, e.g. by clicking on a preview window as it's done on Smartphone devices?

Thanks for your help!!

Yes, you will have to completely shut down and restart the camera system to force it to re-read the camera tuning file. libcamera does not expose an API to change this at runtime - though I suppose it might do one day if enough people ask for it!

Yes, you will have to completely shut down and restart the camera system to force it to re-read the camera tuning file.

That's actually already happening in my code, right?

libcamera does not expose an API to change this at runtime - though I suppose it might do one day if enough people ask for it!

Where should I ask for it? In the raspberrypi/libcamera repo?

Still monitoring the change, but so far I think it already works better here

Thank you again!

Yes, looks to me like your code should be doing that!. I said there's no API for updating this at runtime, but you can switch between metering modes at runtime, for example with

picam2.set_controls({'AeMeteringMode': controls.AeMeteringModeEnum.Custom})

I suppose you could switch between modes interactively just to test that you're getting difference you expect.