OpenCV C ++代码到Java语言"简单形状检测应用程序"

时间:2015-09-16 06:41:25

标签: java android opencv

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

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

Code是用Eclipse IDE编写的。作为Android应用程序。代码显示无错误。我也尝试过使用它,使用不同的方式转换数据类型,使用Lists而不是仅使用Vectors,并使用MatOfPoint ......但它总是在运行时停止响应。

问题

  1. 它总是在RUNTIME上显示:
  2.   

    不幸的是,ShapeDetection已停止。

    1. 使用MatOfPoint2f存在一个问题,我不清楚它是如何工作的,它是由Java建议的,但后来我需要在ApproxPolyDp函数之后将其转换回常规的MatOfPoint。
    2. 正在翻译的代码与上面给定链接的代码不是100%相同。我想使用相机框架并在实时检测形状,而不是加载图像然后进行后期处理
    3. for loop
    4. 中的错误 INSIDE THE onCameraFrame()
    5. 我尝试过找到解决方案,并逐一应用它们,一切都失败了,有些更难以理解,可能是因为我作为(相当)新手的身份。
    6. 我看到一个与此问题非常相似的问题,但没有答案:link
    7. 我希望您能就此问题与我分享您的专业知识和时间。这对我来说将是一个很大的帮助,我将能够继续完成我的项目并可能向市场发布应用程序。

      以下是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>
      

      一切都值得赞赏!提前谢谢!

2 个答案:

答案 0 :(得分:0)

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

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

该程序在获取approxCurve2.size().equals()值方面运行,尽管仍然存在错误。 C和Java之间的区域可能存在值差异。

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

答案 1 :(得分:0)

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));