...

YOLOv8 Segmentation Tutorial for Real Flood Detection

flood segmentation

Last Updated on 08/11/2025 by Eran Feit

How YOLOv8 Flood Segmentation Helps You Map Real Floods

YOLOv8 flood segmentation gives you more than bounding boxes.
It delivers precise pixel-level masks that outline where floodwater actually appears, turning raw satellite or aerial imagery into clear, data-driven flood maps.

In this tutorial, you’ll use YOLOv8 flood segmentation to build a focused one-class model that separates flooded areas from the background with clean labels and a practical configuration.
You’ll see how to convert binary masks into YOLO-ready annotations, structure your dataset correctly, train a segmentation model on real flood data, and generate high-quality masks you can evaluate, visualize, or plug into your own analytics pipeline.

The entire workflow is written in readable Python, designed so you can reuse the same pattern for future disaster mapping, environmental monitoring, or any scenario where fast, accurate segmentation really matters.

Understanding YOLOv8 Segmentation for Real Flood Mapping

YOLOv8 isn’t just a detection network.
It also offers a powerful segmentation head that lets you generate pixel-level masks in real time, which is exactly what we need for fast, actionable flood mapping.
In this yolov8 segmentation tutorial, you’ll walk through a complete, production-style pipeline: preparing a flood dataset, converting binary masks into YOLO polygon labels, splitting the data, wiring a clean config.yaml, training a one-class YOLOv8-seg model, and finally generating high-quality flood masks on new images.
Everything is written in practical Python, kept readable, and structured so you can adapt it to any other water, damage, or environmental segmentation task without rewriting your entire stack.

By the end, you’ll know not just how to run the code, but why each step exists—so you can trust your model in real-world scenarios like risk analysis, monitoring, or decision support.

You can find more tutorials in my blog : https://eranfeit.net/blog/

Link to the dataset : https://www.kaggle.com/datasets/faizalkarim/flood-area-segmentation

Link for the post in Medium : https://medium.com/@feitgemel/yolov8-segmentation-tutorial-for-real-flood-detection-963f0aaca0c3

 Want to get started with Computer Vision or take your skills to the next level ?

If you’re just beginning, I recommend this step-by-step course designed to introduce you to the foundations of Computer Vision – Complete Computer Vision Bootcamp With PyTorch & TensorFlow

If you’re already experienced and looking for more advanced techniques, check out this deep-dive course – Modern Computer Vision GPT, PyTorch, Keras, OpenCV4


Part 1 – From Environment Setup to YOLO Polygon Labels (Masks → YOLO Format)

In this first part, you prepare a clean environment for YOLOv8 segmentation and convert the Flood Area Segmentation masks into YOLO-compatible polygon labels.
The goal is simple: turn binary flood masks into training-ready .txt files, so YOLOv8-seg can learn where flooded regions appear at pixel level.
This step is crucial for any serious segmentation workflow—if labels are messy, your model will be too.
Here we make the process transparent, reusable, and aligned with a single one-class setup (flood).

Detailed explanation (Part 1)

You start by isolating the experiment in its own Conda environment to avoid dependency conflicts.
Then you install PyTorch with CUDA support, Ultralytics YOLOv8, and OpenCV—these are the core tools behind efficient segmentation pipelines.
Next, you loop over all binary masks, clean them with thresholding, extract contours, and convert each contour into normalized polygon coordinates in YOLO format.
Each output .txt file corresponds to an image and contains polygon points describing flooded regions as class 0 (your single flood class), ready for YOLOv8-seg.

### Create a dedicated Conda environment with Python 3.8 to keep this YOLOv8 segmentation tutorial isolated and stable. conda create --name YoloV8 python=3.8  ### Activate the new environment so all following installs and runs stay clean. conda activate YoloV8  ### (Optional but recommended) Check that CUDA is installed correctly on your system. nvcc --version  ### Install PyTorch with CUDA 11.8 support from the official channels for reliable GPU acceleration. conda install pytorch==2.1.1 torchvision==0.16.1 torchaudio==2.1.1 pytorch-cuda=11.8 -c pytorch -c nvidia  ### Install the specific Ultralytics YOLOv8 version used in this tutorial for consistent behavior. pip install ultralytics==8.1.0  ### Remove conflicting OpenCV headless builds to avoid import and display issues. pip uninstall opencv-python-headless -y  ### Uninstall any previous OpenCV to ensure a clean reinstall. pip uninstall opencv-python -y  ### Install a compatible OpenCV version with GUI support for visualization. pip install opencv-python>=4.6.0 

