Using Image Recognition to Assess Forest Areas Prone to Wildfires
Advisor: Ms. Shahin
Internship Location: Berkeley (Virtual)
Onsite Mentor: Ignas Bolsakovas
While the South Atlantic suffers from hurricanes, wildfires define the West Coast. They have been the most occurring natural disaster in California in the last two decades–made clear by the fires currently raging in LA. It’s an all too familiar problem for Bay Area residents, one that Has no clear solution. While traditional methods of fire mitigation focus on reactive measures like early detection and fire suppression, my project shifts the focus to preemptive prevention by using image recognition to identify fire-prone areas. Through this virtual project, I’ll design a model that analyzes factors such as vegetation dryness, density, and other fire-risk indicators using satellite and drone imagery–as well as relevant quantitative data. This project is a proof of concept, with the final product showcasing a website where users can select certain Northern California forest areas to determine forest fire proneness. By combining high-resolution data from drones with large-scale satellite imagery, my aim is to demonstrate the possibility of an accessible, modular system that can be integrated seamlessly with commercial drones. I hope to enable local communities, park rangers, and environmental agencies to better assess and manage wildfire risks.
Project Blog
Week 0: Blog
Hello! Welcome to my senior project blog. Today I wanted to talk a little bit about my backstory, which I hope gives some insight into why I decided to do this project.
My interest in working on engineering projects to benefit the environment began in 8th grade. One of my close friends invited me to a high school nonprofit called Beyond Terra, where we first worked on a project called the seedball. Through a series of experiments, we determined an optimal formula for the seedballs to maximize the health and vitality of the seeds inside. We actually generated a significant amount of interest, both from the press and potential customers, but our next question was, where do we go from there? We had a few ideas for engineering projects, such as a drain sock, but none proved feasible to implement with our resources.
Four years of thinking finally led to a new idea—a drone that could assess an area’s proneness to forest fires. Sources like DroneLife and MITSolve show that drones are currently focused on early detection of fires that have already been started, as well as combating fires. I detected a gap in that many of these drones area focused on mitigation rather than prevention, one I intend to bridge.
In addition, I’ve loved working with autonomous vehicles and image detection algorithms throughout high school. On my robotics team, I used detection models such as TensorFlow Object Detection, and built-in cameras such as HuskyLens to solve randomization tasks that required shape or color input. The past summer at MIT I worked with autonomous underwater vehicles, using edge detection of the lines at the bottom of the pool in order to navigate pool currents and enable my vehicle to traverse the pool in a straight line. This required a different type of camera orientation, where the camera had an upward perspective on the features of interest, which is similar to what a drone or satellite camera would experience when hovering overa tree canopy. Having immersed myself into land and water vehicles, it would seem obvious that the logical next step is drones and aerial imagery!
So now…about my project…
Through this project, I’ll design a model that analyzes factors such as vegetation dryness, density, and other fire-risk indicators using satellite and drone imagery–as well as relevant quantitative data. This project is a proof of concept, with the final product showcasing a website where users can select certain Northern California forest areas to determine forest fire proneness. The platform will provide visualized risk levels, offering a valuable tool for early detection and mitigation planning.
I hope to explore the possibility of preemptive wildfire prevention on a large scale. By combining high-resolution data from drones with large-scale satellite imagery, my aim is to demonstrate an accessible, modular system that can be integrated seamlessly with commercial drones. I hope this system will empower local communities, park rangers, and environmental agencies with precise, data-driven insights to better predict, assess, and mitigate wildfire threats before they escalate. In the long run, this project could help bridge the gap between cutting-edge technology and practical wildfire prevention efforts–as well as teach me about the intersection of the two.
Week 1: Preliminary Analysis
Welcome back to another edition of my blog…
This week, I’ve been completing the first (and most important step) of my senior project—determining exactly what factors contribute to an area being “prone to forest fires.” From my extensive research (which I won’t cite here but which is included in my syllabus texts), I realized that climate & weather conditions, vegetation, topography, and ignition events (such as lightning strikes) are the most glaring and obvious factors.
However, during my research I came across a study which highlighted that high risk areas weren’t solely due to natural factors—there are the inherent human risks, such as in areas of high population density. While this is less of a risk in the areas I plan to analyze, it may be something important to note in the future. To determine which factors are of high significance, the researchers built a logistic regression model incorporating socio-economic and environmental variables. While I don’t plan on focusing solely on human-caused fires, I may perform a similar test to determine the most relevant factors next week.
On Thursday, I stumbled upon an article by NASA, which dealt with a topic that wasn’t in my syllabus’s plan for this week but nonetheless very revealing. I learned about how the MODIS satellite/dataset is being used to determine dry regions in Africa. However, according to the article, MODIS struggles in African drylands because trees are so spaced out, making it much harder to analyze a canopy (since there wouldn’t truly be a canopy). As a result extreme dryness is often overlooked by satellite imaging—this is something that I’ll definitely keep in mind when defining the scope of my project and beginning to gather data on Bay Area-specific regions.
I also completed some bookkeeping tasks, such as creating a GitHub repository for my project. I spoke to a local robotics team about their image detection software—trying to use preexisting pipelines to make my job easier for me. And on Tuesday, I spoke with a UCLA undergraduate who has been involved with climate-tech organizations and is especially familiar with the combination of drone imaging and reforestation from his organization’s work.
Overall, I’m happy with my progress this week, as I dove into the environmental science behind my project. At some point along the way, I realized that I’ve never taken an environmental science class, so I would say this past week has been both necessary and fulfilling.
See you next week!
Week 2: Gathering Data
Welcome again to my blog! If you’re reading this, I’m assuming you’re not bored yet! This week was filled with a thrilling ordeal: gathering data.
On Monday, Tuesday, and Wednesday, I compiled historical satellite images spanning ten years before and after certain historical California fires, accessing MODIS-derived fire detection points, Sentinel-2 imagery, and Landsat 8 NDVI trends from USGS Earth Explorer. It wasn’t easy familiarizing myself with the interfaces for these datasets, but after an hour of painstakingly scrolling through forums, I was able to extract the data that I needed. While I wouldn’t say my data collection is completely finalized, I definitely achieved what I set out to do this week.
With my image data mostly compiled, I met with a Beyond Terra member to discuss methodologies for feature extraction. Back in 2021, we worked on a project rating cities based on how closely they follow IPCC standards, so I asked the leader of that project—who had experience scrolling through NASA datasets—to help me look for my numerical data. In addition, Ms. Shahin had the idea of adding the locations of PG&E power lines as a feature for my model, so I spent a short while looking for an appropriate dataset.
After collecting my numerical data, I realized that my image data will need to contain a good amount of areas where the risk of fire is relatively minimal, so I’ll make sure to go back and download image data from areas in California that haven’t been victim to fires in the past forty years.
Moving forward, I plan to preprocess and analyze this dataset to identify trends in wildfire-prone areas and refine the selection of predictive variables. I’ll do some preliminary analysis using regression models to determine which variables/features I can drop before moving on to my next step: researching my model.
Week 3: Model Frankenstein
Welcome again, loyal followers, to another edition of my blog. Hope everyone is doing great!
This week I delved into the more technical side of my project—deciding exactly which type of model to use for my purposes. This was no easy decision because my data is unlike anything I’ve worked with before, with a unique combination of both image and numerical data. After consulting with my advisors, I found that Python packages like Keras have a built-in method of including different forms of data (or what’s referred to as “mixed data”).
In addition, if I’d like to explore a manual implementation, I just need a stack of convolutions to process the images, which I can flatten to a vector and concatenate with numerical features (this is an easy solution I found on an online forum).
But this week my project did improve drastically in terms of the clarity of what I would like to accomplish. Essentially, what I’ll be doing is looking at five years of image/numerical fire-related data. In addition to the data I collected last week, I added a dataset that geographically details the coordinates of all fires within the regions I’m considering over this span. Using this dataset, I’ll see if my model can correctly classify if a fire will happen in the next month, then the next six months, then the next year, then the next two years—to see what the bounds on this type of analysis are. So you may be wondering, wasn’t your model supposed to output a fire proneness? My model will be a binary classification model, but in terms of my final product, it will output the model’s prediction without the last layer (so a range from 0-1) which I will term to be the fire proneness.
Next week, I’ll look into image preprocessing, such as removing noise and increasing contrast. See you then!
Week 4: Data, data, and then some more data
Hello again, and welcome to week 4 of my senior project! This week I got into the technical, more complicated but less research-y side of my senior project…
As I mentioned in the previous week’s post, this week I primarily focused on data preprocessing, which although sometimes tedious can also be fulfilling. Along the way, I gained a better understanding of which of my data was usable and which wasn’t. For example, for the Earth Explorer Dataset, around 20% of the data was obscured under cloud cover and simply unusable. As a result, I had to use an extra parameter when filtering data—that cloud cover is below 60%. In addition, I found that the satellite Landsat 8-9 was best suited for the time range that I’ll be looking at, along with eMODIS NDVI v6 which is specially designed for vegetation monitoring.
As for processing the actual images in the dataset, I decided to first start with a few sample images before downloading every single one. I installed a new application called ImageJ, which has built-in contrast enhancers and filters, allowing me to find a combination that will best allow my model to analyze each image. Next, I’ll write a macro which will allow me to perform the same operations on every image in the dataset.
One problem I faced was that not every satellite image of Butte County (the county I decided to look at first) has edge coordinates in the same location, so I created a Python script that automatically detects the boundaries of each image based on its metadata and adjusts the coordinates accordingly. This ensures that all images are consistently aligned before processing.
This week I also preprocessed my label data, which is essentially a historical dataset of the fire boundaries of all fires burned in the United States. This dataset looks at fires that burn over 1000 acres (approximately 1 square mile), which is perfect for my purposes. Then, using the Butte County shapefile, I narrowed the data down to all fires burned in Butte County, within the time period that I desire.
This week the progress was a bit slow, and I didn’t get to start model implementation as I had hoped. I don’t necessarily see this as a problem, however, since Senior Project is just getting started and I had set a good amount of time for hyperparameter tuning (I have since found data preprocessing to be much more time-consuming).
Next week, I’ll need to spend the first few days preprocessing my numerical data, and then I can get started on tuning my model. See you then!
Week 5: One (More) Problem
Welcome back, everyone. Disclaimer: This week was an annoying one, a tedious one, but still a productive one.
The first problem I faced was coding my model. I hadn’t actually touched Keras or Tensorflow in around a year (I decided to use Keras, for now at least), so I had to relearn it via online tutorials, which luckily wasn’t too hard. According to my external advisor, coding should be the least of my worries, which I found pretty much true.
However, if you remember, last week, I took a sample set of my image data to preprocess. But when I looked to download my entire vegetation image data set of around 1000 images, I faced a BIG problem. Each image was around 100 MB—which was strange—and when I looked into it, each “image” was actually four images, of the exact same location. They were of varying quality, with some of them pitch-black and some of them high resolution, but of no sensical order for each data point. There was only one option—manually sort through EVERY SINGLE data point, filtering for the best image for each day. But still, the dataset is huge, and my 2018 Macbook Pro can only handle so many gigs of data. So I decided to spend a little more time filtering, taking data every week over five years, for example, to reduce the size of my dataset.
While I was solving that problem, I did manage to start my web app. Having worked with Foodware as a software engineering intern, it was relatively easy to get started with my React App, creating the outline/framework of the web app with a clickable map for every square mile. I also included a time slider where the user can enter how far in the future they would like their fire proneness to be.
Next week, I hope to (finally) begin training my model. See you then!
Week 6: Training Season
¡Hola, todos! Yes, it’s time for another week of my blog (and I just realized we are more than halfway through!)
At the beginning of the week, I finally finished downloading my image data. However, my data troubles weren’t over yet. When processing my data, I needed to create a one-square-mile by one-square-mile grid—and for my numerical data, this meant calculating the average of each feature (like temperature, humidity, etc) over a one-square-mile grid. This is when I realized that the data was already being averaged over an area much larger than one square mile. Since I was looking for more precise data, I went back to the drawing board that day and looked for another dataset, finding the ERA5 Land satellite and using Google Earth Engine to write a script to find the daily average of each feature within one-square-mile grids in Butte County.
Finally, it was time to train my model. I first decided to include only my numerical data as features in the model. I began with PG&E powerline data, temperature, leaf area, and precipitation, to gain as many iterations of my model before including the images. At first, my LSTM looked at the past 30 days of data to predict fire occurrence one day ahead. I then played around with the sequence length, trying to see if my LSTM could predict one month ahead. Throughout this process, the accuracy hovered from around 40-60% accuracy, with tuning slightly improving the model’s accuracy. It’s important to remember this is without any image data. Towards the end of the week, I began incorporating the satellite images into my data, starting with data in monthly intervals. Next week, I’ll begin tuning the final model, determining how far in advance my model can accurately predict forest fire proneness.
Week 7: Bugs
Welcome back to another edition of my blog! This week, unfortunately, went a little slower than expected due to several bugs that consumed my time. However, the past week has made it clear that preprocessing the data and processing the data specifically for the model are two separate and important tasks. Lesson learned: I didn’t devote enough time to the second one.
While repeatedly running my model on the numerical data, I realized that it was highly overfitting, regardless of what I set the hyperparameters to. After hours of exploring and debugging, I realized that the grids between my features and labels didn’t even match up! The grid for the features was a 69-square-mile grid, because I forgot to convert from degrees to square miles when extracting the data from Google Earth Engine! However, this meant I had significantly more data than I thought, and to redownload the correctly gridded data, I would need to break up my exports yearly to work around the Google Earth Engine data limit (and at this point, I decided to just use data from weekly intervals). Once I figured out how to do that, I would also need to compress the data (yes, I need to be extremely conservative with the amount of storage I use, because I only have around 20GB or so left on my MacOS). After a little bit of research, I decided to use the .parquet file format in place of .csv, which significantly reduced the size for me.
The next step was even more challenging. I had to ensure the grids of the two datasets lined up again. I wasn’t careful with this step the first time around, which led to a lot of wasted time. Therefore, I switched over from the Butte County shapefile to a simple polygon, ensuring all the way that the number of squares in each grid matched up exactly.
After processing the numerical data, I was much more careful with the image data this time around, and getting it ready for the model required me to write several Python scripts. I wrote code to process these .tif files, using metadata from the .met files to accurately crop the images to the correct geographic bounds. The metadata provided key information such as the geographical coordinates of the image corners, which allowed me to map the raster data correctly onto the region of Butte County. This step involved some trial and error with the cropping function, but after some debugging, the script successfully cropped all images to the desired area, optimizing the data for further use.
I may work a little over spring break next week to try to make up for the lost time, and if all goes well, my model should be good to go at the beginning of the next week of senior project. See you in two weeks!
Week 8: Firecast
Welcome back, everyone! After a much-needed rest during spring break, I am diving into the home stretch of my project.
Now that I had collected all of my image data, I was almost ready to run my model. After writing a script to extract the NDVI values from all of the NDVI images in my folder, I created a one-square-mile by one-square-mile grid, using the resolution given in the metadata for the images. I did the same thing for my Landsat data, and after several hours of debugging, I finally got the grids to align and was ready to run my model. For the Landsat data, I made a business decision to take the average pixel value of each image over a single grid square, putting this into an array. This meant I wouldn’t need to use a CNN, which would greatly optimize my LSTM and overall streamline my tuning process and save me hours of painful debugging.
Running the LSTM was a relatively simple process. However, I needed to ensure that my model wouldn’t overestimate its accuracy due to the fact that the majority of the data points aren’t during fires. So I did two things: I oversampled the fire data, and I used a technique called SMOTE to generate synthetic samples from the minority fire class. After these safety measures, there was roughly an equal amount of both classes in my data. After a day of tuning, my model reached an accuracy of 88%. For future reference, this does not include the PG&E power line data, which I may go back to later.
Once I had saved my model, I began devoting all of my time to working on my final product: the web app. I wrote the JavaScript code for a map with a clickable one-square-mile by one-square-mile grid over Butte County. While coding the base structure was relatively simple, I have much less experience working on the backend, which will be where the web app accesses the current data for each grid square in the region and runs my model on that data, outputting fire proneness to the user. After a little research and some help, I decided to use Python on the backend (because my model runs in Python anyway).
I’m still working out a few kinks, but everything is finally shaping up. I’m excited to see where I’ll be at next week. See you then!
Week 9: hAPIness
Hello, and welcome to another edition of my blog!
This week was by far the most fulfilling, mainly because I finally got to test out a working webapp. After extensive debugging, I fixed the API network error by specifying exactly which IP the endpoints are being hosted on, both in the backend and in the frontend. I don’t know how this will affect the implementation of the web app on other computers, but that’s a problem for another time.
So, what does my prototype look like so far? Center stage is a map centered on the grid of Butte County, with thousands of square-mile grid squares shaded in blue. The user can click any of these grids, which will pop out in a small bubble the fire proneness of that grid square. On the right side of the webapp is a feature that allows users to understand where this prediction is coming from, displaying the available data for that square.
One problem I faced this week was the time it takes for the model to run after a grid square is pressed. Remember, each time this model “predicts” on data, it’s not just predicting from features at a single snapshot in time. Instead, the LSTM sequence uses the past 10 weeks of data to predict what will happen in the current week, which leads to longer processing time. Therefore, for the sake of user-friendliness, I decided to run the model preemptively and store all predictions in a CSV, which the API simply needs to access to output the predictions.
Next week, I’ll continue polishing my web app, along with adding a time slider so the user can observe what the proneness will look like during times of high fire risk. See you then!
Week 10: On Fire
So, it’s the last week. My final product has been submitted, my last timesheet has been made. So for one last time, let’s go through the race to the end that was Week 10 of my senior project.
First up was the time slider. I went back and ran my model on all data from 2013 to 2018, and loaded those predictions into my backend server instead of simply the last week. While that was relatively straightforward, the difficult part was cleaning up the layout on the frontend, such as spacing out the week labels so that they wouldn’t overlap, and adding small fire icons directly on top of the slider so the user can tell which weeks actually had fires.
Next, I implemented a heatmap on top of the actual grid over Butte County. At first, I tried using the React Heatmap component, but it looked slightly unnatural over the squares. Instead, I recolored the grids on a linear scale from green to red, experimenting until I achieved the maximum contrast that best represented the differences in fire-proneness that week. As a note, the colors are only relative in a single week, not over multiple weeks, so if the highest proneness in a single week is around 0.5, it will look redder than 0.5 in a week where 0.9 is the highest proneness.
At this point, I learned that I wouldn’t be able to bring my laptop to the showcase, so in the remaining days, I decided to try to deploy my project on Render. Unfortunately, however, the free plan on Render only supports a maximum of 512 MB of memory, and I kept hitting that limit when my server uploaded my data to pandas from CSV. Therefore, I devised code that incrementally saves my data using a cache, very slowly loading my data on the backend. After hours of decoding, I finally had a working (although slow) web app! After a few fixes, like improvising descriptions on the feature sidebar, I was finally done.
It's bittersweet to be writing this final blog post. These past ten weeks have been an incredible journey filled with challenges, breakthroughs, and countless learning experiences. I'm proud of the work accomplished and hopeful that this project can contribute to wildfire prevention efforts in the future. Thanks for following along!