【问题标题】:OpenCV C++ code to Java Language "Simple Shape Detection App"OpenCV C++ 代码到 Java 语言“简单形状检测应用程序”
【发布时间】:2015-11-29 03:21:25
【问题描述】:

我对 OpenCV 很陌生。我正在尝试学习如何将给定的 C++ OpenCV 代码转换为其 Java 等效代码。

以下“Java翻译”代码的原始代码来自(GitHub) Shape Detection Algorithm

代码是在 Eclipse IDE 中编写的。作为 Android 应用程序。代码显示没有错误。而且我也尝试过解决它,使用不同的方法来转换数据类型,使用列表而不是仅使用向量,并应用了 MatOfPoint 的使用......但它总是在运行时停止响应。

问题

  1. 它总是在运行时停止显示:

很遗憾,ShapeDetection 已停止。

  1. MatOfPoint2f 的使用存在问题,我不清楚它是如何工作的,Java 建议使用它,但稍后我需要在 ApproxPolyDp 函数之后将其转换回常规 MatOfPoint。
  2. 正在翻译的代码与上面给定链接中的代码并非 100% 相同。我想使用相机框架并实时检测形状,而不是加载图像然后进行后期处理
  3. 错误是INSIDE THE for loop in onCameraFrame()
  4. 我曾尝试寻找解决方案,并一一应用,但都失败了,有些更难理解,可能是因为我是(相当)新手。
  5. 我看到一个和这个问题很相似的问题,但是没有答案:link

我希望您能就这个问题与我分享您的专业知识和时间。这对我来说是一个很大的帮助,我将能够继续做我的项目并可能向市场发布应用程序。

这里是 MainActivity

    package com.example.shapedetection;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;

import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.OpenCVLoader;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.MatOfPoint2f;
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 android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends ActionBarActivity implements CvCameraViewListener2 {

    private CameraBridgeViewBase cameraView;
    private final String TAG = "ShapeDetection::";
    private List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
    //private Vector <Vector <Point> > contours;

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN);

        setContentView(R.layout.activity_main);
        cameraView = (CameraBridgeViewBase) findViewById(R.id.surface_view);
        cameraView.setVisibility(SurfaceView.VISIBLE);
        cameraView.setCvCameraViewListener(this);
    }

    protected void onResume() {
        super.onResume();
        OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this, mLoaderCallback);
    }

    protected void onPause() {
        super.onPause();
        if (cameraView != null)
            cameraView.disableView();
    }

    protected void onDestroy() {
        super.onDestroy();
        if (cameraView != null)
            cameraView.disableView();
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

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

    @Override
    public void onCameraViewStopped() {     
    }

    @Override
    public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
        Mat cameraFrame = inputFrame.rgba();
        Mat grayFrame = new Mat();
        Imgproc.cvtColor(cameraFrame, grayFrame, Imgproc.COLOR_BGR2GRAY);
        Mat binaryFrame = new Mat();    
        Mat mHierarchy = new Mat();
        Mat retImg = new Mat();
        //Imgproc.Canny(grayFrame, binaryFrame, 80, 90);
        Imgproc.Canny(grayFrame, binaryFrame, 0, 50);
        //Vector <Vector <Point> > contours;
        Imgproc.findContours(binaryFrame.clone(), contours, mHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

        //List<MatOfPoint> approx;
        //Vector <Point> approx;
        retImg = cameraFrame.clone();
        //Convert List<MatOfPoint> to an array
        //casted the (Point[]) array
        // Object --> Array of Points
        //Point[] contourArray = (Point[]) contours.toArray();
        MatOfPoint2f approxCurve = new MatOfPoint2f();

        for (int i = 0; i < contours.size(); i++)
        {

            MatOfPoint2f contour2f = new MatOfPoint2f(contours.get(i).toArray());
            double approxDistance = Imgproc.arcLength(contour2f, true) * 0.02;
            // Approximate contour with accuracy proportional
            // to the contour perimeter
            Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
            //MatOfPoint2f back to MatOfPoint
            MatOfPoint approxCurve2 = new MatOfPoint(approxCurve);
            // Skip small or non-convex objects 
            //contourArray[i]
            if (Math.abs(Imgproc.contourArea(contour2f)) < 100 || !Imgproc.isContourConvex((MatOfPoint) approxCurve2))
                //continue;

            if (approxCurve2.size().equals(3))
            {
                setLabel(retImg, "TRI", contours.get(i));    // Triangles
            }
            else if (approxCurve2.size().equals(4) || approxCurve2.size().equals(5) || approxCurve2.size().equals(6))
            {
                // Number of vertices of polygonal curve
                //Point[] sizer = approxCurve2.toArray();
                //int vtc = sizer.length;
                int vtc;
                if (approxCurve2.size().equals(4))
                    vtc = 4;
                else if (approxCurve2.size().equals(5))
                    vtc = 5;
                else
                    vtc = 6;

                // Get the cosines of all corners

                //Converting approxCurve2(MatOfPoint) to Array
                //This process seems to be one of the reasons to the 
                //silent error, when I tested it
                Point[] approxCurveToArray = approxCurve2.toArray();
                Vector<Double> cos = new Vector<Double>(2);
                for (int j = 2; j < vtc+1; j++)
                    cos.add(angle(approxCurveToArray[j%vtc], approxCurveToArray[j-2], approxCurveToArray[j-1]));

                // Sort ascending the cosine values
                Collections.sort(cos);
                // Get the lowest and the highest cosine
                double mincos = cos.firstElement();
                double maxcos = cos.lastElement();

                // Use   the degrees obtained above and the number of vertices
                // to determine the shape of the contour
                if (vtc == 4 && mincos >= -0.1 && maxcos <= 0.3)
                    setLabel(retImg, "RECT", contours.get(i));
                else if (vtc == 5 && mincos >= -0.34 && maxcos <= -0.27)
                    setLabel(retImg, "PENTA", contours.get(i));
                else if (vtc == 6 && mincos >= -0.55 && maxcos <= -0.45)
                    setLabel(retImg, "HEXA", contours.get(i));
            }
            else
            {
                // Detect and label circles
                double area = Imgproc.contourArea(contours.get(i));
                Rect r = Imgproc.boundingRect(contours.get(i));
                int radius = r.width / 2;

                if (Math.abs(1 - ((double)r.width / r.height)) <= 0.2 &&
                    Math.abs(1 - (area / (Math.PI * Math.pow(radius, 2)))) <= 0.2)
                    setLabel(retImg, "CIR", contours.get(i));
            }
        } //End of for loop

        ////////////////////////

        //Test Sample
        /*
        Point pt1, pt2;
        pt1 = new Point(200,200);
        pt2 = new Point(500,800);

        org.opencv.core.Core.rectangle(grayFrame, pt1, pt2,new Scalar(255,255,255), -1);
        org.opencv.core.Core.putText(grayFrame, "TEST SAMPLE", pt1, 3, .4, new Scalar(0,0,0), 1);
        return grayFrame;
        */

        return retImg;
    }

    //Helper function to find a cosine of angle between vectors
    public double angle(Point pt1, Point pt2, Point pt0) {
        double dx1 = pt1.x - pt0.x;
        double dy1 = pt1.y - pt0.y;
        double dx2 = pt2.x - pt0.x;
        double dy2 = pt2.y - pt0.y;
        return (dx1*dx2 + dy1*dy2)/Math.sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10);
    }

    void setLabel(Mat im, String label, MatOfPoint contour)
    {
        int fontface = 3;
        double scale = 0.4;
        int thickness = 1;
        int[] baseline = {0};
        Point pt;
        Size text = org.opencv.core.Core.getTextSize(label, fontface, scale, thickness, baseline);

                //getTextSize(label, fontface, scale, thickness, baseline);
        Rect r = Imgproc.boundingRect((MatOfPoint) contours);

        pt = new Point(r.x + ((r.width - text.width) / 2), r.y + ((r.height + text.height) / 2));   
        //pt1 = new Point(0, baseline[0]);
        //pt2 = new Point(text.width, -text.height);
        org.opencv.core.Core.rectangle(im, pt /*Point(0, baseline)*/, pt/*Point(text.width, -text.height)*/,new Scalar(255,255,255), thickness - 2);
        //rectangle(im, pt /*Point(0, baseline)*/, pt/*Point(text.width, -text.height)*/,new Scalar(255,255,255), thickness - 2);
        //putText(im, label, pt, fontface, scale, new Scalar(0,0,0), thickness, 8);
        org.opencv.core.Core.putText(im, label, pt, fontface, scale, new Scalar(0,0,0), thickness);
    }
}

