FLIR ADK
Getting Sta rt e d
The information contained herein does not contain technology as
defined by EAR,15 CFR772, is publicly available, and therefore
not subject to EAR.
26
6.2 Running OpenCV algorithm
Most of the open source intrinsic camera calibrations eventually call the OpenCV function
findChessboardCorners. This function expects a white boarder around the chessboard.
However, when imaged with the ADK, hotter is brighter and colder in darker. When
illuminated with the light, the black squares absorb more radiation and heat up. So the dark
squares in the visible image end up being the white squares in the thermal image and visa
versa. The result the standard chessboard ends up with a black boarder and the grid is not
recognized.
There are several ways of solving the ‘findChessboardCorners needs a white boarder issue:
1. Print the chessboard with a black boarder. (This might cause problems for visible camera
calibration
2. Put the camera into ‘Black Hot’ mode. Normally the hotter something is the lighter in
appears in the image (aka White Hot). In Black Hot, hotter things are darker. Since the
typical chessboard has a white boarder, it stays cooler and therefor lighting in this mode
3. In software, invert the image to make the boarder lighter (shown below)
Most software packages call findChessboardCorners with some options enabled. Some of
those option make detecting the chessboard in thermal difficult. We recommend use the
only the Adaptive Threshold option when calling findChessboardCorneres.
The following example code is based the the Camera Calibration library in the Image
Pipeline ROS package. The code snippets show the lines of code changed in
ROS/image_pipline/camera_calibration/calibrator.oy to calibrate a thermal cameras
1) Invert the image creating the equivalent of a photographic negative. Here I’ve added ‘255 –
mono8a’ to the last else statement.
def mkgray(self, msg):
"""
Convert a message into a 8-bit 1 channel monochrome OpenCV image
"""
# as cv_bridge automatically scales, we need to remove that behavior
# TODO: get a Python API in cv_bridge to check for the image depth.
if self.br.encoding_to_dtype_with_channels(msg.encoding)[0] in
['uint16', 'int16']:
mono16 = self.br.imgmsg_to_cv2(msg, '16UC1')
mono8 = numpy.array(mono16 / 256, dtype=numpy.uint8)
return mono8
elif 'FC1' in msg.encoding:
# floating point image handling
img = self.br.imgmsg_to_cv2(msg, "passthrough")
_, max_val, _, _ = cv2.minMaxLoc(img)
if max_val > 0:
scale = 255.0 / max_val
mono_img = (img * scale).astype(numpy.uint8)
else: