在 Android 中使用 OpenCV 进行 轮廓查找(Contour Detection)和 联通区域查找(Connected Components)是图像处理中的常见任务。你可以通过这些方法检测图像中的不同区域或物体。通过结合 bounding box(边界框)来标记这些检测到的区域,可以更直观地展示分析结果。
下面是详细的步骤和代码示例,演示如何在 Android 上使用 OpenCV 查找轮廓和联通区域,并且通过绘制 bounding box 来标记检测到的区域。
步骤总结
-
图像预处理:通常将图像转换为灰度图像,然后应用阈值或边缘检测(如 Canny)来获取二值图像。
-
查找轮廓:通过 findContours 查找图像中的轮廓。
-
联通区域查找:通过 connectedComponents 查找图像中的连通区域。
-
绘制边界框:在每个检测到的轮廓或联通区域上绘制矩形边界框(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);
}
}
代码解释:
-
加载图像并转换为 Mat 格式:首先,使用 BitmapFactory.decodeResource() 从资源文件中加载图像,并使用 Utils.bitmapToMat() 将 Bitmap 转换为 OpenCV 的 Mat 类型。
-
灰度化和模糊:将图像转换为灰度图像,然后应用高斯模糊(GaussianBlur)来去除噪声。这样有助于提高边缘检测的效果。
-
Canny 边缘检测:使用 Imgproc.Canny() 方法进行边缘检测,将图像中的边缘区域提取出来。
-
查找轮廓:通过 Imgproc.findContours() 查找图像中的轮廓。返回的轮廓是一个点集合,表示图像中的边界。
-
绘制边界框:通过 Imgproc.boundingRect() 获取每个轮廓的外接矩形(bounding box)。然后使用 Imgproc.rectangle() 在图像上绘制矩形,标记出每个轮廓区域。
-
转换并显示结果图像:将处理后的 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);
代码解释:
-
二值化图像:使用阈值处理将图像转换为二值图像。这样可以清晰地将前景和背景分开,为联通区域查找做准备。
-
connectedComponents() :该函数用于查找图像中的所有连通区域,并返回一个标签图(labels)。每个连通区域都有一个唯一的标签值。
-
查找每个连通区域的轮廓:通过 Imgproc.findContours() 查找每个连通区域的轮廓。
-
绘制边界框:通过 Imgproc.boundingRect() 获取每个连通区域的外接矩形,然后在图像上绘制矩形,标记出每个连通区域。
4. 总结
• 轮廓查找:通过 findContours 查找图像中的物体轮廓,常用于形状分析、物体检测等任务。轮廓通常是物体的边界,可以通过 boundingRect 获取每个轮廓的边界框。
• 联通区域查找:通过 connectedComponents 查找图像中的连通区域,适用于图像分割、区域标记等任务。每个连通区域都可以用 boundingRect 绘制外接矩形。
通过这些方法,你可以在 Android 上进行边缘检测、轮廓查找和联通区域查找,并通过绘制 bounding box 标记检测结果。