Transforming Flood Masks into YOLOv8-Ready Segmentation Labels

In this step, we turn raw binary masks into training-ready labels that YOLOv8 can actually understand for yolov8 flood segmentation.
Each mask image highlights flooded areas in white and everything else in black, but YOLOv8 needs those regions described as normalized polygon coordinates in a .txt file.

The script below automates this entire process: it scans your mask directory, cleans each mask, finds the contours of the flooded regions, filters out tiny noise, normalizes all points to the image size, and writes them into YOLO-style polygon annotations for a single flood class.
Once this conversion is done, your dataset is no longer just a collection of images and masks—it becomes a structured, YOLOv8-compatible flood segmentation dataset ready for training.

### Import core libraries used for file handling and image processing. import os import cv2  ### Define the input directory containing binary mask images. input_dir = 'C:/Data-sets/Flood Area Segmentation/Mask'  ### Define the output directory where YOLO polygon label files will be saved. output_dir = 'C:/Data-sets/Flood Area Segmentation/labels'  ### Create the labels directory if it does not already exist. if not os.path.exists(output_dir):     os.makedirs(output_dir)  ### Iterate over every mask file in the input directory. for j in os.listdir(input_dir):     ### Build the full path to the current mask image.     image_path = os.path.join(input_dir, j)     ### Load the binary mask in grayscale so we can process intensities directly.     mask = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)     ### Threshold the mask so any non-zero pixel becomes pure white (255).     _, mask = cv2.threshold(mask, 1, 255, cv2.THRESH_BINARY)      ### Read the mask height and width for later normalization.     H, W = mask.shape     ### Find outer contours of the flooded regions.     contours, hierarchy = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)      ### Prepare a list to hold polygons for this mask.     polygons = []     ### Loop over each detected contour.     for cnt in contours:         ### Filter out tiny noisy blobs by minimum area.         if cv2.contourArea(cnt) > 200:             ### Initialize a polygon list for this contour.             polygon = []             ### Loop through every point in the contour.             for point in cnt:                 ### Extract x, y pixel coordinates.                 x, y = point[0]                 ### Normalize x and y to [0,1] by dividing by width and height.                 polygon.append(x / W)                 polygon.append(y / H)             ### Store this polygon for later writing.             polygons.append(polygon)      ### Open a corresponding .txt file in the labels directory for this mask.     with open('{}.txt'.format(os.path.join(output_dir, j)[:-4]), 'w') as f:         ### Loop through each polygon and write it in YOLO segmentation format.         for polygon in polygons:             for p_, p in enumerate(polygon):                 ### First value is the class index (0 for 'flood') followed by polygon coordinates.                 if p_ == len(polygon) - 1:                     f.write('{}\n'.format(p))                 elif p_ == 0:                     f.write('0 {} '.format(p))                 else:                     f.write('{} '.format(p)) 

YOLO Format Sample (add this explanation right after Part 1 code):

0 0.5003125 0.4148148148148148 0.235625 0.5898148148148148 1 0.7546875 0.36574074074074076 0.11875 0.2814814814814815 
  • 0 / 1 are class indices (0-based).
  • Values are normalized: x_center y_center width height relative to image size.
  • For example, class 0 might be “person” at ~50%/41% of the image with given width/height; class 1 might be “car” around 75%/36%.
  • In this tutorial, you use a one-class setting (0 = flood) and polygons instead of simple boxes, but the idea of normalized coordinates is the same and helps YOLOv8 learn consistently across resolutions.

Part 1 Summary

You now have:

  • A clean YOLOv8 environment.
  • Binary masks converted into YOLO-style polygon labels with a single flood class.
  • A consistent foundation so training will correctly learn flooded regions instead of random shapes.

Part 2 – Splitting the Dataset and Building a Clean YOLOv8 Config

