在Android上玩转Opencv 系列:21.基础知识 分水岭算法(Watershed)和洪水填充(FloodFill)

270 阅读3分钟

Android 版 OpenCV 分水岭算法(Watershed)和洪水填充(FloodFill)详解

在 OpenCV 中,分水岭算法(Watershed)洪水填充算法(FloodFill) 是两种常见的区域分割方法,常用于图像分割物体分割前景提取

1. 分水岭算法(Watershed Algorithm)

1.1 分水岭算法原理

分水岭算法是一种基于拓扑形态学的图像分割方法,主要步骤如下:

  1. 计算梯度(边缘)

• 使用 cv2.Canny() 或 cv2.Sobel() 计算图像梯度(找到边界)。

  1. 生成标记(Markers)

• 定义前景(对象区域)背景(非对象区域)

• 用 cv2.connectedComponents() 或手动绘制标记点。

  1. 应用分水岭变换(Watershed Transform)

• 让水流从标记点扩展,最终填充整个图像,形成不同区域的分割。

1.2 分水岭算法实现

步骤

  1. 读取图像并转换为灰度图

  2. 使用 GaussianBlur() 平滑图像

  3. 进行 Canny() 边缘检测

  4. 使用 dilate() 和 erode() 处理前景和背景

  5. 标记区域,并使用 watershed() 进行分割

代码示例

import org.opencv.android.Utils;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import android.graphics.Bitmap;

public class WatershedSegmentation {
    public static Bitmap applyWatershed(Bitmap inputBitmap) {
        // 转换 Bitmap 为 Mat
        Mat srcMat = new Mat();
        Utils.bitmapToMat(inputBitmap, srcMat);

        // 转换为灰度图
        Mat grayMat = new Mat();
        Imgproc.cvtColor(srcMat, grayMat, Imgproc.COLOR_BGR2GRAY);

        // 高斯模糊降噪
        Imgproc.GaussianBlur(grayMat, grayMat, new Size(5, 5), 0);

        // 使用 Canny 进行边缘检测
        Mat edges = new Mat();
        Imgproc.Canny(grayMat, edges, 100, 200);

        // 形态学操作(膨胀和腐蚀)来修正边界
        Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(3, 3));
        Imgproc.dilate(edges, edges, kernel);
        Imgproc.erode(edges, edges, kernel);

        // 寻找连通区域(标记前景和背景)
        Mat markers = new Mat();
        Imgproc.connectedComponents(edges, markers);

        // 应用分水岭算法
        Imgproc.watershed(srcMat, markers);

        // 在原图上绘制分割边界(红色)
        for (int i = 0; i < markers.rows(); i++) {
            for (int j = 0; j < markers.cols(); j++) {
                if (markers.get(i, j)[0] == -1) {
                    srcMat.put(i, j, new double[]{0, 0, 255}); // 标记分割边界为红色
                }
            }
        }

        // 转换回 Bitmap
        Bitmap outputBitmap = Bitmap.createBitmap(srcMat.cols(), srcMat.rows(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(srcMat, outputBitmap);

        return outputBitmap;
    }
}

应用场景

• 物体分割(如细胞分割、车牌分割)

• 车道检测

• 复杂背景下的前景提取

2. 洪水填充算法(Flood Fill)

2.1 洪水填充原理

洪水填充(Flood Fill) 类似于 Photoshop 里的“油漆桶”工具,能够填充相似颜色区域。

基本概念

起点(Seed Point) : 从哪个像素点开始填充

填充颜色(New Color) : 替换旧颜色的颜色

容差(Tolerance) : 允许多少颜色变化范围

掩码(Mask) : 限制填充范围,避免溢出

2.2 洪水填充算法实现

步骤

  1. 读取图像并转换为灰度图

  2. 选择一个起始种子点

  3. 使用 floodFill() 填充区域

代码示例

import org.opencv.android.Utils;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import android.graphics.Bitmap;

public class FloodFillAlgorithm {
    public static Bitmap applyFloodFill(Bitmap inputBitmap, Point seedPoint, Scalar fillColor) {
        // 转换 Bitmap 为 Mat
        Mat srcMat = new Mat();
        Utils.bitmapToMat(inputBitmap, srcMat);

        // 创建掩码
        Mat mask = new Mat(srcMat.rows() + 2, srcMat.cols() + 2, CvType.CV_8UC1, new Scalar(0));

        // 设定填充参数
        Scalar newColor = fillColor; // 替换颜色
        Scalar loDiff = new Scalar(10, 10, 10); // 颜色变化下界
        Scalar upDiff = new Scalar(10, 10, 10); // 颜色变化上界

        // 进行洪水填充
        Imgproc.floodFill(srcMat, mask, seedPoint, newColor, new Rect(), loDiff, upDiff, Imgproc.FLOODFILL_FIXED_RANGE);

        // 转换回 Bitmap
        Bitmap outputBitmap = Bitmap.createBitmap(srcMat.cols(), srcMat.rows(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(srcMat, outputBitmap);

        return outputBitmap;
    }
}

应用场景

背景填充(如更换背景颜色)

区域分割(填充特定区域)

形状检测(找到连通区域)

3. 分水岭 vs 洪水填充

分水岭算法(Watershed)洪水填充(FloodFill)
适用场景复杂对象分割颜色填充
需要的输入需要梯度或边缘信息需要种子点
计算复杂度较高较低
优点适合分割复杂形状速度快,易用

4. 总结

分水岭算法 适用于复杂的 图像分割,如车道检测、医学图像处理等。

洪水填充 适用于 区域填充,如背景替换、形状检测等。

• 在 Android OpenCV 中,可以结合 Canny()Morphology 进行更精确的分割和填充。