Android 版 OpenCV 分水岭算法(Watershed)和洪水填充(FloodFill)详解
在 OpenCV 中,分水岭算法(Watershed) 和 洪水填充算法(FloodFill) 是两种常见的区域分割方法,常用于图像分割、物体分割 和 前景提取。
1. 分水岭算法(Watershed Algorithm)
1.1 分水岭算法原理
分水岭算法是一种基于拓扑形态学的图像分割方法,主要步骤如下:
- 计算梯度(边缘)
• 使用 cv2.Canny() 或 cv2.Sobel() 计算图像梯度(找到边界)。
- 生成标记(Markers)
• 定义前景(对象区域) 和 背景(非对象区域) 。
• 用 cv2.connectedComponents() 或手动绘制标记点。
- 应用分水岭变换(Watershed Transform)
• 让水流从标记点扩展,最终填充整个图像,形成不同区域的分割。
1.2 分水岭算法实现
步骤
-
读取图像并转换为灰度图
-
使用 GaussianBlur() 平滑图像
-
进行 Canny() 边缘检测
-
使用 dilate() 和 erode() 处理前景和背景
-
标记区域,并使用 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 洪水填充算法实现
步骤
-
读取图像并转换为灰度图
-
选择一个起始种子点
-
使用 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 进行更精确的分割和填充。