XML 非常简单:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.shapedetection.MainActivity" >

    <org.opencv.android.JavaCameraView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:visibility="gone"
        android:id="@+id/surface_view" />

</RelativeLayout>

任何事情都值得赞赏!提前致谢!

【问题讨论】:

  • 错误是异常吗?如果是,则显示完整的堆栈跟踪。
  • 我在手机上运行它时没有看到任何异常。我将我的手机用于这个特定的应用程序......并且 logcat 速度太快......

标签: java android opencv


【解决方案1】:

我自己发现了错误,它在 setLabel() 方法中。

Rect r = Imgproc.boundingRect((MatOfPoint) contours); 应该是 Rect r = Imgproc.boundingRect(contour);参数传递错误。

在获取approxCurve2.size().equals() 值方面,程序运行,尽管它仍然是错误的。 C 和 Java 在这方面可能存在价值差异。

未检测到三角形和矩形,所有检测到的轮廓都标记为圆形。

【讨论】:

    【解决方案2】:

    approxCurve2.size() 为您提供宽度 x 高度,例如1 x 3 用于三角形。相比之下, approxCurve2.width() 为所有基本形状返回 1,但 approxCurve.height() 返回实际的顶点数。

    以下将用于检测顶点数:

        if (approxCurve2.height() == 3)
        {
            setLabel(outMat, "TRI", contours.get(i));    // Triangles
        }
        else if (approxCurve2.height() == 4 || approxCurve2.height() == 5 || approxCurve2.height() == 6)
        {
            // Number of vertices of polygonal curve
            int vertices = approxCurve2.height();
            ......
        }
    

    对于五边形检测,您可能还需要考虑角度余弦值的小数舍入。

    以下值范围将可靠地检测正五边形。

    if (vertices == 4 && minCosineOfCorners >= -0.1 && maxCosineOfCorners <= 0.3)
        setLabel(outMat, "RECT", contours.get(i));
    else if (vertices == 5 && minCosineOfCorners >= -0.34 && maxCosineOfCorners <= -0.26)
        setLabel(outMat, "PENTA", contours.get(i));
    else if (vertices == 6 && minCosineOfCorners >= -0.55 && maxCosineOfCorners <= -0.45)
        setLabel(outMat, "HEXA", contours.get(i));
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-10
      • 1970-01-01
      • 1970-01-01
      • 2016-06-01
      • 2014-07-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多