This part organizes your data into train/val splits and tells YOLOv8 where everything lives via config.yaml.
A clear structure boosts reproducibility, avoids label mismatches, and keeps your yolov8 segmentation tutorial aligned with real-world best practices.
You’ll create Train and Val folders—with images/ and labels/ inside each—and then define a one-class config pointing to flood.
Once this is locked in, you’re one command away from training.

Detailed explanation (Part 2)

The split_data function shuffles images, creates parallel folder structures for images and labels, and copies matching pairs into train or val folders using an 80/20 split.
Because segmentation relies on precise alignment, each .txt label is matched to its corresponding image by filename, so no guesswork is involved.
Then, config.yaml becomes your single source of truth: it sets the dataset root, train/val paths, number of classes, and their names.
This pattern works not only for flood segmentation but any custom one-class or multi-class segmentation dataset you build.

### Import required modules for file operations, shuffling, and copying. import os import random import shutil  ### Define a helper function to split images and labels into train and validation sets. def split_data(source, destination_train, destination_val, split_ratio):     ### Create train and validation root directories if needed.     if not os.path.exists(destination_train):         os.makedirs(destination_train)     if not os.path.exists(destination_val):         os.makedirs(destination_val)          ### Inside each split, create images and labels subdirectories.     train_image_dir = os.path.join(destination_train, "images")     train_label_dir = os.path.join(destination_train, "labels")     val_image_dir = os.path.join(destination_val, "images")     val_label_dir = os.path.join(destination_val, "labels")      if not os.path.exists(train_image_dir):         os.makedirs(train_image_dir)     if not os.path.exists(train_label_dir):         os.makedirs(train_label_dir)     if not os.path.exists(val_image_dir):         os.makedirs(val_image_dir)     if not os.path.exists(val_label_dir):         os.makedirs(val_label_dir)          ### Read all image filenames from the 'image' folder under the source directory.     images = os.listdir(os.path.join(source, "image"))     ### Shuffle the list to ensure a random and unbiased split.     random.shuffle(images)          ### Compute the index where training set ends and validation set begins.     split_index = int(len(images) * split_ratio)          ### Split into training and validation subsets.     train_images = images[:split_index]     val_images = images[split_index:]          ### Copy train images and labels.     print("Copying files to train folder:")     for image_name in train_images:         ### Build full source and destination paths for each image.         shutil.copy(os.path.join(source, "image", image_name), train_image_dir)         print(f"Copied {image_name} to {train_image_dir}")         ### Build matching label name (.txt) and copy it.         label_name = os.path.splitext(image_name)[0] + ".txt"         shutil.copy(os.path.join(source, "labels", label_name), train_label_dir)         print(f"Copied {label_name} to {train_label_dir}")          ### Copy validation images and labels.     print("Copying files to val folder:")     for image_name in val_images:         shutil.copy(os.path.join(source, "image", image_name), val_image_dir)         print(f"Copied {image_name} to {val_image_dir}")         label_name = os.path.splitext(image_name)[0] + ".txt"         shutil.copy(os.path.join(source, "labels", label_name), val_label_dir)         print(f"Copied {label_name} to {val_label_dir}")  ### Define the dataset root directory that holds 'image' and 'labels'. source_dir = "C:/Data-sets/Flood Area Segmentation"  ### Define target directories for training and validation splits. destination_train = os.path.join(source_dir, "Train") destination_val = os.path.join(source_dir, "Val")  ### Perform an 80/20 split of the data for training and validation. split_data(source_dir, destination_train, destination_val, split_ratio=0.8) 

Now create a config.yaml file in the same dataset directory with this content:

Now create a config.yaml file in the same dataset directory with this content:

This tells YOLOv8 where your images are and defines a single ‘flood’ class

path: 'C:/Data-sets/Flood Area Segmentation' train: 'train/images' val: 'val/images'  nc: 1 names: ['flood'] 

Part 2 Summary :

You now have:

  • Train/Val folders with synced images and labels.
  • A simple, readable config.yaml that describes a one-class flood segmentation task.
  • A dataset layout fully compatible with YOLOv8-seg training and easy to reuse on other machines.

Part 3 – Training YOLOv8-seg and Predicting High-Quality Flood Masks

