A Portable NDVI Device

Using Raspberry Pi to Build a Device to Measure Effects of Climate Change on Vegetation

Anna Heck
11 min readDec 28, 2021

Open a window or door and look outside. What do you see? If you live in Arizona or Texas you might see sand, cacti, or other warm-loving plants. Chances are the color scheme you are looking at is full of tans and browns. If you live in Alaska or Montana or somewhere similar, you will probably see lots of green trees and grass and plants. The color scheme you are looking at is probably pretty green. These colors are what make the places we live in so unique. But what happens when the green trees start to turn brown, and those plants you have grown up around no longer thrive? How does climate change affect the vegetation that shapes our environments?

Global vegetation map

What is NDVI?

NDVI stands for normalized difference vegetation index and it is a way to measure green, live vegetation. Basically, this means that we can determine what part of an area has live vegetation using a graphical indicator. We can also use NDVI to compare a certain area over time to see the change that has occurred to vegetation.

Picture of crops after NDVI was applied

NDVI is calculated via a mathematical algorithm. First, an image is collected using near-infrared lights. Different types of vegetation absorb different amounts of light, so this is used to calculate the value. For example, a green-leafed tree will absorb a lot of light, whereas snow will reflect it. One formula that is used to calculate NDVI is a near-infrared light minus reflection of the red light, divided by the near-infrared light plus reflection of the red light.

NDVI = NIR-RED/NIR+RED

Using this formula, a value between -1.00 and +1.00 is calculated. This provides us with a vegetation index, which tells us how dense and healthy a certain area is. Areas with rock or snow typically have an NDVI index of less than 0.1. On the other end, areas such as tropical rainforests that are very dense will have an NDVI of around 0.8.

Building the Device

So when I originally started this project, I did not realize that the Raspberry Pi is essentially a computer that you needed to construct. Although this set me back at first, it actually allowed for a lot of freedom in terms of what I could do. Because it is a computer, you can run ML programs on the device, which you typically cannot do with other small devices.

Constructing the Base

The first step in getting my project to work is building the base or the computer. In order to do this, I purchased a Raspberry Pi monitor. You can also run the program on a computer or TV screen with an HDMI plugin, but this was most efficient for me, as I wanted it to be portable.

The setup of the monitor is fairly simple. First, you screen the Raspberry Pi in between two protective pieces of plastic and attach it to the screen.

Next, you attach the HDMI and power plug into the Raspberry Pi and connect them to the main screen. This allows your Raspberry Pi to turn on.

Once that is done, the first step of building the device is finished! Next, we move on to setting up the Raspberry Pi infrastructure.

Setting up Raspberry Pi

When you plug your Raspberry Pi into a power source, the HDMI screen should power on. From here, you can control the pi just like you would any other computer.

The first thing we need to do to get the pi ready for our project is load some libraries. The libraries we need to load are NumPy, OpenCV, and a package called libatlas-base-dev. NumPy is a python library used to do mathematical calculations and arrays, which we will need to calculate NDVI. OpenCV is a library that allows us to run real-time computer vision, which means that we can gather information from pictures. Libaltlas-base-dev is a package that generates numerical software that we can use for mathematical calculations.

To load these libraries into the Raspberry Pi, we can use pip3.

sudo pip3 install -U numpy
sudo pip3 install opencv-python
sudo apt install libatlas-base-dev

After preparing the libraries, we can move on to setting up the camera. We will use the camera to take images that we can analyze using the NDVI software we build later.

The first step in setting up the camera is to connect it to the Raspberry Pi. You can do this by opening the clip on the pi and inserting the camera into it with the blue side facing the HDMI port. Once the camera is in place, you can check if it is picked up by the camera using raspistill -o test.jpg. If an image shows up, then the camera is connected.

Now you are ready to set up the python program that determines NDVI!

Creating the Python Program

The first step to building the python program to determine NDVI is to open a Thonny programming document. You can access this in the top left corner of the main screen, by clicking the button that looks like a raspberry.

Loading the Libraries

Once you open the document, you need to load your libraries. This will connect your document with the libraries we loaded onto the Raspberry Pi earlier. We need to import:

  • cv2 to import openCV
  • NumPy for array computations
  • Fastiecam for analyzing how much light is absorbed by each area in the picture
  • Pi camera and Pi camera array to take and upload pictures

To import Fastiecam you will need to download the python file and put it into your Raspberry Pi home folder. All the other libraries should have already been uploaded or included on your Raspberry Pi. You can get the Fastiecam file here.

Displaying the Image

After the libraries are imported, we can start working on loading and displaying the image. To do this you will need this code:

