OpenCV Android 背景减法

发布于 2025-01-04 17:29:32 字数 2512 浏览 0 评论 0原文

我正在开发一个机器人项目,使用 Android 手机作为主处理器,并使用摄像头来检测运动。我从 OpenCV 获取了 Android 二进制包 并正确安装了它。我可以使用 OpenCV 本机相机捕获图像并将其显示到屏幕上。不过,我在使用背景扣除类时遇到了问题。我可以在构造函数中创建一个新的 BackgroundSubtractorMOG 对象,但是当我尝试运行下面的代码时,它会强制退出,我从本机代码中收到错误“BackgroundSubtractorMOG 仅支持 1 通道和 3 通道 8 位图像”。我尝试将 Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA 更改为 Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGB,然后它不会强制退出,但我得到的只是黑屏。我很确定 FRAME_RGB 的 bmp 仍然为空,因为屏幕保持黑色,并且我在位图之后绘制的 fps 计数器(为了清楚起见,从下面发布的代码中删除,作为故障排除步骤)没有显示。

我查看了该函数的 OpenCV C++ 代码(第388行),如果图像类型不是CV_8UC1或CV_8UC3,则会出现图像类型错误,所以我尝试使用java capture.retrieve() 中使用 CvType.CV_8UC3 而不是 Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA,但它强制关闭,并且出现“不支持输出帧格式”错误。

我猜我刚刚遇到了类型转换问题,但我一生都无法弄清楚 OpenCV 的 Android 特定图像类型与记录的常规图像类型是否相符。任何帮助将不胜感激。

变量:

private SurfaceHolder mHolder;
private VideoCapture mCamera;
private Mat mRgba;
private Mat mFGMask;
private BackgroundSubtractorMOG mBGSub;

我的 SurfaceView 的 run() 函数:

public void run() {    
    Bitmap bmp = null;

    synchronized (this) {
        if (mCamera == null)
            break;

        if (!mCamera.grab()) {
            Log.e(TAG, "mCamera.grab() failed");
            break;
        }

        processFrame(mCamera);
        bmp = Bitmap.createBitmap(mFGMask.cols(), mFGMask.rows(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(mFGMask, bmp);
    }

    if (bmp != null) {
        Canvas canvas = mHolder.lockCanvas();
        if (canvas != null) {
            canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2, (canvas.getHeight() - bmp.getHeight()) / 2, null);
            mHolder.unlockCanvasAndPost(canvas);
        }
        bmp.recycle();
    }
}

run() 中引用的 processFrame() 函数:

protected void processFrame(VideoCapture capture) {
    capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
    mBGSub.apply(mRgba, mFGMask);
}

编辑:

最终有效的解决方案:

protected void processFrame(VideoCapture capture) {
    capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGB);
    //GREY_FRAME also works and exhibits better performance
    //capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_GREY_FRAME);
    mBGSub.apply(mRgba, mFGMask, 0.1);
    Imgproc.cvtColor(mFGMask, mRgba, Imgproc.COLOR_GRAY2BGRA, 4);
}

I am working on a robotics project using an Android phone as the main processor and the camera to detect movement. I got the Android binary package from OpenCV and got it correctly installed. I can capture images using the OpenCV native camera and display them to the screen. I'm having problems using the background subtraction class, though. I can make a new BackgroundSubtractorMOG object in the constructor, but when I attempt to run the code below, it force quits I get the error "Only 1- and 3-channel 8-bit images are supported in BackgroundSubtractorMOG" from the native code. I tried changing Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA to Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGB, and then it doesn't force quit, but all I get is a black screen. I'm pretty sure bmp is still null with FRAME_RGB, because the screen stays black, and the fps counter I was drawing right after the bitmap (removed from the code posted below for clarity and as a troubleshooting step) doesn't show up.

I took a look at the OpenCV C++ code for this function (line 388 here), and the image type error occurs if the image type isn't CV_8UC1 or CV_8UC3, so I tried using the java CvType.CV_8UC3 instead of Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA in capture.retrieve(), but it force closed and I got a "Output frame format is not supported" error.

I'm guessing I've just got a type conversion problem, but I can't figure out for the life of me where OpenCV's Android-specific image types fit with their regular image types that are documented. Any help would be appreciated.

The variables:

private SurfaceHolder mHolder;
private VideoCapture mCamera;
private Mat mRgba;
private Mat mFGMask;
private BackgroundSubtractorMOG mBGSub;

My SurfaceView's run() function:

public void run() {    
    Bitmap bmp = null;

    synchronized (this) {
        if (mCamera == null)
            break;

        if (!mCamera.grab()) {
            Log.e(TAG, "mCamera.grab() failed");
            break;
        }

        processFrame(mCamera);
        bmp = Bitmap.createBitmap(mFGMask.cols(), mFGMask.rows(), Bitmap.Config.ARGB_8888);
        Utils.matToBitmap(mFGMask, bmp);
    }

    if (bmp != null) {
        Canvas canvas = mHolder.lockCanvas();
        if (canvas != null) {
            canvas.drawBitmap(bmp, (canvas.getWidth() - bmp.getWidth()) / 2, (canvas.getHeight() - bmp.getHeight()) / 2, null);
            mHolder.unlockCanvasAndPost(canvas);
        }
        bmp.recycle();
    }
}

The processFrame() function referenced in run():

protected void processFrame(VideoCapture capture) {
    capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
    mBGSub.apply(mRgba, mFGMask);
}

Edit:

The solution that ended up working:

protected void processFrame(VideoCapture capture) {
    capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGB);
    //GREY_FRAME also works and exhibits better performance
    //capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_GREY_FRAME);
    mBGSub.apply(mRgba, mFGMask, 0.1);
    Imgproc.cvtColor(mFGMask, mRgba, Imgproc.COLOR_GRAY2BGRA, 4);
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

多情癖 2025-01-11 17:29:32

您是否尝试过使用 cvtColorCV_RGB2RGBACV_RGBA2RGB。因此,也许可以尝试将帧 RGBA 转换为 RGB,然后进行背景减法。像这样的东西:

protected void processFrame(VideoCapture capture) {
    capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
    Mat rgb;
    Imgproc.cvtColor(mRgba, rgb, Imgproc.COLOR_RGBA2RGB);
    mBGSub.apply(rgb, mFGMask);
}

编辑:您可以查看位于此处。但是,测试在主测试用例中有 fail("Not Yet Implemented");

我不确定这是否意味着测试未完成,或者对 BackgroundSubtractorMOG 的支持尚未实现。您可以尝试运行此单元测试中包含的代码,看看它是否确实有效。

此外,C++ 示例 segment_objects.cpp 可能作为使用示例很有帮助。

希望有帮助! :)

Have you tried using cvtColor with CV_RGB2RGBA and CV_RGBA2RGB. So, maybe try converting frame RGBA to RGB, then do background subtraction. Something like this:

protected void processFrame(VideoCapture capture) {
    capture.retrieve(mRgba, Highgui.CV_CAP_ANDROID_COLOR_FRAME_RGBA);
    Mat rgb;
    Imgproc.cvtColor(mRgba, rgb, Imgproc.COLOR_RGBA2RGB);
    mBGSub.apply(rgb, mFGMask);
}

EDIT : You might check out the OpenCV unit-test for BackgroundSubtractorMOG located here. However, the test has fail("Not yet implemented"); in the main test case.

I'm not sure if that means the test isn't complete, or the support for BackgroundSubtractorMOG is not implemented. You might try running the code contained in this unit-test to see if it actually works.

Also, the C++ sample segment_objects.cpp might be helpful as a usage example.

Hope that helps! :)

肥爪爪 2025-01-11 17:29:32

非常感谢你们!对于未来访问此页面的查看者,您可能需要调整这些知识才能使事情正常进行。在 SDK v2.4.4 中,我在 onCameraFrame 方法中应用了它。回想一下,该方法从相机接收输入帧。您使用输入并返回要在 Android 设备屏幕上显示的框架。这里有一个例子:

//Assume appropriate imports    
private BackgroundSubtractorMOG sub = new BackgroundSubtractorMOG(3, 4, 0.8);
private Mat mGray = new Mat();
private Mat mRgb = new Mat();
private Mat mFGMask = new Mat();

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    mGray = inputFrame.gray(); //I chose the gray frame because it should require less resources to process
    Imgproc.cvtColor(mGray, mRgb, Imgproc.COLOR_GRAY2RGB); //the apply function will throw the above error if you don't feed it an RGB image
    sub.apply(mRgb, mFGMask, learningRate); //apply() exports a gray image by definition

    return mFGMask;
}

为了理解我关于 apply() 产生的灰度图像的观点,如果你想做 RGBA 版本,你可能必须在调用 apply() 之后使用 cvtcolor:

private Mat mRgba = new Mat();

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    mRgba = inputFrame.rgba();
    Imgproc.cvtColor(mRgba, mRgb, Imgproc.COLOR_RGBA2RGB); //the apply function will throw the above error if you don't feed it an RGB image
    sub.apply(mRgb, mFGMask, learningRate); //apply() exports a gray image by definition
    Imgproc.cvtColor(mFGMask, mRgba, Imgproc.COLOR_GRAY2RGBA);

    return mRgba;
}

Thanks you guys so much! And for future viewers who come to this page, you might have to tweak this knowledge to get things working. In SDK v2.4.4 I applied this in the onCameraFrame method. Recall that the method takes in an input frame from the camera. You use the input and return the frame that is to be displayed on the screen of your android device. Here's an example:

//Assume appropriate imports    
private BackgroundSubtractorMOG sub = new BackgroundSubtractorMOG(3, 4, 0.8);
private Mat mGray = new Mat();
private Mat mRgb = new Mat();
private Mat mFGMask = new Mat();

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    mGray = inputFrame.gray(); //I chose the gray frame because it should require less resources to process
    Imgproc.cvtColor(mGray, mRgb, Imgproc.COLOR_GRAY2RGB); //the apply function will throw the above error if you don't feed it an RGB image
    sub.apply(mRgb, mFGMask, learningRate); //apply() exports a gray image by definition

    return mFGMask;
}

To get across my point about the gray image that comes out of apply(), if you wanted to do a RGBA version, you might have to use a cvtcolor after the call to apply():

private Mat mRgba = new Mat();

public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
    mRgba = inputFrame.rgba();
    Imgproc.cvtColor(mRgba, mRgb, Imgproc.COLOR_RGBA2RGB); //the apply function will throw the above error if you don't feed it an RGB image
    sub.apply(mRgb, mFGMask, learningRate); //apply() exports a gray image by definition
    Imgproc.cvtColor(mFGMask, mRgba, Imgproc.COLOR_GRAY2RGBA);

    return mRgba;
}
情深如许 2025-01-11 17:29:32

另外,使用最新的 openCV,您需要初始化:

private BackgroundSubtractorMOG2 Sub = Video.createBackgroundSubtractorMOG2();

Also with latest openCV you need to initialize with:

private BackgroundSubtractorMOG2 Sub = Video.createBackgroundSubtractorMOG2();

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文