Now you connect everything: the pre-trained YOLOv8-seg model, your config.yaml, and the organized dataset.
You fine-tune yolov8l-seg.pt for a single flood class, track results in a dedicated project folder, and apply the trained model to a new test image.
You also merge all predicted instance masks into a single binary flood mask for fast visualization and export.
This completes a realistic yolov8 segmentation tutorial pipeline from raw masks to deployable flood inference.

Detailed explanation (Part 3)

The training script loads yolov8l-seg.pt as a strong starting point and runs 100 epochs or early-stops via patience, logging under My-Flood-Model.
Parameters like imgsz, batch, and device give you direct control over performance without complicating the core idea.
The prediction script then loads best.pt, runs inference on a single flood image, extracts class IDs and mask tensors, accumulates them into a final mask, and saves both per-object masks and a combined map.
Resized previews help you quickly debug results without needing huge windows or heavy dashboards.

### Import the YOLO class from Ultralytics for training and inference. from ultralytics import YOLO  ### Define the main training routine to fine-tune YOLOv8-seg on the flood dataset. def main():     ### Load the pretrained YOLOv8 large segmentation model as a starting point.     model = YOLO('yolov8l-seg.pt')     ### Set the root project directory for logs, runs, and weights.     project = "c:/Data-sets/Flood Area Segmentation"     ### Name this training experiment for easy tracking.     experiment = "My-Flood-Model"     ### Choose a batch size that fits your GPU memory.     batch_size = 16      ### Point to the dataset configuration file you created earlier.     config_file = "Best-Semantic-Segmentation-models/Yolo-V8/Segment-One-Class-Flood-Segmentation/config.yaml"      ### Launch the training process with key hyperparameters.     results = model.train(         data=config_file,         epochs=100,         project=project,         name=experiment,         batch=batch_size,         device=0,         imgsz=640,         patience=30,         verbose=True,         val=True     )  ### Run training only when this script is executed directly. if __name__ == "__main__":     main() 

Putting YOLOv8 Flood Segmentation to the Test

Now that the model is trained, it’s time to see how well YOLOv8 flood segmentation performs on a real image.
In this testing step, we load the best weights, run inference on a sample flood scene, and generate precise masks that highlight only the flooded regions.

The script below does a few important things for you automatically: it applies the trained model to a new image, collects all predicted masks, merges them into a clean final flood mask, saves each output to disk, and shows a side-by-side visualization of the original image and the detected flooded areas.
This is where your pipeline becomes practical—you move from training logs and configs to clear visual evidence that your YOLOv8 flood model can map real flood extent in a way that is easy to validate, explain, and integrate into your own workflows.

Here is the test image :

Flood segmentation
Flood segmentation
### Import YOLO for inference and supporting libraries for arrays and visualization. from ultralytics import YOLO import numpy as np import cv2  ### Define the trained model weights path (update if your path differs). model_path = "C:/Data-sets/Flood Area Segmentation/My-Flood-Model/weights/best.pt"  ### Define the input image on which you want to predict flood masks. image_path = "Best-Semantic-Segmentation-models/Yolo-V8/Segment-One-Class-Flood-Segmentation/test-flood.jpg"  ### Read the input image in BGR format using OpenCV. img = cv2.imread(image_path) ### Extract image dimensions for resizing masks later. H, W, _ = img.shape  ### Load the trained YOLOv8 segmentation model. model = YOLO(model_path)  ### Run prediction on the loaded image to get segmentation outputs. results = model(img) ### Take the first (and in this case, only) result from the list. result = results[0]  ### Access the class name mapping for readability. names = model.names print(names)  ### Initialize an empty mask to accumulate all predicted flood regions. final_mask = np.zeros((H, W), dtype=np.uint8)  ### Extract predicted class indices from bounding boxes. predicted_classes = result.boxes.cls.cpu().numpy() print(predicted_classes)  ### Loop over each predicted mask and its corresponding class. for j, mask in enumerate(result.masks.data):     ### Convert the mask tensor to a NumPy array and scale to [0,255].     mask = mask.cpu().numpy() * 255     ### Get the class id for this mask.     classId = int(predicted_classes[j])      ### Print a short log line to understand which class was detected.     print("Object " + str(j) + " detected as " + str(classId) + " - " + names[classId])      ### Resize mask to match the original image resolution.     mask = cv2.resize(mask, (W, H))      ### Accumulate the masks so any flooded pixel across instances is included.     final_mask = np.maximum(final_mask, mask)      ### Save each individual mask for inspection and debugging.     file_name = "output" + str(j) + ".png"     cv2.imwrite("C:/Data-sets/Flood Area Segmentation/My-Flood-Model/" + file_name, mask)  ### Save the final combined flood mask image. cv2.imwrite("C:/Data-sets/Flood Area Segmentation/My-Flood-Model/final_mask.png", final_mask)  ### Optionally, resize the original image and final mask for easier side-by-side viewing. scale_precent = 30 width = int(img.shape[1] * scale_precent / 100) height = int(img.shape[0] * scale_precent / 100) dim = (width, height)  ### Resize the original image and mask to the new dimensions. resized = cv2.resize(img, dim, interpolation=cv2.INTER_AREA) resized_mask = cv2.resize(final_mask, dim, interpolation=cv2.INTER_AREA)  ### Display the original image and predicted flood mask in separate windows. cv2.imshow("img", resized) cv2.imshow("final mask", resized_mask) cv2.waitKey(0) 

