This is a tutorial for implementing a very basic adaptive linear automatic gain control (AGC) for the output of the FLIR Lepton camera.

The Need

When using a Lepton in radiometic mode, you get 14 bits of grayscale data. It’s only natural to want to view this image yourself to make sure that it’s pointed in the right direction, rotated correctly, and just generally working how it should. Computer displays can typically only display around 8 bits of grayscale data, though, so you’ll need to find a way to map that 14 bit data into an 8-bit color space before you can view it.

There are a lot of ways to do this, which all have plusses and minuses. The first that most people think of, and the default way for a lot of image viewers, is to discard the 6 least significant bits of every pixel. This solves the problem, but you lose a ton of detail, which in this case is a synonym for contrast.

laptop truncated

It’s almost impossible to figure out what the subject of this image even is. There has to be a better way.

A Better Way

Eyes need contrast to pick up detail. No getting around that. So we’ll introduce contrast by discarding temperature data outside of the range of temperatures currently in the frame.

lowVal = minPixel(image)
highVal = maxPixel(image)
countRange = max(highVal - lowVal, 1)
for pixel in image:
    agcPixel = (pixel - lowVal) * (255.0 / countRange);

This is just a linear mapping. It’s y=mx+b applied to an entire image, with values for m and b calculated on a per-image basis. When applied to the image from before, it looks pretty good.

laptop linear

Linear AGC’s Downfall: Outliers

A huge pitfall to linear AGC’s in general is that they don’t respond well to very hot or very cold things. Firefighting comes to mind.

Consider a firefighter trying to locate a human in a room filled with smoke because of a fire. The walls and floor might be at around 30° C, and the human is at a typical body temperature of 37° C. The fire might be around 1000° C.

If the firefighter is carrying a FLIR firefighting unit with state-of-the-art image processing, picking out the human would be no trouble at all. However, if the firefighter is carrying a thermal firefighting unit that uses a linear AGC, the 7 degrees Celsius between the human and the room won’t show in the image as visible contrast, since it represents only 0.7% of the total range. This is obviously very bad.

I don’t have a fire nearby, thankfully, so here’s an image of a handprint next to a 3D printer extruder set to 240° C.

fire linear

You can hardly tell that the handprint is there. If you’re running through a burning building looking for that handprint, you’re going to seriously struggle if this is the AGC you’re stuck with. A reasonable response might be capping the upper limit of the AGC at 100° C or so. This works, sort of, but then the firefighter would struggle to determine where the hottest parts of the fire are, since everything hotter than 100° C will look the same. That might look like this in my example.

fire clipped

You can’t even see exactly where the extruder is, because it’s all just a blob. There isn’t an easy fix to this, unfortunately. The solution is to ditch linear AGC entirely and move to a more sophisticated technique like histogram equalization. Here’s the same image put through OpenCV’s contrast-limited adaptive histogram equalization.

fire equalization

You can even get more detail out of the image of a laptop from earlier with this technique.

laptop equalization

Despite this major drawback, linear AGC can be an excellent choice for a quick and easy viewer when your Lepton is has radiometry turned on. It will help you verify functionality and ensure the camera is pointed where you want it to be. It won’t, however, come close anywhere to the Lepton’s built-in AGC in terms of image quality.