image = cv2.imread('/home/pi/park.png')
image = np.array(image, dtype=float)/float(255)
cv2.namedWindow('Original')
cv2.imshow('Original', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

The first line of code helps us load an image onto the Raspberry Pi so that we can use it. The next line will convert the image to an array, which makes it easier to represent and use in a machine learning model. The third line of code creates a window that is used in the next line to display the image on the HDMI screen. Finally, the last two codes initialize that the window will go away when any key is pressed.

cv2.namedWindow('Original') # create window

This next piece of code will name the window we created ‘Original.’ This is used to test out our code on a picture we load from the internet. In our actual code, we will comment this out.

def display(image, image_name):
...
display(original, 'Original')

To finish off displaying our image, we will sandwich our code in between the two lines above. This will turn our code into a function that can be used.

Creating Contrast

To calculate NDVI, we will need to increase the contrast of the image, so we can better see the dark and light areas. We will start by making another function:

def contrast_stretch(im):

To start, add these two lines of code under the function. These lines will help pick out the brightest pixels in the image, to base the contrast on.

in_min = np.percentile(im, 5)
in_max = np.percentile(im, 95)

Next, we need to set the maximum and minimum brightness we want our pixels to be. The max we can get on a Raspberry Pi screen is 255, so that is what we will set our max to be. We will set the minimum to be 0.

    out_min = 0.0
out_max = 255.0

Now we need to figure out the new value for the pixels. We can do that using this formula:

out = im - in_min
out *= ((out_min - out_max) / (in_min - in_max))
out += in_min

return out

To calculate out (the new pixel value) we need to first take the original value (im) and take it away from the minimum. We then will multiple that by the new min- new max/ original min — original max. Finally, we add the original minimum.

This concludes the calculations for the contrast, so now all we have to do is apply them. We can do this using display, which will create and show a new window with the contrast applied.

contrasted = contrast_stretch(original)
display(contrasted, 'Contrasted original')

To save this window we will use cv2.imwrite, which will save the updated image at a specific location with a name, in this example ‘Contrasted original.’

cv2.imwrite('contrasted.png', contrasted)

Now we have created our contrasted image! Next, we want to calculate NDVI based on our contrasted image.

Calculating NDVI of Pictures

To calculate NDVI, we are going to make the blue, lighter, pixels white and the red, darker, pixels black.

First, we need to create another function for calculating NDVI.

def calc_ndvi(image):

Now to get the red and blue pixels we need, we have to split the image. We can use cv2.split to do this and create red, blue, and green parts.

b, g, r = cv2.split(image)

After we split the image, we should have red and blue pixels left. First, we need to add the number of blue and red pixels together. This will be the NIR+RED part on the top of the NDVI equation I mentioned above. After that, we will subtract the blue pixels from the red pixels, which will be the NIR — RED. Finally, we will divide those two equations by each other, and return the result.

bottom = (r.astype(float) + b.astype(float))
bottom[bottom==0] = 0.01
ndvi = (b.astype(float) - r) / bottom
return ndvi

Now, we have calculated our NDVI! All we have left to do is apply the calculation to the picture and create a new window called NDVI.

ndvi = calc_ndvi(contrasted)
display(ndvi, 'NDVI')
cv2.imwrite('ndvi.png', ndvi)

Creating a Color Map

The last thing we have to do before taking our own pictures is to color the grey pixels we created using NDVI. The grey pixels signify the living tissue from the picture. We will turn the lighter pixels to red, and the ones that are darker will become blue.

The first thing we need to do, before changing the colors, converts the numbers to whole numbers. Originally, the calculated numbers were floats, which means they had decimals. But to use Fastiecam to change the colors, we can only use whole numbers.

color_mapped_prep = ndvi_contrasted.astype(np.uint8)

Once the numbers are whole numbers, we can use cv2’s color mapping function to create the new image- color mapped.

color_mapped_image = cv2.applyColorMap(color_mapped_prep, fastiecm)
display(color_mapped_image, 'Color mapped')
cv2.imwrite('color_mapped_image.png', color_mapped_image)

Integrating the Camera

To start, we want to comment out the line of code initializing the downloaded picture as the picture we are analyzing.

# original = cv2.imread('park.png')

After we comment that out, we are going to use the picamera library to have the camera take a picture. Also using picamera, we can save the captured picture as an array for analysis.

stream = picamera.array.PiRGBArray(cam)
cam.capture(stream, format='bgr', use_video_port=True)
original = stream.array

Now, we can save the new picture as ‘Original.’

cv2.imwrite('original.png', original)

And that’s it! Using this program you can identify dead and alive plants!

Results

To test out the device, I used a dying bamboo plant. As you can see below, the bottom was still alive (pink), while the top half was mostly dead. If you were to use a red filter on your camera, the final picture may have more colors, but since I used a regular camera, the living plant is represented just by the color pink.

Original (left) and contrasted (right)
NDVI (left) and color mapped (right)

Applications of NDVI

Farming

One application of NDVI is in farming. Using drones, farmers can take pictures of large portions of their crops at once and analyze them. Then using NDVI, it can be determined which areas are healthy or not, and how dense each specific region is. For some crops, too dense or too sparse of an area could mean issues with the yield. For example, the optimal density for corn crops is around 30.5 cm apart, which translates to sparse and medium density. If you want the best corn, then that is what you would look for in the pictures.

Climate Change

Another big application of NDVI is in measuring climate change- what I focused on. Because NDVI works great with analyzing large portions of land, it is the perfect tool for remote sensing. Using a satellite, pictures of coasts, countries, and even whole continents can be taken and analyzed. Using NDVI, we can get an idea of the health of vegetation from across the world.

Not only can we get an idea of the overall health, but using NDVI we can also compare change over time in certain regions. For example, an area of concern for climate change is coastal regions. Using NDVI we can see the number of live plants compared to in past years, as well as the water levels. Because NDVI senses different types of terrain, it can also classify ocean vs land. This means that we can tell if the ocean has risen and how far, based on the amount of land in a certain region.

Also using NDVI, we can better optimize the space that we have to live on. We can compare different terrains, and see if there are areas that we could benefit from utilizing, that previously weren’t used. We can also use NDVI to tell if there is overfarming in a certain area. Overfarming occurs when a certain area is used so much that it can no longer be used. This might appear as large portions of dead land that could not be used, as well as dying vegetation.

NDVI can be used as a tool to measure and fight climate change, as we understand the vegetation we live in and how it is changing.

Resources

If you’ve made it this far, thank you! I am a 16-year-old who is interested in regenerative medicine, biocomputing, and public health. If you want to see me continue to grow and 10X myself, sign up for my newsletter here!

--

--

Anna Heck

I'm a 17-year old trying to make science stories more accessible to all and fostering collaboration through science communications and emerging technologies.