Here is the Flood segmentation result :

result
YOLOv8 Segmentation Tutorial for Real Flood Detection 5

Part 3 Summary

You have:

  • Trained a YOLOv8 one-class flood segmentation model end-to-end.
  • Generated individual and merged flood masks on new images.
  • Built a reusable template for future segmentation tasks with minimal changes.

YOLOv8 Flood Segmentation Tutorial — FAQ :

What does YOLOv8 segmentation do in this flood tutorial?

It predicts pixel-level masks for flooded regions, allowing you to generate accurate flood extent maps from images.

Why train a one-class YOLOv8 model for floods?

A one-class setup focuses the model on detecting only flooded areas, improving clarity and reducing label noise.

Do I need binary masks before creating YOLO labels?

Yes, binary masks provide the shapes that are converted into YOLO polygon labels for segmentation training.

How important is the config.yaml file?

It defines dataset paths, class count, and names, ensuring YOLOv8 loads your flood data correctly.

Can I run this YOLOv8 flood model on modest hardware?

Yes, but training is faster on a GPU; inference can still run on CPU for smaller workloads.

What image size is recommended for training?

An image size of 640 is a practical choice balancing speed and segmentation quality.

How do I know if my masks and labels match correctly?

Each image must have a label file with the same base name, and polygons should align visually on sample images.

Can I extend this model beyond floods?

Yes, you can add more classes and retrain using the same pipeline for roads, buildings, damage, or vegetation.

What metrics should I monitor during training?

Track IoU, mAP, and visual quality of masks on validation images to confirm stable learning.

Is this YOLOv8 flood workflow production-ready?

It is a solid starting template; add more data, tuning, and monitoring to move it into production safely.

Conclusion

Building a robust flood segmentation model does not have to feel overwhelming.
By structuring the process into clear steps—environment setup, mask-to-YOLO conversion, train/val splitting, config definition, training, and prediction—you turn a complex research topic into a practical engineering workflow that anyone can follow.

This yolov8 segmentation tutorial shows how a one-class setup can already deliver powerful insights: clean flood masks that help visualize risk, support decision-makers, and speed up exploratory analysis on new regions or events.
Because everything is based on standard tools (Conda, PyTorch, Ultralytics YOLOv8, OpenCV), you can reproduce the results on your own machine, your team’s workstation, or a cloud GPU with minimal friction.

From here, you can extend the same pipeline to multi-class segmentation (roads, buildings, water), temporal analysis on videos, disaster-response dashboards, or integration with GIS systems.
The important part is that you now understand not only which commands to run, but also how the dataset structure, labels, and configuration work together to create a stable, high-quality YOLOv8 segmentation model.

Keep iterating, keep validating on real-world images, and this flood project becomes a strong, credible reference for your broader computer vision portfolio.


Connect :

☕ Buy me a coffee — https://ko-fi.com/eranfeit

🖥️ Email : feitgemel@gmail.com

🌐 https://eranfeit.net

🤝 Fiverr : https://www.fiverr.com/s/mB3Pbb

Enjoy,

Eran

Leave a Comment

Your email address will not be published. Required fields are marked *

Eran Feit