Thermal imaging is considered to be one of those state-of-the-art technologies in computer vision that in the modern-day scenario finds its application in various places. As the pandemic situation worsens in 2021, more and more noncontact temperature detection tools are in demand. A thermal temperature sensor that can measure the temperature of the human body can also be used in multiple domains for a variety of applications like Medical Diagnosis (which can detect skin diseases, tumors, and even some forms of cancers), fault detection (in circuits and appliances) Defense and surveillance (for night vision and human detection) and many more.
So, today in this article, we will be making a simple Raspberry-Pi based thermal image sensor that can detect the temperature of any object. It can also measure the body temperature, which we will be able to see in our display, so without further ado, let's get right into it.
Previously, we have also built other similar thermometers for body temperature measurement, you can find them here.
- Arduino based digital thermometer using MAX30205
- Contactless Body Temperature screening using RFID and Arduino with MLX90614
- Non-Contact Infrared Thermometer Gun using Arduino
- Wall Mount Non-Contact Thermometer using Arduino
- Smart Thermometer using Arduino with Android Application
The AMG8833 Thermal Image Sensor
Today, we will try to interface a simple Thermal camera module called AMG8833. It is a thermal grid-eye sensor from Panasonic, you can also call it a Temperature Monitoring device meaning it divides the captured data into several blocks of 8x8 = 64. Its FOV (Field of View) is 60x60 sq. deg. This might be narrow compared to visible imaging camera arrays but is apt when compared to even industrial-level thermal cameras. The number of blocks is equivalent to the number of pixels in the camera image (heatmap) and this is called the resolution of the thermal camera. Hence, AMG8833 has a resolution of 8x8 or 64 pix. Each pixel is like a separate IR sensor and gives a separate temperature measurement, making this sensor much better than PIR and pyrometric sensors that can provide only a single temperature value.
For people who are not familiar with thermal cameras, this might seem to be very less compared to the visible camera segment. But the conventional cameras have much higher resolutions and lower costs than the AMG module. For instance, the AMG module costs around 4-5k INR (or even more) depending on the seller and originality. Yet, it only provides 64 pix resolution while the Rpi camera v1, which costs under 500 INR, provides a 5MPx resolution which is like 100 times more.
- The main reason behind this expense is the lens cost. Glass is more or less opaque to IR waves and thermal imaging arrays mostly detect waves of wavelength 8-14 um, which come under LWIR or Long Wave IR category. Hence, more expensive materials like germanium or chalcogenides are used.
- The circuitry is another cost factor since extra care has to be taken that the temperature of the camera itself does not affect its readings, which is very common when cameras are used for prolonged periods. Thermal cameras whose images are not affected by their temperature are called Radiometric Thermal Cameras.
- Production problems and heavy custom duties are other problems in this sector, as thermal imaging is considered to be a niche and expensive domain.
- Heat dissipation and the large size of the camera array are two major reasons due to the comparatively large size.
Nonetheless, after the discovery of a few materials and techniques, thermal cameras have become very affordable and user-friendly. Most of them are available as I2C sensors or USB cameras. Now, let's talk about some basic principles of thermal imaging, some important parameters, and useful tips while playing with a thermal domain.
Principles and Concepts Related to Thermal Imaging
IR-based temperature measurement works on a simple principle illustrated by Stefan Boltzmann's law - Hot bodies emit radiation in the form of IR waves. The quantity of radiation emitted and its relation with the temperature of the emitting body is given by Stefan Boltzmann's law:
Where:
- P is the Power emitted by the object emitting the radiation (in Watts)
- A is the Surface Area of the object emitting the radiation (in m2)
- s is the Emissivity of the object emitting the radiation (constant unique to the object properties, no units)
- σ is (W.m)
- T is the temperature of the object emitting the radiation (in Kelvin)
Using this relation, it is possible to back-calculate the surface temperature of a non-shiny object, like the human skin or a vessel of hot water using the inputs emissivity. However, in practical situations, due to attenuation losses, errors occur in the calculation process. Other parameters that affect the thermal readings include ambient temperature, relative humidity, and lighting conditions (if the light source emits a lot of thermal radiation, like a bulb).
The emissivity of a human is approx. 0.97, and for a perfect black body, this ratio is 1. Other values of emissivity for common materials are easily available online. The presence of the emissivity constant makes IR-based temperature measurement tricky because even at the same temperature different materials will emit a different amount of IR radiation. This sets the stage for an important point–IR-based temperature measurement is not accurate for all materials. Generally, the higher the emissivity (or closer a material is to a perfect black body), more accurate is the temperature estimation for that material. Gases and sparsely dense objects are also difficult to capture on a thermal camera, which means that if you try to capture steam using a normal thermal camera, you will just see the normal ambient temperature and not 100 degC.
A second important note is that IR waves attenuate exponentially (to the power of 4) with distance. This means without compensating for these errors, the temperature measurement will be a few degrees lesser than the actual value depending on the distance between the camera and the object.
The above graphs show the approximate effect of the distance between the pyrometric sensor and the subject to be screened for various ambient temperatures ranging from 73 deg to 91 deg. Now, Let’s focus on the AMG8833 sensor and its coding.
Interfacing AMG8833 with Raspberry Pi
As mentioned earlier, AMG8833 is an 8x8mm I2C thermal camera sensor from Panasonic. Only higher resolution thermal camera sensor comes under 10K INR. It's because it is not a USB thermal cam like the MLX90640, and while it has a higher resolution (32x24), it makes a lot of trade-off between the pixels refreshing algorithm for this extra resolution. Simply put, despite the extra resolution, I feel AMG8833 produces better images with lesser effort. It might also be because the software libraries for AMG8833 produce better color maps than that of MLX90640. The output of the sensor itself is an 8x8 2D array of temperature values. These temperature values can then be used to produce a false color heatmap.
This is done in 4 ways mainly:
1. By dividing a spectrum of color values between the minimum and maximum temperature values captured by the AMG8833 sensor. This creates a dynamic color map, where depending on the minimum and maximum values of your scene, a particular temperature value might be assigned different colors. This means that the thermal image does not provide actual temperature data and hence that data is lost. However, this provides a good contrast in almost any surrounding environment. There are a lot of CMAPS available, you can visit the website for matplotlib where you can find more of these CMPs to better calibrate the device for your particular application.
2. The other way is by fixing a minimum and maximum temperature and dividing the color range once and for all. This will be a static color map and hence you can map your colors amongst the necessary temperatures to maximize the contrast ratio. But your image will also have some temperature data (though not all) but the biggest disadvantage is that you will have to manually calibrate it for every new environment.
3. The last two methods are to directly scale the temperature values into grayscale values using static or dynamic scaling as mentioned above, but this retains the entire temperature information as there is no colormap involved. The problem with color maps is that there are a lot of them, with overlapping colors. Hence, it is too much of an effort to recreate a temperature matrix from a color-mapped image. Unfortunately, grayscale thermal images are very difficult to view because of the bad contrast that is created by the lack of colors.
Hence, depending on your requirement, you may choose a particular method of mapping the matrix of values into a viewable image. For advanced thermal imaging, it is better to play with the matrix itself as it captures even minute temperature variations which might not be visible in an image to a human eye. However, for a low-resolution sensor like AMG8833, it is sufficient to use a static CMAP. If you are asking why? We will verify that later in the article.
AMG8833 Based Thermal Temperature Sensor - Components Required
Here, we are using a Raspberry Pi Zero with Raspbian OS. All the basic Hardware and Software requirements are previously discussed, you can look it up in the Raspberry Pi Introduction and Raspberry PI LED Blinking for getting started. Other than that we need:
- Raspberry Pi (Any Version of RPI will Work)
- AMG8833
- 5V piezo buzzer
- BC547 or 2N222 transistor
- 10k Resistor
- HDMI display
- Mouse and keyboard or VNC (for using the RPI)
Schematic Diagram for AMG8833 Based Thermal Temperature Sensor
Let’s look at the schematic, we will be using the RPi Zero, the AMG8833 module, and a 5V piezo-electric buzzer. You can use an Arduino as well, but it is a bit of a pain to display images using a single script or embedded device. You will have to use a desktop system to visualize the images using the Processing IDE or python. As there are 64 sensor values, uploading the data on a server like Arduino IOT Cloud or ThingSpeak does not fulfill the requirement too. Hence, I chose a Raspberry Pi to process my images using python.
The connections are quite simple. The buzzer is triggered by a GPIO, via the base of a transistor (2N222 or BC547), and the AMG8833 sensor is connected to the SCL and SDA pins of the Raspberry Pi 40 pin header. You can use any RPi, I have used RPi Zero-W. We will be using a 7” HDMI Display for viewing the thermal images. I have modified a simple AMG with a python library that can be found here to integrate my buzzer and some other variations.
AMG8833 Based Thermal Temperature Sensor- Python Code
The buzzer buzzes every time the sensor takes a new image in my code, feel free to modify it to give audio alerts for high temperature or human detection. I have attached the library zip just in case you are unable to download the original library. Each line is explained by a corresponding comment statement. The code itself does not have many comments, as it makes editing difficult sometimes. Hence, I like to keep a separate copy for comments.
Block 1: include libraries
import time,sys #to keep a track of time and system path sys.path.append('../') #allows us to directly import the amg8833_i2c compiled python file without full path import amg8833_i2c #the compiled python library for using amg8833 with python import numpy as np #used for computation and array manipulations import matplotlib.pyplot as plt #used for plotting the 2D temperature array as an image from scipy import interpolate #used for interpolation algorithms import RPi.GPIO as GPIO #used to interface the buzzer and use the RPi GPIOs
Block 2: Sensor and GPIO initialization
GPIO.setmode(GPIO.BCM) #This allows us to use the GPIO numbering instead of the pin numbering GPIO.setwarnings(False) #disables warnings BUZZER= 23 #Buzzer attached to GPIO 23 via transistor buzzState = False #variable to store value for buzzer pin. GPIO.setup(BUZZER, GPIO.OUT) #configures buzzer pin as output t0 = time.time() #initialise a variable to store current time sensor = [] #variable to store amg8833 object
Now, the AMG8833 sensor has 2 I2C addresses, in case the default address 0x69 is occupied by some other I2C sensor. The alternate address can be used by shorting the 2 pads at the back of the module, which connects the AD0 pin of the AMG8833 to 5V.
while (time.time()-t0)<1: # wait 1sec for sensor to start try: sensor = amg8833_i2c.AMG8833(addr=0x69) # start AMG8833 at default address (AD0 is GND) except: sensor = amg8833_i2c.AMG8833(addr=0x68) # start AMG8833 at default address (AD0 is VCC) finally: pass time.sleep(0.1) # wait for sensor to settle if sensor==[]: # If no device is found, exit the script print("No AMG8833 Found - Check Your Wiring") sys.exit(); # exit the app if AMG88xx is not found
Block 3: Interpolation
As the AMG8833 only has 64 pixels, the image it produces is a blocky low-resolution image that makes very little sense, as shown below.
Hence, to make the image more understandable, we will use interpolation. Interpolation is the method of increasing the resolution of the heatmap image generated by using software algorithms to generate pixels from neighboring pixels. There are a lot of interpolation algorithms like cubic, bicubic, Lanczos, etc. We will be using a simple cubic interpolation algorithm. The original resolution is, as we know 8x8 is the default resolution of the sensor. We use the linspace function from numpy library to generate equally spaced samples, from 0 to 7, stored in xx and yy variables. This is basically for making the temperature array to store the 64 temperature values.
pix_res = (8,8) # original pixel resolution xx,yy = (np.linspace(0,pix_res[0],pix_res[0]), np.linspace(0,pix_res[1],pix_res[1])) #check here for details zz = np.zeros(pix_res) # set array with zeros first Now, we use a multiplier to increase the resolution to (pix_mult*8 x pix_mult*8) and use the linspace function again to generate equally spaced samples for the interpolated image - pix_mult = 6 # multiplier for interpolation interp_res = (int(pix_mult*pix_res[0]),int(pix_mult*pix_res[1])) #final resolution of interpolated image grid_x,grid_y = (np.linspace(0,pix_res[0],interp_res[0]), np.linspace(0,pix_res[1],interp_res[1])) The algorithm for interpolation will be cubic interpolation, as coded below using the interpolate function from scipy library - def interp(z_var): #pass the original AMG8833 temperature matrix as z_var for interpolation f = interpolate.interp2d(xx,yy,z_var,kind='cubic') #check here for details return f(grid_x,grid_y) grid_z = interp(zz) # interpolated image
Block 4: Figure initialization
Now, that we have our interpolated array, let's convert the array into a viewable heatmap. As discussed earlier, we will be using a static cmap as we want to view our hand as clearly as possible, enough to distinguish the fingers. This can be done only by using custom, fixed temperature bounds. The general room temperature is around 25 degC, and the hands are at around 27-30 degC. Hence, we have used a range of 27-32 degC so that the hand is contrasted from the surroundings. We also store this background, since it remains more or less the same, and copy it to the image to save plotting time. This is called blitting. We will use the rainbow color map in non-inverted form. You can replace it with any color map from the matplotlib website which I have linked previously. For eg, to use PiYG colour map in inverted form, replace cmap=plt.cm.rainbow by cmap=plt.cm.PiYG_r. adding _r at the end inverts the color map. Generally, cool colors (black, blue, green) are used to indicate cooler temperatures.
plt.rcParams.update({'font.size':16}) #fontsize for all elements of the matplot fig_dims = (10,9) # figure size fig,ax = plt.subplots(figsize=fig_dims) # start figure fig.canvas.set_window_title('AMG8833 Image Interpolation') #Name of image window im1 = ax.imshow(grid_z,vmin=27,vmax=32,cmap=plt.cm.rainbow) # plot image, with temperature bounds cbar = fig.colorbar(im1,fraction=0.0475,pad=0.03) # colorbar, shows mapping between the cmap and temps. cbar.set_label('Temperature [C]',labelpad=10) # temp. label # label of the colour bar fig.canvas.draw() # draw figure ax_bgnd = fig.canvas.copy_from_bbox(ax.bbox) # background for speeding up runs using blitting fig.show() # show figure
Block 5: Real-time plotting – the final loop function
Now that we are done with all the setup, we proceed to the looping part of our code–
while True: #forever loop status,pixels = sensor.read_temp(64) # read AMG8833 pixels with status if status: # if error in pixel, re-enter loop and try again continue print(pixels) #print the pixels on the command line interface GPIO.output(BUZZER, 1) #Buzzer high #print("Buzzer ON") time.sleep(1) #sleep for 1 second GPIO.output(BUZZER, 0) #Buzzer low T_thermistor = sensor.read_thermistor() # read thermistor temp print("Thermistor Temperature: {0:2.2f}".format(T_thermistor)) # print thermistor temp fig.canvas.restore_region(ax_bgnd) # restore background (speeds up run) new_z = interp(np.reshape(pixels,pix_res)) # interpolate the captured array im1.set_data(new_z) # update plot with new interpolated temps ax.draw_artist(im1) # draw image again fig.canvas.blit(ax.bbox) # blitting - for speeding up run fig.canvas.flush_events() # for real-time plot
This ends the coding and interfacing part.
Working and Practical Applications
My full setup is shown below. The output image will initially be blue if your surrounding temperatures are around 27 degC or less. After you introduce your hand over the sensor, you should see your hand and distinguish pals and fingers very well. If you cannot, try tweaking the min-max temperature values in the show function. You can refer to the original 64-pixel array captured by the AMG8833 module that we printed on CLI. After viewing multiple images, you can easily figure out the approx.. min, max, and object temperatures for your surroundings and get the perfect contrast with the object. You can also play around with different cmaps and image plotting methods to see which one suits you the best. Lastly, you can try other interpolation algorithms like Lanczos for even better results and understanding.
Practical applications: The project we just developed is not just for playing around with some hardware. You can try and test the sensor to check the onboard temperatures of your RPI core and compare them with the values shown by the RPI core temperature widget. Just note that because of attenuation and emissivity differences, the core temperature might be a few degrees lower than what is reported by the widget. You can also test for heat and electrical malfunctions. The sensor can also be used with a TF-lite tiny ML model to detect humans and faces. You can see the output of the code explained earlier and temperature checking of the RPI core in the demo video below. The weird noise in the background is my buzzer, which I feel is due to its short legs breaking off connection with the breadboard contacts.
Output 1: Thermal image of the hand. With proper contrast, even fingers are visible, which is commendable for such low resolutions.
Output 2: AMG8833 temperature array and thermistor temperature data on the CLI.
Output 3: Thermal image of RPI core. As clearly visible, the board is at around 45 degC and the core is at around 50-55 degC.