我对Java和Android一般都很陌生,但对C / C ++有一些很好的经验。我试图在同一个应用程序中使用多个类文件和多个Camera API。
我正在尝试构建一个获得全分辨率传感器数据的全景应用程序(我有一个800万像素的Nexus 5)。据我所知和研究,CameraBridgeViewBase类'方法onCameraFrame()基本上对数据进行缩减采样以适合您设备的屏幕分辨率(在我的情况下为1280x960)。因此,我不能将它用于我想要做的事情;我需要使用Java Camera API。
我基本上接受了代码presented here,并重做了几件事来尝试获取完整数据。
主java文件:
package org.opencv.samples.tutorial3;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.CameraBridgeViewBase;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewFrame;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener;
import org.opencv.android.CameraBridgeViewBase.CvCameraViewListener2;
import org.opencv.android.JavaCameraView;
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.Point;
import org.opencv.core.Scalar;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
import org.opencv.android.JavaCameraView;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
public class Sample3Native extends Activity implements CvCameraViewListener2{
private static final String TAG = "OCVSample::Activity";
public static final int VIEW_MODE_RGBA = 0;
public static final int SAVE_IMAGE_MAT = 1;
public static final int CAPT_STILL_IM = 2;
private static int viewMode = VIEW_MODE_RGBA;
// public static int image_count = 0;
private MenuItem mStitch;
private MenuItem mItemCaptureImage;
private Mat mRgba;
private Mat mGrayMat;
private Mat panorama;
private Mat mtemp;
private List<Mat> images_to_be_stitched = new ArrayList<Mat>();
private CameraBridgeViewBase mOpenCvCameraView;
private long mPrevTime = new Date().getTime();
private static final int FRAME2GRAB = 10;
private int mframeNum = 0;
private static final File tempImageDir = new File(Environment.getExternalStorageDirectory() + File.separator + "panoTmpImage");
private static final File StitchImageDir = new File(Environment.getExternalStorageDirectory()+ File.separator + "panoStitchIm");
private static final String mImageName = "im";
private static final String mImageExt = ".jpeg";
private long recordStart = new Date().getTime();
private static final long MAX_VIDEO_INTERVAL_IN_SECONDS = 3 * 1000; // Convert milliseconds to seconds
//private Sample3NativeCapturer imageCapturer = new Sample3NativeCapturer(getBaseContext(),1);
//private Sample3Native_imgCapture imageCapturer;
//Had to insert to get it to work..
public final Handler mHandler = new Handler();
public
// Create runnable for posting
final Runnable mUpdateResults = new Runnable() {
public void run() {
updateResultsInUi();
}
};
private void updateResultsInUi()
{
}
private BaseLoaderCallback mLoaderCallback = new BaseLoaderCallback(this) {
@Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS: {
Log.i(TAG, "OpenCV loaded successfully");
//imageCapturer.enableView();
//imageCapturer.setOnTouchListener( Sample3Native.this);
// Load native library after(!) OpenCV initialization
System.loadLibrary("native_sample");
mOpenCvCameraView.enableView();
}
break;
default: {
super.onManagerConnected(status);
}
break;
}
}
};
public Sample3Native() {
Log.i(TAG, "Instantiated new " + this.getClass());
}
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "called onCreate");
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
setContentView(R.layout.tutorial3_surface_view);
final Button btnVidCapt = (Button) findViewById(R.id.btnVidCapt);
btnVidCapt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
startVidCap();
}
});
final Button btnStitch = (Button) findViewById(R.id.btnStitch);
btnStitch.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
stitchImages();
}
});
final Button btnViewStitchedIm = (Button) findViewById(R.id.btnViewStitchedIm);
btnViewStitchedIm.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
viewStitchImages();
}
});
//button I may have to modify when capturing an image..
final Button btnCapStil = (Button) findViewById(R.id.btnCapStil);
btnCapStil.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
captStillImage();
}
});
mOpenCvCameraView = (CameraBridgeViewBase) findViewById(R.id.tutorial4_activity_surface_view);
mOpenCvCameraView.setCvCameraViewListener(this);
// imageCapturer = (Sample3NativeCapturer) findViewById(R.id.tutorial4_activity_surface_view);
// imageCapturer.setCvCameraViewListener(this);
}
@Override
public void onPause() {
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
// if (imageCapturer != null)
// imageCapturer.disableView();
super.onPause();
}
@Override
public void onResume() {
super.onResume();
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_3, this,
mLoaderCallback);
}
public void onDestroy() {
super.onDestroy();
if (mOpenCvCameraView != null)
mOpenCvCameraView.disableView();
// if (imageCapturer != null)
// imageCapturer.disableView();
}
public void onCameraViewStarted(int width, int height) {
mRgba = new Mat(height, width, CvType.CV_8UC3);
mGrayMat = new Mat(height, width, CvType.CV_8UC1);
mtemp = new Mat(height, width, CvType.CV_8UC3);
panorama = new Mat(height, width, CvType.CV_8UC3);
}
public void onCameraViewStopped() {
mRgba.release();
mGrayMat.release();
mtemp.release();
panorama.release();
}
public Mat onCameraFrame(CvCameraViewFrame inFrame) {
Mat inputFrame = inFrame.rgba();
inputFrame.copyTo(mRgba);
switch (Sample3Native.viewMode) {
case Sample3Native.VIEW_MODE_RGBA: {
Core.putText(mRgba, "Video Mode", new Point(10, 50), 3, 1, new Scalar(255, 0, 0, 255), 2);
// Update start recordtime until starting recording
}break;
case Sample3Native.SAVE_IMAGE_MAT: {
long curTime = new Date().getTime();
Core.putText(mRgba, "Record Mode", new Point(10, 50), 3, 1, new Scalar(255, 0, 0, 255), 2);
long timeDiff = curTime - recordStart;
Log.i("timeDiff", Long.toString(timeDiff));
if ( timeDiff < MAX_VIDEO_INTERVAL_IN_SECONDS) {
if ((mframeNum % FRAME2GRAB) == 0) {
saveImageToArray(inputFrame);
mframeNum++;
}
else
mframeNum++;
}
else
{
mframeNum = 0;
turnOffCapture();
}
}break;
case Sample3Native.CAPT_STILL_IM :
{
saveImageToArray(inputFrame);
//RIGHT HERE IS WHERE I NEED TO MODIFY! CAPTURE IMAGE
//WITH THE CAMERA INSTEAD OF USING THE PREVIEW.
//IF I CAN DO THIS, WE CAN GET AND STITCH FULL-RES IMAGES...
//Camera.Parameters params = mCamera.getParameters();
// Mat theImage = imageCapturer.getCapturedImage(1);
// saveImageToArray(theImage);
Sample3Native.viewMode = Sample3Native.VIEW_MODE_RGBA;
}
}
return mRgba;
}
public void startVidCap() {
if (Sample3Native.viewMode == Sample3Native.VIEW_MODE_RGBA)
{
turnOnCapture();
}
else if (Sample3Native.viewMode == Sample3Native.SAVE_IMAGE_MAT)
{
turnOffCapture();
}
}
private void turnOffCapture()
{
// Button startVidCapture = (Button) findViewById(R.id.btnVidCapt);
// Stop Recording
// startVidCapture.setText("Start Video Capture");
Sample3Native.viewMode = Sample3Native.VIEW_MODE_RGBA;
}
private void turnOnCapture()
{
// Button startVidCapture = (Button) findViewById(R.id.btnVidCapt);
// Start recording
Sample3Native.viewMode = Sample3Native.SAVE_IMAGE_MAT;
// startVidCapture.setText("Stop Video Capture");
images_to_be_stitched.clear();
recordStart = new Date().getTime();
}
public void stitchImages() {
if(!images_to_be_stitched.isEmpty())
{
for (int j = 0; j < images_to_be_stitched.size(); j++) {
writeImage(images_to_be_stitched.get(j), j);
}
Log.i("stitchImages", "Done writing 2 disk. Starting stitching " + images_to_be_stitched.size() + " images");
FindFeatures(images_to_be_stitched.get(0).getNativeObjAddr(),
images_to_be_stitched.get(0).getNativeObjAddr(),
panorama.getNativeObjAddr(), images_to_be_stitched.size());
Log.i("stitchImages", "Done stitching. Writing panarama");
writePano(panorama);
Log.i("stitchImages", "deleting temp files");
deleteTmpIm();
}
}
public void captStillImage()
{
Sample3Native.viewMode = Sample3Native.CAPT_STILL_IM;
}
private String getFullFileName( int num)
{
return mImageName + num + mImageExt;
}
/* private void writeImage(List<Mat> imList) {
int counter = 0;
for (Mat im : imList) {
String fileName = "/sdcard/im" + counter + ".jpeg";
// Highgui.imwrite(s, images_to_be_stitched.get(j));
counter++;
}
}
*/
private void writeImage(Mat image, int imNum)
{
writeImage(image, getFullFileName(imNum));
}
private void writeImage(Mat image, String fileName) {
File createDir = tempImageDir;
if(!createDir.exists())
createDir.mkdir();
Highgui.imwrite(tempImageDir+File.separator + fileName, image);
}
private void writePano(Mat image)
{
Date dateNow = new Date();
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
if(!StitchImageDir.exists())
StitchImageDir.mkdir();
Highgui.imwrite(StitchImageDir.getPath()+ File.separator + "panoStich"+dateFormat.format(dateNow) +mImageExt, image);
}
private void deleteTmpIm()
{
File curFile;
for (int j = 0; j < images_to_be_stitched.size(); j++) {
curFile = new File(getFullFileName(j));
curFile.delete();
}
images_to_be_stitched.clear();
}
public void viewStitchImages()
{
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("content:" + StitchImageDir.getAbsolutePath()));//media/external/images/media"));
startActivity(intent);
}
private void saveImageToArray(Mat inputFrame) {
images_to_be_stitched.add(inputFrame.clone());
}
private int FPS() {
long curTime = new Date().getTime();
int FPS = (int) (1000 / (curTime - mPrevTime));
mPrevTime = curTime;
return FPS;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
Log.i(TAG, "called onCreateOptionsMenu");
mStitch = menu.add("Stitch Images");
mItemCaptureImage = menu.add("Capture Image");
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
Log.i(TAG, "called onOptionsItemSelected; selected item: " + item);
return true;
}
// public native void FindFeatures(List<Long> pano_images, Long stitch );
public native void FindFeatures(long image1, long image2, long image3,
int count);
}
辅助文件(在第一个文件中实例化):
package org.opencv.samples.tutorial3;
import java.io.File;
import java.io.FileOutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import org.opencv.android.JavaCameraView;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.hardware.Camera.Size;
import android.os.Environment;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.Toast;
import android.hardware.Camera.AutoFocusCallback;
public class Sample3NativeCapturer extends JavaCameraView implements PictureCallback {
public Sample3NativeCapturer(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
public Sample3NativeCapturer(Context context, int camID){
super(context,camID);
}
private static final String TAG = "Sample::Tutorial3View";
private static int MEDIA_TYPE_IMAGE = 1;
private int interpBy = 1;
private Mat theImage;
public List<String> getEffectList() {
return mCamera.getParameters().getSupportedColorEffects();
}
public boolean isEffectSupported() {
return (mCamera.getParameters().getColorEffect() != null);
}
public String getEffect() {
return mCamera.getParameters().getColorEffect();
}
public void setEffect(String effect) {
Camera.Parameters params = mCamera.getParameters();
params.setColorEffect(effect);
mCamera.setParameters(params);
}
public List<Size> getResolutionList() {
return mCamera.getParameters().getSupportedPreviewSizes();
//mCamera.getParameters().getSupportedPictureSizes()
}
public void setResolution(Size resolution) {
disconnectCamera();
mMaxHeight = resolution.height;
mMaxWidth = resolution.width;
connectCamera(getWidth(), getHeight());
}
//Set CAPTURE resolution (currently only setting preview resolution)
public void setCaptureResolution(Size resolution){
Parameters params = mCamera.getParameters();
params.setPictureSize(resolution.width,resolution.height);
mCamera.setParameters(params);
}
public Size getResolution() {
return mCamera.getParameters().getPreviewSize();
}
public void takePicture() {
Log.i(TAG, "Taking picture");
Parameters params = mCamera.getParameters();
try{
params.setJpegQuality(100);
params.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
}finally{}
mCamera.setParameters(params);
// Postview and jpeg are sent in the same buffers if the queue is not empty when performing a capture.
// Clear up buffers to avoid mCamera.takePicture to be stuck because of a memory issue
mCamera.setPreviewCallback(null);
// PictureCallback is implemented by the current class
mCamera.takePicture(null, null, this);
}
//@Override
public void onPictureTaken(byte[] data, Camera camera) {
Log.i(TAG, "Saving a bitmap to file");
// The camera preview was automatically stopped. Start it again.
mCamera.startPreview();
mCamera.setPreviewCallback(this);
// Write the image in a file (in jpeg format)
//File pictureFile = getOutputMediaFile(MEDIA_TYPE_IMAGE);
try {
//Now try to convert and save.
Parameters params = camera.getParameters();
Size sz = params.getPictureSize();
Mat raw = new Mat(1, data.length, CvType.CV_8UC1);
raw.put(0, 0, data);
theImage = Highgui.imdecode(raw, 0);
} finally {
Log.e("PictureDemo", "Exception in photoCallback");
}
}
@SuppressLint("SimpleDateFormat")
public static File getOutputMediaFile(int type){
// To be safe, you should check that the SDCard is mounted
// using Environment.getExternalStorageState() before doing this.
File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_PICTURES), "MyCameraApp");
// This location works best if you want the created images to be shared
// between applications and persist after your app has been uninstalled.
// Create the storage directory if it does not exist
if (! mediaStorageDir.exists()){
if (! mediaStorageDir.mkdirs()){
Log.d("MyCameraApp", "failed to create directory");
return null;
}
}
// Create a media file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File mediaFile;
if (type == MEDIA_TYPE_IMAGE){
mediaFile = new File(mediaStorageDir.getPath() + File.separator +
"IMG_"+ timeStamp + ".jpg");
} else {
return null;
}
return mediaFile;
}
public Mat getCapturedImage(int interpVal){
interpBy = interpVal;
takePicture();
return theImage;
}
}
经过调试,我发现问题来自于实例化行中的第二个类
private Sample3NativeCapturer imageCapturer = new Sample3NativeCapturer(getBaseContext(),1);
将上下文和整数作为其设置。如果我把它放在BaseLoaderCallback类中似乎没问题,但如果我把它放在原来的位置,它会崩溃,我甚至无法看到导致调试器崩溃的原因。也许这不是我的问题?
我想知道是否可以使用这种双功能(CameraBridgeViewBase
和Camera API),或者我应该尝试将所有内容移植到Camera API?同样,我对Android来说还是一个新手,所以如果有替代解决方案,我宁愿避免使用它。