Android 版本 OpenCV 图像侦差法(Image Difference Detection)的使用
图像侦差法(Image Difference)主要用于运动检测、变化检测、物体检测等。它通过计算两幅图像之间的像素差异,识别发生变化的区域。OpenCV 提供了多种方法来实现图像侦差。
1. 主要方法
(1) 直接像素差分法
• 适用于两张静态图像对比,计算像素级差异。
• 使用 Core.absdiff() 计算两张图像的绝对差分。
• 适用于静态背景和变化区域明显的情况。
(2) 帧间差分法
• 适用于视频流,通过相邻帧比较检测运动物体。
• 适用于摄像头监控、运动检测等应用。
(3) 背景建模(Background Subtraction)
• 适用于动态场景,用于去除背景,仅保留前景运动目标。
• OpenCV 提供了 BackgroundSubtractorMOG2 和 BackgroundSubtractorKNN 方法。
2. 代码实现
方法1:直接像素差分法
适用于两张静态图像的对比。
实现步骤
-
读取两张图像,并转换为灰度图。
-
使用 Core.absdiff() 计算像素级差异。
-
进行二值化处理,增强对比度。
-
找到差异区域并进行轮廓检测。
import org.opencv.android.Utils;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import android.graphics.Bitmap;
public class ImageDifference {
public static Bitmap detectDifference(Bitmap img1, Bitmap img2) {
// 转换 Bitmap 为 Mat
Mat mat1 = new Mat();
Mat mat2 = new Mat();
Utils.bitmapToMat(img1, mat1);
Utils.bitmapToMat(img2, mat2);
// 转换为灰度图
Imgproc.cvtColor(mat1, mat1, Imgproc.COLOR_BGR2GRAY);
Imgproc.cvtColor(mat2, mat2, Imgproc.COLOR_BGR2GRAY);
// 计算绝对差分
Mat diff = new Mat();
Core.absdiff(mat1, mat2, diff);
// 进行二值化处理,增强差异区域
Imgproc.threshold(diff, diff, 25, 255, Imgproc.THRESH_BINARY);
// 轮廓检测
Mat hierarchy = new Mat();
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(diff, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// 绘制轮廓
for (int i = 0; i < contours.size(); i++) {
Imgproc.drawContours(mat1, contours, i, new Scalar(0, 255, 0), 2);
}
// 转换回 Bitmap
Bitmap resultBitmap = Bitmap.createBitmap(mat1.cols(), mat1.rows(), Bitmap.Config.ARGB_8888);
Utils.matToBitmap(mat1, resultBitmap);
return resultBitmap;
}
}
方法2:帧间差分法(适用于视频)
用于检测视频中的运动物体。
实现步骤
-
从摄像头获取连续帧。
-
计算当前帧和上一帧之间的差异。
-
进行阈值化处理,突出变化区域。
-
通过形态学操作(如膨胀)减少噪声。
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
import org.opencv.videoio.VideoCapture;
public class MotionDetection {
private Mat previousFrame = new Mat();
public Mat detectMotion(Mat currentFrame) {
Mat gray = new Mat();
Imgproc.cvtColor(currentFrame, gray, Imgproc.COLOR_BGR2GRAY);
if (previousFrame.empty()) {
gray.copyTo(previousFrame);
return currentFrame;
}
// 计算帧差
Mat diff = new Mat();
Core.absdiff(previousFrame, gray, diff);
// 二值化处理
Imgproc.threshold(diff, diff, 30, 255, Imgproc.THRESH_BINARY);
// 形态学操作,去噪
Imgproc.dilate(diff, diff, new Mat(), new Point(-1, -1), 2);
// 轮廓检测
Mat hierarchy = new Mat();
List<MatOfPoint> contours = new ArrayList<>();
Imgproc.findContours(diff, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
// 在当前帧上绘制运动目标框
for (MatOfPoint contour : contours) {
Rect rect = Imgproc.boundingRect(contour);
Imgproc.rectangle(currentFrame, rect.tl(), rect.br(), new Scalar(0, 255, 0), 2);
}
// 更新上一帧
gray.copyTo(previousFrame);
return currentFrame;
}
}
方法3:背景建模(适用于复杂场景)
当背景不是固定的,可以使用背景减法来检测前景目标。
import org.opencv.video.BackgroundSubtractorMOG2;
public class BackgroundSubtraction {
private BackgroundSubtractorMOG2 bgSubtractor = Video.createBackgroundSubtractorMOG2();
public Mat detectForeground(Mat frame) {
Mat fgMask = new Mat();
bgSubtractor.apply(frame, fgMask);
// 形态学操作,去噪
Imgproc.morphologyEx(fgMask, fgMask, Imgproc.MORPH_OPEN, new Mat());
return fgMask;
}
}
3. 关键技术解析
(1) Core.absdiff()
计算两幅图像的像素差异:
Core.absdiff(mat1, mat2, diff);
(2) Imgproc.threshold()
对差异图像进行二值化:
Imgproc.threshold(diff, diff, 25, 255, Imgproc.THRESH_BINARY);
• 25 是阈值,表示低于 25 的像素被设为 0,高于 25 的像素设为 255。
(3) Imgproc.findContours()
检测二值图中的轮廓:
Imgproc.findContours(diff, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
(4) Imgproc.rectangle()
绘制检测到的变化区域:
Imgproc.rectangle(mat1, matchLoc, new Point(matchLoc.x + template.cols(), matchLoc.y + template.rows()), new Scalar(0, 255, 0), 2);
4. 适用场景对比
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 直接像素差分法 | 静态图片对比 | 计算简单,适用于小场景 | 不能处理动态变化 |
| 帧间差分法 | 监控、运动检测 | 实时性强,计算高效 | 需要连续帧,不适用于大范围场景 |
| 背景建模 | 复杂环境下的目标检测 | 适用于长期监控,能分离前景 | 计算较复杂,背景变化大时效果不佳 |
总结
• 简单对比 → Core.absdiff()
• 视频运动检测 → 帧间差分
• 复杂场景目标检测 → 背景减法
你打算应用在哪种场景?如果是实时摄像头检测,可以用帧间差分,如果是目标跟踪,可以结合背景建模和特征匹配。