在开发Android应用时,一直在寻找一种简单的图像处理和运动检测方法。OpenCV作为一个开源的计算机视觉和机器学习软件库,非常适合需求,因为它已经被移植到Android环境中。
OpenCV4Android提供了一个接口,可以访问手机内置的摄像头,以便捕获摄像头帧进行进一步处理。可以在官方的示例中查看代码是如何使用的。在应用中,捕获输入帧,然后使用库提供的算法进行运动检测。
以下是使用OpenCV4Android进行运动检测的代码示例。
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
if (isNotRealTimeProcessing()) {
try {
streamingLock.lock();
capturedFrame = inputFrame.rgba();
} finally {
streamingLock.unlock();
}
if (!frameProcessing && !isUploadInProgress) {
frameProcessing = true;
capturedFrame.copyTo(processingFrame);
fireProcessingAction(ProcessingAction.PROCESS_FRAME);
}
return capturedFrame;
} else {
return processFrame(inputFrame.rgba());
}
}
检测运动的一种简单方法是将帧转换为灰度,并计算帧之间的差异。
public final class BasicDetector extends BaseDetector implements IDetector {
private static final String TAG = AppConfig.LOG_TAG_APP + ":BasicDetector";
public static final int N = 4;
private Mat[] buf = null;
private int last = 0;
private List contours = new ArrayList();
private int threshold;
private Mat hierarchy = new Mat();
public BasicDetector(int threshold) {
this.threshold = threshold;
}
@Override
public Mat detect(Mat source) {
Size size = source.size();
int i, idx1 = last, idx2;
Mat silh;
if (buf == null || buf[0].width() != size.width || buf[0].height() != size.height) {
if (buf == null) {
buf = new Mat[N];
}
for (i = 0; i < N; i++) {
if (buf[i] != null) {
buf[i].release();
buf[i] = null;
}
buf[i] = new Mat(size, CvType.CV_8UC1);
buf[i] = Mat.zeros(size, CvType.CV_8UC1);
}
}
Imgproc.cvtColor(source, buf[last], Imgproc.COLOR_BGR2GRAY);
idx2 = (last + 1) % N;
last = idx2;
silh = buf[idx2];
Core.absdiff(buf[idx1], buf[idx2], silh);
Imgproc.threshold(silh, silh, threshold, 255, Imgproc.THRESH_BINARY);
contours.clear();
Imgproc.findContours(silh, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
Imgproc.drawContours(source, contours, -1, contourColor, contourThickness);
return source;
}
}
另一种方法是使用背景减法技术来检测运动。
public final class BackgroundSubtractorDetector extends BaseDetector implements IDetector {
private static final double LEARNING_RATE = 0.1;
private static final int MIXTURES = 4;
private static final int HISTORY = 3;
private static final double BACKGROUND_RATIO = 0.8;
private Mat buf = null;
private BackgroundSubtractorMOG bg;
private Mat fgMask = new Mat();
private List contours = new ArrayList();
private Mat hierarchy = new Mat();
public BackgroundSubtractorDetector() {
bg = new BackgroundSubtractorMOG(HISTORY, MIXTURES, BACKGROUND_RATIO);
}
public BackgroundSubtractorDetector(double backgroundRatio) {
bg = new BackgroundSubtractorMOG(HISTORY, MIXTURES, backgroundRatio / 100.0);
}
@Override
public Mat detect(Mat source) {
Size size = source.size();
if (buf == null || buf.width() != size.width || buf.height() != size.height) {
if (buf == null) {
buf = new Mat(size, CvType.CV_8UC1);
buf = Mat.zeros(size, CvType.CV_8UC1);
}
}
Imgproc.cvtColor(source, buf, Imgproc.COLOR_RGBA2RGB);
bg.apply(buf, fgMask, LEARNING_RATE);
Imgproc.erode(fgMask, fgMask, new Mat());
Imgproc.dilate(fgMask, fgMask, new Mat());
Imgproc.cvtColor(fgMask, silh, Imgproc.COLOR_GRAY2RGBA);
contours.clear();
Imgproc.findContours(fgMask, contours, hierarchy, Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
Imgproc.drawContours(source, contours, -1, contourColor, contourThickness);
return source;
}
}