scriptorron / indi_pylibcamera

INDI libcamera driver made in Python
MIT License
13 stars 4 forks source link

Report SensorBlackLevels metadata #29

Closed aaronwmorris closed 1 year ago

aaronwmorris commented 1 year ago

Would there be any way to report the SensorBlackLevels metadata?

This is a recent discovery for me, but in RAW mode, most of the camera modules have an automatic (16-bit) offset of 4096 for every pixel. Every pixel needs to be offset by -256 in 12-bit mode or -64 in 10-bit mode to return to the true value of each pixel.

scriptorron commented 1 year ago

I checked the picamera2 manual: the SensorBlackLevels metadata is easy to get. In fact the driver already reads the metadata to write analog gain, true exposure time and sensor temperature in the FITS. It will be easy to extend this by SensorBlackLevels.

The SensorBlackLevels are 4 integer values (uint16_t), one for each color in the Bayer pattern. Do you have a preference how to output them? For instance I can add 4 values to the FITS header: SensorBlackLevels_0, SensorBlackLevels_1, SensorBlackLevels_2 and SensorBlackLevels_3. What would be the easiest way for you?

Another question: Am I right that this makes only sense for raw images and not for pre-processed RGB images?

aaronwmorris commented 1 year ago

Reporting each value as _0, _1, etc is fine.

The black level values only make a sense when the Auto White balance is disabled, when AWBGains is defined.

scriptorron commented 1 year ago

I found right now that the names in FITS headers are limited to 8 characters, SensorBlackLevels_* is too long. So I will call them SBLK_0, SBLK_1, SBLK_2 and SBLK_3.

The values will be in the FITS header only when libcamera made them available in the picture metadata.

The updated code is in branch #29. It is still not tested. I will do that this night or tomorrow.

aaronwmorris commented 1 year ago

After reviewing some FITS files.. we may want to use OFFSET_#. The black level is technically just an offset which is a standard concept of cameras. The common fits header is OFFSET

scriptorron commented 1 year ago

Makes sense. I will do it like that.

scriptorron commented 1 year ago

Very strange. The pilibcamera2 documentation (https://datasheets.raspberrypi.com/camera/picamera2-manual.pdf) says:

The black levels of the raw sensor image. This control appears only in captured image
metadata and is read-only. One value is reported for each of the four Bayer channels,
scaled up as if the full pixel range were 16 bits (so 4096 represents a black level of 16 in 10-
bit raw data).

I don't understand the example at the end. To scale up 10bit data to 16bit I have to multiply with 64 (or left shift by 6). According to my calculations a black level of 4096 at 16 bit would be at 10bit a value of 4096 / 64 = 64. That's very different from 16!

I tried it with my HQ camera and it does not matter what raw mode I use (12bit w/o binning, 12bit with 2x binning, 10bit with 2x binning), I always get SensorBlackLevels of 4096. That would be 256 at 12bit and 64 at 10bit. Does that makes sense?

aaronwmorris commented 1 year ago

Yes, it makes sense. This was a challenge for me in indi-allsky. Some 12-bit cameras automatically upscale their data to 16-bit, others do not. I have to detect the maximum value in the image and infer the bit depth of the camera.

Once I have established the depth, I scale the black level with a right shift. This is my code in indi-allsky. I used CV2 to subtract since it automatically clips the data at 0. Numpy requires more handling since it will underrun on uint16 (back to 65536)

                    black_level_scaled = int(libcamera_black_level) >> (16 - self._max_bit_depth)

                    # use opencv to prevent underruns
                    data = cv2.subtract(data, black_level_scaled)
scriptorron commented 1 year ago

Thank you. I will merge the branch.