在Android上玩转Opencv 系列:12.基础知识 轮廓查找/联通区域查找用法,并且用bounding box 标记结果

142 阅读4分钟

在 Android 中使用 OpenCV 进行 轮廓查找(Contour Detection)和 联通区域查找(Connected Components)是图像处理中的常见任务。你可以通过这些方法检测图像中的不同区域或物体。通过结合 bounding box(边界框)来标记这些检测到的区域,可以更直观地展示分析结果。

下面是详细的步骤和代码示例,演示如何在 Android 上使用 OpenCV 查找轮廓和联通区域,并且通过绘制 bounding box 来标记检测到的区域。

步骤总结

  1. 图像预处理:通常将图像转换为灰度图像,然后应用阈值或边缘检测(如 Canny)来获取二值图像。

  2. 查找轮廓:通过 findContours 查找图像中的轮廓。

  3. 联通区域查找:通过 connectedComponents 查找图像中的连通区域。

  4. 绘制边界框:在每个检测到的轮廓或联通区域上绘制矩形边界框(bounding box)。

1. 配置 OpenCV

在 build.gradle 文件中加入 OpenCV 依赖:

implementation 'org.opencv:opencv-android:4.5.1'

确保在项目中正确配置 OpenCV。

2. 轮廓查找与联通区域查找的实现

完整代码示例

import android.graphics.Bitmap;
import android.os.Bundle;
import android.widget.ImageView;
import androidx.appcompat.app.AppCompatActivity;
import org.opencv.android.Utils;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;
import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取原始图片并转换为OpenCV Mat格式
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.sample_image);
        Mat imgMat = new Mat();
        Utils.bitmapToMat(bitmap, imgMat);

        // 将图片转换为灰度图像
        Mat grayMat = new Mat();
        Imgproc.cvtColor(imgMat, grayMat, Imgproc.COLOR_RGB2GRAY);

        // 高斯模糊去噪声
        Imgproc.GaussianBlur(grayMat, grayMat, new Size(5, 5), 1.5, 1.5);

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

        // 查找轮廓
        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();
        Imgproc.findContours(edges, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

        // 创建一个结果图像并在其上绘制边界框
        Mat resultMat = imgMat.clone();
        for (int i = 0; i < contours.size(); i++) {
            // 获取每个轮廓的外接矩形
            Rect boundingBox = Imgproc.boundingRect(contours.get(i));

            // 绘制边界框
            Imgproc.rectangle(resultMat, boundingBox.tl(), boundingBox.br(), new Scalar(0, 255, 0), 2);
        }

        // 将处理后的Mat转换为Bitmap
        Bitmap resultBitmap = Bitmap.createBitmap(resultMat.cols(), resultMat.rows(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(resultMat, resultBitmap);

        // 显示图像
        ImageView imageView = findViewById(R.id.imageView);
        imageView.setImageBitmap(resultBitmap);
    }
}

代码解释:

  1. 加载图像并转换为 Mat 格式:首先,使用 BitmapFactory.decodeResource() 从资源文件中加载图像,并使用 Utils.bitmapToMat() 将 Bitmap 转换为 OpenCV 的 Mat 类型。

  2. 灰度化和模糊:将图像转换为灰度图像,然后应用高斯模糊(GaussianBlur)来去除噪声。这样有助于提高边缘检测的效果。

  3. Canny 边缘检测:使用 Imgproc.Canny() 方法进行边缘检测,将图像中的边缘区域提取出来。

  4. 查找轮廓:通过 Imgproc.findContours() 查找图像中的轮廓。返回的轮廓是一个点集合,表示图像中的边界。

  5. 绘制边界框:通过 Imgproc.boundingRect() 获取每个轮廓的外接矩形(bounding box)。然后使用 Imgproc.rectangle() 在图像上绘制矩形,标记出每个轮廓区域。

  6. 转换并显示结果图像:将处理后的 Mat 转换为 Bitmap,然后通过 ImageView 在 Android 界面上显示。

3. 联通区域查找

使用 connectedComponents() 函数查找图像中的连通区域。这个函数返回的是图像中所有相连的区域,每个区域都有一个唯一的标签。对于每个连通区域,我们可以绘制一个 bounding box。

示例代码:联通区域查找并标记边界框

// 进行二值化处理
Mat thresholdMat = new Mat();
Imgproc.threshold(grayMat, thresholdMat, 100, 255, Imgproc.THRESH_BINARY);

// 存储连通区域的标签
Mat labels = new Mat();
int numComponents = Imgproc.connectedComponents(thresholdMat, labels);

// 绘制连通区域的边界框
Mat resultMat = imgMat.clone();
for (int i = 0; i < numComponents; i++) {
    // 获取每个连通区域的边界框
    Mat componentMask = labels.clone();
    Core.inRange(componentMask, new Scalar(i), new Scalar(i), componentMask);

    // 查找每个连通区域的外接矩形
    List<MatOfPoint> contourList = new ArrayList<>();
    Imgproc.findContours(componentMask, contourList, new Mat(), Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

    for (MatOfPoint contour : contourList) {
        Rect boundingBox = Imgproc.boundingRect(contour);
        Imgproc.rectangle(resultMat, boundingBox.tl(), boundingBox.br(), new Scalar(0, 255, 0), 2);
    }
}

// 将处理后的Mat转换为Bitmap
Bitmap resultBitmap = Bitmap.createBitmap(resultMat.cols(), resultMat.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(resultMat, resultBitmap);

// 显示图像
ImageView imageView = findViewById(R.id.imageView);
imageView.setImageBitmap(resultBitmap);

代码解释:

  1. 二值化图像:使用阈值处理将图像转换为二值图像。这样可以清晰地将前景和背景分开,为联通区域查找做准备。

  2. connectedComponents() :该函数用于查找图像中的所有连通区域,并返回一个标签图(labels)。每个连通区域都有一个唯一的标签值。

  3. 查找每个连通区域的轮廓:通过 Imgproc.findContours() 查找每个连通区域的轮廓。

  4. 绘制边界框:通过 Imgproc.boundingRect() 获取每个连通区域的外接矩形,然后在图像上绘制矩形,标记出每个连通区域。

4. 总结

轮廓查找:通过 findContours 查找图像中的物体轮廓,常用于形状分析、物体检测等任务。轮廓通常是物体的边界,可以通过 boundingRect 获取每个轮廓的边界框。

联通区域查找:通过 connectedComponents 查找图像中的连通区域,适用于图像分割、区域标记等任务。每个连通区域都可以用 boundingRect 绘制外接矩形。

通过这些方法,你可以在 Android 上进行边缘检测、轮廓查找和联通区域查找,并通过绘制 bounding box 标记检测结果。