【问题标题】:Android Real Time DFT in Camera View using OpenCV使用 OpenCV 的相机视图中的 Android 实时 DFT
【发布时间】:2016-07-17 17:31:44
【问题描述】:

我一直在努力实现一个将 DFT 直接应用于相机视图的 Android 应用程序。在stackoverflow做研究,我可以找到以下主题:

SOLVED - Load Image in Mat and Display after DFT process

SOLVED - Load Image in Mat and Display after DFT process

Convert OpenCv DCT to Android

我还尝试了使用 JNI 的不同解决方案: http://allaboutee.com/2011/11/12/discrete-fourier-transform-in-android-with-opencv/

然后我可以实现我的主要活动代码:

package ch.hepia.lsn.opencv_native_androidstudio;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceView;
import android.view.WindowManager;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import android.hardware.Camera;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.core.Rect;
import org.opencv.core.Size;
import org.opencv.imgproc.Imgproc;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity implements CameraBridgeViewBase.CvCameraViewListener2 {
    private static final String TAG = "OCVSample::Activity";

    private CameraBridgeViewBase mOpenCvCameraView;

    private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
        @Override
        public void onManagerConnected(int status) {
            switch (status) {
                case LoaderCallbackInterface.SUCCESS: {
                    Log.i(TAG, "OpenCV loaded successfully");
                    mOpenCvCameraView.enableView();
                }
                break;
                default: {
                    super.onManagerConnected(status);
                }
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Load ndk built module, as specified
        // in moduleName in build.gradle
        System.loadLibrary("native");

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        setContentView(R.layout.activity_main);

        mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.main_surface);
        mOpenCvCameraView.setVisibility(SurfaceView.VISIBLE);
        mOpenCvCameraView.setCvCameraViewListener(this);
    }

    @Override
    public void onPause() {
        super.onPause();
        disableCamera();
    }

    @Override
    public void onResume() {
        super.onResume();
        if (!OpenCVLoader.initDebug()) {
            Log.d(TAG, "Internal OpenCV library not found. Using OpenCV Manager for initialization");
            OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_3_0_0, this, mLoaderCallback);
        } else {
            Log.d(TAG, "OpenCV library found inside package. Using it!");
            mLoaderCallback.onManagerConnected(LoaderCallbackInterface.SUCCESS);
        }
    }

    public void onDestroy() {
        super.onDestroy();
        disableCamera();
    }

    public void disableCamera() {
        if (mOpenCvCameraView != null)
            mOpenCvCameraView.disableView();
    }

    public void onCameraViewStarted(int width, int height) {
    }

    public void onCameraViewStopped() {
    }

    private Mat getDFT(Mat singleChannel) {

        singleChannel.convertTo(singleChannel, CvType.CV_64FC1);

        int m = Core.getOptimalDFTSize(singleChannel.rows());
        int n = Core.getOptimalDFTSize(singleChannel.cols()); // on the border
        // add zero
        // values
        // Imgproc.copyMakeBorder(image1,
        // padded, 0, m -
        // image1.rows(), 0, n

        Mat padded = new Mat(new Size(n, m), CvType.CV_64FC1); // expand input
        // image to
        // optimal size

        Core.copyMakeBorder(singleChannel, padded, 0, m - singleChannel.rows(), 0,
                n - singleChannel.cols(), Core.BORDER_CONSTANT);

        List<Mat> planes = new ArrayList<Mat>();
        planes.add(padded);
        planes.add(Mat.zeros(padded.rows(), padded.cols(), CvType.CV_64FC1));

        Mat complexI = Mat.zeros(padded.rows(), padded.cols(), CvType.CV_64FC2);

        Mat complexI2 = Mat
                .zeros(padded.rows(), padded.cols(), CvType.CV_64FC2);

        Core.merge(planes, complexI); // Add to the expanded another plane with
        // zeros

        Core.dft(complexI, complexI2); // this way the result may fit in the
        // source matrix

        // compute the magnitude and switch to logarithmic scale
        // => log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
        Core.split(complexI2, planes); // planes[0] = Re(DFT(I), planes[1] =
        // Im(DFT(I))

        Mat mag = new Mat(planes.get(0).size(), planes.get(0).type());

        Core.magnitude(planes.get(0), planes.get(1), mag);// planes[0]
        // =
        // magnitude

        Mat magI = mag;
        Mat magI2 = new Mat(magI.size(), magI.type());
        Mat magI3 = new Mat(magI.size(), magI.type());
        Mat magI4 = new Mat(magI.size(), magI.type());
        Mat magI5 = new Mat(magI.size(), magI.type());

        Core.add(magI, Mat.ones(padded.rows(), padded.cols(), CvType.CV_64FC1),
                magI2); // switch to logarithmic scale
        Core.log(magI2, magI3);

        Mat crop = new Mat(magI3, new Rect(0, 0, magI3.cols() & -2,
                magI3.rows() & -2));

        magI4 = crop.clone();

        // rearrange the quadrants of Fourier image so that the origin is at the
        // image center
        int cx = magI4.cols() / 2;
        int cy = magI4.rows() / 2;

        Rect q0Rect = new Rect(0, 0, cx, cy);
        Rect q1Rect = new Rect(cx, 0, cx, cy);
        Rect q2Rect = new Rect(0, cy, cx, cy);
        Rect q3Rect = new Rect(cx, cy, cx, cy);

        Mat q0 = new Mat(magI4, q0Rect); // Top-Left - Create a ROI per quadrant
        Mat q1 = new Mat(magI4, q1Rect); // Top-Right
        Mat q2 = new Mat(magI4, q2Rect); // Bottom-Left
        Mat q3 = new Mat(magI4, q3Rect); // Bottom-Right

        Mat tmp = new Mat(); // swap quadrants (Top-Left with Bottom-Right)
        q0.copyTo(tmp);
        q3.copyTo(q0);
        tmp.copyTo(q3);

        q1.copyTo(tmp); // swap quadrant (Top-Right with Bottom-Left)
        q2.copyTo(q1);
        tmp.copyTo(q2);

        Core.normalize(magI4, magI5, 0, 255, Core.NORM_MINMAX);

        Mat realResult = new Mat(magI5.size(), CvType.CV_8UC1);

        magI5.convertTo(realResult, CvType.CV_8UC1);

        return realResult;
    }

    public Mat onCameraFrame(CameraBridgeViewBase.CvCameraViewFrame inputFrame) {
        //System.out.print("teste");
        Mat matGray = inputFrame.gray();
        return getDFT(inputFrame.gray());
    }
}

