我对OpenCV很陌生。我试图学习如何将给定的C ++ OpenCV代码转换为Java等效代码。
以下原始代码" Java翻译"代码来自(GitHub) Shape Detection Algorithm
Code是用Eclipse IDE编写的。作为Android应用程序。代码显示无错误。我也尝试过使用它,使用不同的方式转换数据类型,使用Lists而不是仅使用Vectors,并使用MatOfPoint ......但它总是在运行时停止响应。
问题
不幸的是,ShapeDetection已停止。
for loop
onCameraFrame()
我希望您能就此问题与我分享您的专业知识和时间。这对我来说将是一个很大的帮助,我将能够继续完成我的项目并可能向市场发布应用程序。
以下是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>
一切都值得赞赏!提前谢谢!
答案 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));