title: OpenCV C++ code to Java Language "Simple Shape Detection App"
body: <p>I am quite new to OpenCV. I'm trying to learn how to convert a given C++ OpenCV code into its Java equivalent.</p>
  
  <p>The Original Code of the below "Java translated" code is from <a href="https://github.com/bsdnoobz/opencv-code/blob/master/shape-detect.cpp" rel="nofollow noreferrer">(GitHub) Shape Detection Algorithm</a></p>
  
  <p>The Code is being written in Eclipse IDE. As an Android Application. The code shows NO ERRORS. And I have tried working my way around it as well, used different ways to convert data types, using Lists instead of only Vectors, and applied the use of MatOfPoint as well... But it always stops responding at Runtime.</p>
  
  <p><strong>ISSUES</strong></p>
  
  <ol>
  <li>IT ALWAYS STOPS AT RUNTIME showing:</li>
  </ol>
  
  <blockquote>
    <p>Unfortunately, ShapeDetection has stopped.</p>
  </blockquote>
  
  <ol start="2">
  <li>There is an issue of the usage of MatOfPoint2f, which is unclear to me how it works, it is being suggested by Java but then later on I need to convert it back to regular MatOfPoint after the ApproxPolyDp function.</li>
  <li>The code being translated is not 100% same of the code from the given link above. I want to use the camera frame and detect shapes at <strong>REAL TIME</strong> instead of loading an image then post-processing</li>
  <li>The error is <strong>INSIDE THE</strong> <code>for loop</code> in <code>onCameraFrame()</code></li>
  <li>I have tried finding solutions, and applied them one by one, and all fails, some is harder to understand probably because of my status as a (quite) newbie.</li>
  <li>I have seen a question quite similar to this question, but there is no answer: <a href="https://stackoverflow.com/questions/24068959/opencv-c-conversion-to-java-for-shape-detection-issues">link</a></li>
  </ol>
  
  <p>I hope you can share your expertise and time with me regarding this problem. It will be a big help for me, I will be able to continue doing my project and possibly release an application to the market.</p>
  
  <p><strong>Here is the MainActivity</strong></p>
  
  <pre><code>    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&lt;MatOfPoint&gt; contours = new ArrayList&lt;MatOfPoint&gt;();
      //private Vector &lt;Vector &lt;Point&gt; &gt; 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 &lt;Vector &lt;Point&gt; &gt; contours;
          Imgproc.findContours(binaryFrame.clone(), contours, mHierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
  
          //List&lt;MatOfPoint&gt; approx;
          //Vector &lt;Point&gt; approx;
          retImg = cameraFrame.clone();
          //Convert List&lt;MatOfPoint&gt; to an array
          //casted the (Point[]) array
          // Object --&gt; Array of Points
          //Point[] contourArray = (Point[]) contours.toArray();
          MatOfPoint2f approxCurve = new MatOfPoint2f();
  
          for (int i = 0; i &lt; 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)) &lt; 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&lt;Double&gt; cos = new Vector&lt;Double&gt;(2);
                  for (int j = 2; j &lt; 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 &amp;&amp; mincos &gt;= -0.1 &amp;&amp; maxcos &lt;= 0.3)
                      setLabel(retImg, "RECT", contours.get(i));
                  else if (vtc == 5 &amp;&amp; mincos &gt;= -0.34 &amp;&amp; maxcos &lt;= -0.27)
                      setLabel(retImg, "PENTA", contours.get(i));
                  else if (vtc == 6 &amp;&amp; mincos &gt;= -0.55 &amp;&amp; maxcos &lt;= -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)) &lt;= 0.2 &amp;&amp;
                      Math.abs(1 - (area / (Math.PI * Math.pow(radius, 2)))) &lt;= 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);
      }
  }
  </code></pre>
  
  <p><strong>The XML is quite simple:</strong> </p>
  
  <pre><code>&lt;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" &gt;
  
      &lt;org.opencv.android.JavaCameraView
          android:layout_width="fill_parent"
          android:layout_height="fill_parent"
          android:visibility="gone"
          android:id="@+id/surface_view" /&gt;
  
  &lt;/RelativeLayout&gt;
  </code></pre>
  
  <p>Anything is appreciated! Thanks in advance!</p>