但问题是,我仍然收到此错误:

07-03 22:46:46.205 13700-28322/ch.hepia.lsn.opencv_native_androidstudio A/libc:致命 信号 11 (SIGSEGV),代码 1,tid 28322 中的故障地址 0x10 (线程 9802)

我认为这是因为一些处理限制,因为我只是复制了与其他用户使用普通图像一起工作的代码。

我的问题是:

  • 如何检查此错误是否是由于处理限制造成的?

  • 还有其他方法可以使用 OpenCV 或其他库来实现吗?

谢谢。

【问题讨论】:

  • 它是立即崩溃还是像这个问题一样在一段时间后崩溃:stackoverflow.com/questions/33052806/…,第二个问题是:您的代码是否在没有 DFT 的情况下工作?如果是,则错误在 DFT 中。如果即使没有 DFT 也无法正常工作,那么错误就在其他地方。
  • 该应用程序似乎立即崩溃,但如果我查看 logcast,我可以看到它运行了 3~5 次,然后崩溃了。它在没有 DFT 的情况下工作,如果我只输出灰色的框架,它工作正常。
  • 那么问题在于释放资源。您正在创建很多矩阵,但没有释放内存。甚至 java 也不会为您释放它,因为它不知道本机 lib 中分配的资源。因此,请确保在您创建的每个垫子上返回之前调用 relase 方法,除了您要返回的垫子。
  • 您好,我在代码中添加了一些 release() ,但仍然出现相同的错误。我还在 dft 算法之前和之后添加了一些日志。结果显示dft函数只运行一次:07-04 18:38:23.102 22199-22328/ch.hepia.lsn.opencv_native_androidstudio W/myApp: before dft 07-04 18:38:23.122 22199-22199/ch .hepia.lsn.opencv_native_androidstudio D/JavaCameraView:收到预览帧。帧大小:1775616 07-04 18:38:23.172 22199-22199/ch.hepia.lsn.opencv_native_androidstudio D/JavaCameraView:收到预览帧。帧大小:1775616 (...) Breaks
  • 我想我需要一些东西让相机停止捕捉帧,等待处理 dft,然后在屏幕上显示。但我找不到任何关于它的消息来源。

标签: android opencv dft


【解决方案1】:

我已将最初发布的代码移植到 Android Studio(来自 Eclipse)和 OpenCV 3.1.0。我认为这个版本的 openCV 中的 Core.add() 函数存在问题 - 请参阅帖子here

我按照建议使用了 Core.addWeighted(),我至少可以让 dft 显示出来,但不久之后它就会耗尽内存。我认为 像 split 这样的函数也使用 add() 所以我认为我们需要在 openCV 中寻找解决该问题的方法。

我发布的代码可以改进以更好地利用资源,例如,保持键数组的静态分配,不要继续调用 size() 而是再次保持静态,减少分配的 Mats 的数量等。您还可以减小捕获图像的大小,因为 Mats 在更现代的手机(我有三星 S6)上会变得很大,所以使用

mOpenCvCameraView.setMaxFrameSize(176, 152);

或任何更易于管理的大小。

如果您想减少帧进程的数量,请保留一个静态计数器,在每次捕获帧时递增,并且仅在计数器可被 5、10 或其他任何值整除时才调用 getDFT() 以仅每 5 次处理一次,第 10 帧等

【讨论】:

  • 嗨 Timegalore,首先,感谢您的回答。使用 addWeighted 解决了这个问题,但结果我的函数给了我黑框。我想知道我是否在 getDFT 函数中返回了错误的矩阵,在这种情况下我返回的是:realResult。
  • addWeighted 是怎么做的?我用过:Core.addWeighted(magI, 1, Mat.ones(padded.rows(), padded.cols(), CvType.CV_64FC1), 1, 0, magI2);
  • 我使用相同的参数,在代码执行的中间我得到了这个错误:pastebin.com/G0fF5Hah 我添加的唯一与 core.addWeighted 不同的是:mOpenCvCameraView.setMaxFrameSize(176, 152 );我也在用三星s6。
  • 好的,我只是降低了相机拍摄的图像质量,可以得到一些结果。看来问题就是因为这个。现在我认为下一步是想办法释放内存和智能使用图像处理。谢谢你的时间!
猜你喜欢
  • 2016-04-08
  • 2020-10-11
  • 1970-01-01
  • 1970-01-01
  • 2021-07-01
  • 2017-09-08
  • 2021-02-11
  • 2023-03-15
  • 1970-01-01
相关资源
最近更新 更多