I downloaded the project for qr and bar code scanner from git hub and facing difficulty in changing scanner from landscape mode to portrait mode and i checked many stackoverflow links but didn't get proper solution and i am using zxing 2.1 jar in my code.Here is my code please check the code and please do some helpful to solve this problem.Thanks in advance!!
Error:"java.lang.IllegalArgumentException: Crop rectangle does not fit within image data."
//BarcodeReader
package com.dynamsoft.barcodereader;
import android.app.Activity;
import android.os.Bundle;
import android.view.Display;
import android.widget.FrameLayout;
public class BarcodeReader extends Activity {
private CameraPreview mPreview;
private CameraManager mCameraManager;
private HoverView mHoverView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Display display = getWindowManager().getDefaultDisplay();
mHoverView = (HoverView)findViewById(R.id.hover_view);
mHoverView.update(display.getWidth(), display.getHeight());
mCameraManager = new CameraManager(this);
mPreview = new CameraPreview(this, mCameraManager.getCamera());
mPreview.setArea(mHoverView.getHoverLeft(), mHoverView.getHoverTop(), mHoverView.getHoverAreaWidth(), display.getWidth());
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(mPreview);
getActionBar().hide();
}
#Override
protected void onPause() {
super.onPause();
mPreview.onPause();
mCameraManager.onPause();
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mCameraManager.onResume();
mPreview.setCamera(mCameraManager.getCamera());
}
}
//CameraManager .java
package com.dynamsoft.barcodereader;
import android.content.Context;
import android.hardware.Camera;
import android.widget.Toast;
public class CameraManager {
private Camera mCamera;
private Context mContext;
public CameraManager(Context context) {
mContext = context;
// Create an instance of Camera
mCamera = getCameraInstance();
}
public Camera getCamera() {
return mCamera;
}
private void releaseCamera() {
if (mCamera != null) {
mCamera.release(); // release the camera for other applications
mCamera = null;
}
}
public void onPause() {
releaseCamera();
}
public void onResume() {
if (mCamera == null) {
mCamera = getCameraInstance();
}
Toast.makeText(
mContext,
"preview size = "
+ mCamera.getParameters().getPreviewSize().width + ", "
+ mCamera.getParameters().getPreviewSize().height,
Toast.LENGTH_LONG).show();
}
/** A safe way to get an instance of the Camera object. */
private static Camera getCameraInstance() {
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
} catch (Exception e) {
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
}
//HoverView .java
package com.dynamsoft.barcodereader;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
public class HoverView extends View {
private Paint mPaint;
private int mLeft, mTop, mRight, mBottom;
public HoverView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
mPaint = new Paint();
mPaint.setColor(Color.RED);
mPaint.setStyle(Paint.Style.STROKE);
}
public void update(int width, int height) {
int centerX = width / 2;
int centerY = height / 2;
mLeft = centerX - 200;
mRight = centerX + 200;
mTop = centerY - 200;
mBottom = centerY + 200;
invalidate();
}
public int getHoverLeft() {
return mLeft;
}
public int getHoverTop() {
return mTop;
}
public int getHoverAreaWidth() {
return mRight - mLeft;
}
#Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.drawRect(mLeft, mTop, mRight, mBottom, mPaint);
}
}
//CameraPreview.java
package com.dynamsoft.barcodereader;
import java.io.IOException;
import android.app.AlertDialog;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PreviewCallback;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.LuminanceSource;
import com.google.zxing.MultiFormatReader;
import com.google.zxing.NotFoundException;
import com.google.zxing.PlanarYUVLuminanceSource;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private static final String TAG = "camera";
private int mWidth, mHeight;
private Context mContext;
private MultiFormatReader mMultiFormatReader;
private AlertDialog mDialog;
private int mLeft, mTop, mAreaWidth, mAreaHeight;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
mContext = context;
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
Parameters params = mCamera.getParameters();
mWidth = 640;
mHeight = 480;
params.setPreviewSize(mWidth, mHeight);
mCamera.setParameters(params);
mMultiFormatReader = new MultiFormatReader();
mDialog = new AlertDialog.Builder(mContext).create();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d(TAG, "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null) {
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
}
try {
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void setCamera(Camera camera) {
mCamera = camera;
}
public void onPause() {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
}
}
private Camera.PreviewCallback mPreviewCallback = new PreviewCallback() {
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
// TODO Auto-generated method stub
if (mDialog.isShowing())
return;
LuminanceSource source = new PlanarYUVLuminanceSource(data, mWidth,
mHeight, mLeft, mTop, mAreaWidth, mAreaHeight, false);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
Result result;
try {
result = mMultiFormatReader.decode(bitmap, null);
if (result != null) {
mDialog.setTitle("Result");
mDialog.setMessage(result.getText());
mDialog.show();
}
} catch (NotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
public void setArea(int left, int top, int areaWidth, int width) {
double ratio = width / mWidth;
mLeft = (int) (left / (ratio + 1));
mTop = (int) (top / (ratio + 1));
mAreaHeight = mAreaWidth = mWidth - mLeft * 2;
}
}
It does not work on portrait mode. You need to limit your barcode activity to landscape mode.
<activity
android:name=".BarcodeReader"
android:screenOrientation="landscape" />
Related
Hi guys I was trying to make a QRCode reader so I used the used the QRCodeReaderView library provided by dlzaaro66 which provides easy implementation of Zxing library. The code is scanning the qrcode but i wanted to make sort of a reference box so as to indicate the whereabouts of where the code is being scanned from on the camera surface view I tried to use the normal draw technique. Its not giving any error but its not drawing either could you help me with where the problem might be occurring.
This my activity class.
import android.app.Activity;
import android.content.Intent;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.net.Uri;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.CompoundButton;
import android.widget.Switch;
import android.widget.Toast;
import com.dlazaro66.qrcodereaderview.QRCodeReaderView;
import com.dlazaro66.qrcodereaderview.QRCodeReaderView.OnQRCodeReadListener;
public class MyActivity extends Activity implements OnQRCodeReadListener{
QRCodeReaderView decoder;
Switch start_stop;
Paint paint;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
decoder = (QRCodeReaderView) findViewById(R.id.view2);
decoder.setOnQRCodeReadListener(this);
start_stop=(Switch) findViewById(R.id.switch1);
start_stop.setChecked(true);
start_stop.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
if(b){
decoder.getCameraManager().startPreview();
}
else{
decoder.getCameraManager().stopPreview();
}
}
});
paint= new Paint();
paint.setColor(Color.RED);
paint.setStrokeWidth(100);
paint.setAntiAlias(true);
paint.setStyle(Paint.Style.STROKE);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.my, 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 onQRCodeRead(String text, PointF[] points) {
start_stop.setChecked(false);
if(text.startsWith("http")){
Toast.makeText(getApplicationContext(),text,Toast.LENGTH_SHORT).show();
final Intent intent = new Intent(Intent.ACTION_VIEW).setData(Uri.parse(text));
startActivity(intent);
}
else{
Toast.makeText(getApplicationContext(),text,Toast.LENGTH_SHORT).show();
}
Canvas canvas=new Canvas();
for(int i=0;i<points.length-1;i++){
canvas.drawLine(points[i].x,points[i].y,points[i+1].x,points[i+1].y,paint);
}
}
#Override
public void cameraNotFound() {
}
#Override
public void QRCodeNotFoundOnCamImage() {
}
}
This is the library project class from where I am getting the methods and the custom surfaceview
public class QRCodeReaderView extends SurfaceView implements SurfaceHolder.Callback,Camera.PreviewCallback {
public interface OnQRCodeReadListener {
public void onQRCodeRead(String text, PointF[] points);
public void cameraNotFound();
public void QRCodeNotFoundOnCamImage();
}
private OnQRCodeReadListener mOnQRCodeReadListener;
private static final String TAG = QRCodeReaderView.class.getName();
private QRCodeReader mQRCodeReader;
private int mPreviewWidth;
private int mPreviewHeight;
private SurfaceHolder mHolder;
private CameraManager mCameraManager;
public QRCodeReaderView(Context context) {
super(context);
init();
}
public QRCodeReaderView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public void setOnQRCodeReadListener(OnQRCodeReadListener onQRCodeReadListener) {
mOnQRCodeReadListener = onQRCodeReadListener;
}
public CameraManager getCameraManager() {
return mCameraManager;
}
#SuppressWarnings("deprecation")
private void init() {
if (checkCameraHardware(getContext())){
mCameraManager = new CameraManager(getContext());
mHolder = this.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); // Need to set this flag despite it's deprecated
} else {
Log.e(TAG, "Error: Camera not found");
mOnQRCodeReadListener.cameraNotFound();
}
}
/****************************************************
* SurfaceHolder.Callback,Camera.PreviewCallback
****************************************************/
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
// Indicate camera, our View dimensions
mCameraManager.openDriver(holder,this.getWidth(),this.getHeight());
} catch (IOException e) {
Log.w(TAG, "Can not openDriver: "+e.getMessage());
mCameraManager.closeDriver();
}
try {
mQRCodeReader = new QRCodeReader();
mCameraManager.startPreview();
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
mCameraManager.closeDriver();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed");
mCameraManager.getCamera().setPreviewCallback(null);
mCameraManager.getCamera().stopPreview();
mCameraManager.getCamera().release();
mCameraManager.closeDriver();
}
// Called when camera take a frame
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
PlanarYUVLuminanceSource source = mCameraManager.buildLuminanceSource(data, mPreviewWidth, mPreviewHeight);
HybridBinarizer hybBin = new HybridBinarizer(source);
BinaryBitmap bitmap = new BinaryBitmap(hybBin);
try {
Result result = mQRCodeReader.decode(bitmap);
// Notify We're found a QRCode
if (mOnQRCodeReadListener != null) {
// Transform resultPoints to View coordinates
PointF[] transformedPoints = transformToViewCoordinates(result.getResultPoints());
mOnQRCodeReadListener.onQRCodeRead(result.getText(), transformedPoints);
}
} catch (ChecksumException e) {
Log.d(TAG, "ChecksumException");
e.printStackTrace();
} catch (NotFoundException e) {
// Notify QR not found
if (mOnQRCodeReadListener != null) {
mOnQRCodeReadListener.QRCodeNotFoundOnCamImage();
}
} catch (FormatException e) {
Log.d(TAG, "FormatException");
e.printStackTrace();
} finally {
mQRCodeReader.reset();
}
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "surfaceChanged");
if (mHolder.getSurface() == null){
Log.e(TAG, "Error: preview surface does not exist");
return;
}
//preview_width = width;
//preview_height = height;
mPreviewWidth = mCameraManager.getPreviewSize().x;
mPreviewHeight = mCameraManager.getPreviewSize().y;
mCameraManager.stopPreview();
mCameraManager.getCamera().setPreviewCallback(this);
mCameraManager.getCamera().setDisplayOrientation(90); // Portrait mode
mCameraManager.startPreview();
}
/**
* Transform result to surfaceView coordinates
*
* This method is needed because coordinates are given in landscape camera coordinates.
* Now is working but transform operations aren't very explained
*
* TODO re-write this method explaining each single value
*
* #return a new PointF array with transformed points
*/
private PointF[] transformToViewCoordinates(ResultPoint[] resultPoints) {
PointF[] transformedPoints = new PointF[resultPoints.length];
int index = 0;
if (resultPoints != null){
float previewX = mCameraManager.getPreviewSize().x;
float previewY = mCameraManager.getPreviewSize().y;
float scaleX = this.getWidth()/previewY;
float scaleY = this.getHeight()/previewX;
for (ResultPoint point :resultPoints){
PointF tmppoint = new PointF((previewY- point.getY())*scaleX, point.getX()*scaleY);
transformedPoints[index] = tmppoint;
index++;
}
}
return transformedPoints;
}
/** Check if this device has a camera */
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
// this device has a camera
return true;
}
else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT)){
// this device has a front camera
return true;
}
else if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_ANY)){
// this device has any camera
return true;
}
else {
// no camera on this device
return false;
}
}
}
A Surface is part of a producer-consumer buffer queue arrangement. Your application is on the producer end, and for a SurfaceView the system compositor (SurfaceFlinger) is on the consumer end.
A surface can have only one producer at a time. You've established the camera preview as the producer, so it's not possible to also connect a Canvas to perform drawing. You're not seeing failures because you're using new Canvas to create a Canvas in a vacuum -- it's not connected to anything. (Normally you'd use Surface#lockCanvas() to get the Canvas associated with the Surface.)
The surface is a completely separate layer, composited behind everything else by default, which means you can draw on top of it with a custom View. I don't think you need an additional view object though -- I believe you can do it with the 'view' part of the SurfaceView itself, which should have a transparent background. See the "custom drawing" documentation.
If you want to get fancy you can feed the camera preview to OpenGL ES, but that's probably excessive for what you need. (Some examples here.) Also, if you want to learn more about the Android graphics architecture, see this document.
I am developing a camera app and the problem is that the camera i am making shows a distorted preview.
The picture it takes is clear but the preview is messed up.
Complete camera preview code is below just in case:
import java.io.IOException;
import java.util.List;
import com.glass.cuxtomcam.constants.CuxtomIntent.CAMERA_MODE;
import android.content.Context;
import android.content.res.Configuration;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.OnZoomChangeListener;
import android.hardware.Camera.Parameters;
import android.util.Log;
import android.view.OrientationEventListener;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback, OnZoomChangeListener {
private SurfaceHolder mHolder;
private Camera mCamera;
private Context mContext;
private static String TAG = "CAMERA PREVIEW";
private int zoomOffset;
private CameraListener mCallback;
private int cameraMode;
public CameraPreview(Context context, Camera camera, int cameraMode) {
super(context);
mContext = context;
mCamera = camera;
mHolder = getHolder();
this.cameraMode = cameraMode;
mHolder.addCallback(this);
mHolder.setKeepScreenOn(true);
}
public void setCameraListener(CameraListener listener) {
mCallback = listener;
}
#Override
public synchronized void surfaceChanged(SurfaceHolder holder, int format,
int width, int height) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null) {
// preview surface does not exist
return;
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
if (mCamera != null) {
try {
mCamera.stopPreview();
if (mCamera.getParameters().isZoomSupported()) {
mCamera.setZoomChangeListener(this);
zoomOffset = mCamera.getParameters().getMaxZoom() / 5;
}
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
mCamera.lock();
} catch (IOException e) {
Log.e("Error starting preview", e.getMessage());
}
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
// release the camera
mCamera.release();
// unbind the camera from this object
mCamera = null;
}
}
public synchronized void zoomIn() {
if (mCamera != null && mCamera.getParameters().isZoomSupported()
&& mCamera.getParameters().isSmoothZoomSupported()) {
int zoomvalue = mCamera.getParameters().getZoom() + zoomOffset;
if (zoomvalue <= mCamera.getParameters().getMaxZoom()) {
mCamera.startSmoothZoom(zoomvalue);
}
} else {
Toast.makeText(mContext, "Zoom In is not supported",
Toast.LENGTH_LONG).show();
}
}
public synchronized void zoomOut() {
if (mCamera != null && mCamera.getParameters().isZoomSupported()
&& mCamera.getParameters().isSmoothZoomSupported()) {
int zoomvalue = mCamera.getParameters().getZoom() - zoomOffset;
if (zoomvalue >= 0) {
mCamera.startSmoothZoom(zoomvalue);
}
} else {
Toast.makeText(mContext, "Zoom Out is not supported",
Toast.LENGTH_LONG).show();
}
}
#Override
public void onZoomChange(int zoomValue, boolean stopped, Camera camera) {
// Log.i("Camera Zoom Value", zoomValue + "");
}
}
Can anyone guide me on why is this happening?
Not sure if it is still relevant (after 2 months without an answer). But this is a solution I used when my camera was like you showed.
Camera.Parameters parameters = camera.getParameters();
parameters.setPreviewFpsRange(30000, 30000);
parameters.setPreviewSize(640,360);
camera.setParameters(parameters);
It sets the frames per second to 30fps, the problem you are experiencing is a too high framerate (most likely)
MainActivity
public class MainActivity extends Activity implements TextureView.SurfaceTextureListener{
private Camera mCamera;
private TextureView mTextureView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(this);
setContentView(mTextureView);
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
{
mCamera = Camera.open(0);
try {
mCamera.setPreviewTexture(surface);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mCamera.startPreview();
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) {
mCamera.stopPreview();
mCamera.release();
return true;
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,
int arg2) {
// TODO Auto-generated method stub
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture arg0) {
// TODO Auto-generated method stub
}
}
The live preview is flipped to left side. On searching I found that setTransform is a solution, but I am not sure how to use it. Can anyone give me a code example?
NB: It is to flip the live preview not an image.
Follows a minimal example that uses SurfaceTexture to display front facing camera without mirroring. Note that error checking is mostly dropped for brevity. Also, in this example I do not follow the recommendation to open camera in a background thread (on some devices this may freeze the UI for too long).
import java.io.IOException;
import android.app.Activity;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.Surface;
import android.view.TextureView;
import android.widget.FrameLayout;
public class SurfaceTextureActivity extends Activity implements TextureView.SurfaceTextureListener{
private Camera mCamera;
private TextureView mTextureView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(this);
setContentView(mTextureView);
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
int cameraId = 0;
Camera.CameraInfo info = new Camera.CameraInfo();
for (cameraId = 0; cameraId < Camera.getNumberOfCameras(); cameraId++) {
Camera.getCameraInfo(1, info);
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT)
break;
}
mCamera = Camera.open(cameraId);
Matrix transform = new Matrix();
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
int rotation = getWindowManager().getDefaultDisplay()
.getRotation();
Log.i("onSurfaceTextureAvailable", " CameraOrientation(" + cameraId + ")" + info.orientation + " " + previewSize.width + "x" + previewSize.height + " Rotation=" + rotation);
switch (rotation) {
case Surface.ROTATION_0:
mCamera.setDisplayOrientation(90);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.height, previewSize.width, Gravity.CENTER));
transform.setScale(-1, 1, previewSize.height/2, 0);
break;
case Surface.ROTATION_90:
mCamera.setDisplayOrientation(0);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
transform.setScale(-1, 1, previewSize.width/2, 0);
break;
case Surface.ROTATION_180:
mCamera.setDisplayOrientation(270);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.height, previewSize.width, Gravity.CENTER));
transform.setScale(-1, 1, previewSize.height/2, 0);
break;
case Surface.ROTATION_270:
mCamera.setDisplayOrientation(180);
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
transform.setScale(-1, 1, previewSize.width/2, 0);
break;
}
try {
mCamera.setPreviewTexture(surface);
} catch (IOException t) {
}
mTextureView.setTransform(transform);
Log.i("onSurfaceTextureAvailable", "Transform: " + transform.toString());
mCamera.startPreview();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Ignored, the Camera does all the work for us
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mCamera.stopPreview();
mCamera.release();
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Update your view here!
}
}
I can display a preview of the camera video properly with a TextureView:
package com.example.camerasurfacetexture;
import java.io.IOException;
import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.widget.FrameLayout;
public class MainActivity extends Activity implements SurfaceTextureListener
{
private Camera mCamera = null;
private TextureView mTextureView = null;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mTextureView = new TextureView(this);
mTextureView.setSurfaceTextureListener(this);
setContentView(mTextureView);
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
int height)
{
Log.i("onSurfaceTextureAvailable", "onSurfaceTextureAvailable");
mCamera = Camera.open();
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
mTextureView.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
try
{
mCamera.setPreviewTexture(surface);
}
catch (IOException t)
{
}
mCamera.startPreview();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
int height)
{
// Ignored, the Camera does all the work for us
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface)
{
Log.i("onSurfaceTextureDestroyed", "onSurfaceTextureDestroyed");
mCamera.stopPreview();
mCamera.release();
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface)
{
// Update your view here!
}
}
and with a SurfaceView:
package com.example.cameratest;
import android.app.Activity;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;
public class MainActivity extends Activity
{
private SurfaceView preview = null;
private SurfaceHolder previewHolder = null;
private Camera camera = null;
private boolean inPreview = false;
private boolean cameraConfigured = false;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
preview = (SurfaceView) findViewById(R.id.cpPreview);
previewHolder = preview.getHolder();
previewHolder.addCallback(surfaceCallback);
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void onResume()
{
super.onResume();
camera = Camera.open();
startPreview();
}
#Override
public void onPause()
{
if (inPreview)
{
camera.stopPreview();
}
camera.release();
camera = null;
inPreview = false;
super.onPause();
}
private Camera.Size getBestPreviewSize(int width, int height,
Camera.Parameters parameters)
{
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPreviewSizes())
{
if (size.width <= width && size.height <= height)
{
if (result == null)
{
result = size;
}
else
{
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea > resultArea)
{
result = size;
}
}
}
}
return (result);
}
private void initPreview(int width, int height)
{
if (camera != null && previewHolder.getSurface() != null)
{
try
{
camera.setPreviewDisplay(previewHolder);
}
catch (Throwable t)
{
Log.e("PreviewDemo-surfaceCallback",
"Exception in setPreviewDisplay()", t);
Toast.makeText(MainActivity.this, t.getMessage(),
Toast.LENGTH_LONG).show();
}
if (!cameraConfigured)
{
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = getBestPreviewSize(width, height, parameters);
if (size != null)
{
parameters.setPreviewSize(size.width, size.height);
camera.setParameters(parameters);
cameraConfigured = true;
}
}
}
}
private void startPreview()
{
if (cameraConfigured && camera != null)
{
camera.startPreview();
inPreview = true;
}
}
SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback()
{
public void surfaceCreated(SurfaceHolder holder)
{
// no-op -- wait until surfaceChanged()
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
{
initPreview(width, height);
startPreview();
}
public void surfaceDestroyed(SurfaceHolder holder)
{
// no-op
}
};
}
but if I try to do both at the same time:
package com.example.multiplecamerapreviewtest;
import java.io.IOException;
import android.app.Activity;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.widget.FrameLayout;
import android.widget.Toast;
public class MainActivity extends Activity implements SurfaceTextureListener
{
private SurfaceView svPreview = null;
private SurfaceHolder previewHolder = null;
private Camera mCamera = null;
private boolean inPreview = false;
private boolean cameraConfigured = false;
private TextureView tvPreview = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
svPreview = (SurfaceView) findViewById(R.id.svPreview);
previewHolder = svPreview.getHolder();
previewHolder.addCallback(surfaceCallback);
previewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// mTextureView = new TextureView(this);
tvPreview = (TextureView) findViewById(R.id.tvPreview);
tvPreview.setSurfaceTextureListener(this);
// setContentView(mTextureView);
}
#Override
public void onResume()
{
super.onResume();
mCamera = Camera.open();
startPreview();
}
#Override
public void onPause()
{
if (inPreview)
{
mCamera.stopPreview();
}
mCamera.release();
mCamera = null;
inPreview = false;
super.onPause();
}
private Camera.Size getBestPreviewSize(int width, int height,
Camera.Parameters parameters)
{
Camera.Size result = null;
for (Camera.Size size : parameters.getSupportedPreviewSizes())
{
if (size.width <= width && size.height <= height)
{
if (result == null)
{
result = size;
}
else
{
int resultArea = result.width * result.height;
int newArea = size.width * size.height;
if (newArea > resultArea)
{
result = size;
}
}
}
}
return (result);
}
private void initPreview(int width, int height)
{
if (mCamera != null && previewHolder.getSurface() != null)
{
try
{
mCamera.setPreviewDisplay(previewHolder);
}
catch (Throwable t)
{
Log.e("PreviewDemo-surfaceCallback",
"Exception in setPreviewDisplay()", t);
Toast.makeText(MainActivity.this, t.getMessage(),
Toast.LENGTH_LONG).show();
}
if (!cameraConfigured)
{
Camera.Parameters parameters = mCamera.getParameters();
Camera.Size size = getBestPreviewSize(width, height, parameters);
if (size != null)
{
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
cameraConfigured = true;
}
}
}
}
private void startPreview()
{
if (cameraConfigured && mCamera != null)
{
mCamera.startPreview();
inPreview = true;
}
}
SurfaceHolder.Callback surfaceCallback = new SurfaceHolder.Callback()
{
public void surfaceCreated(SurfaceHolder holder)
{
// no-op -- wait until surfaceChanged()
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height)
{
initPreview(width, height);
startPreview();
}
public void surfaceDestroyed(SurfaceHolder holder)
{
// no-op
}
};
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
int height)
{
Log.i("onSurfaceTextureAvailable", "onSurfaceTextureAvailable");
mCamera = Camera.open();
Camera.Size previewSize = mCamera.getParameters().getPreviewSize();
tvPreview.setLayoutParams(new FrameLayout.LayoutParams(
previewSize.width, previewSize.height, Gravity.CENTER));
try
{
mCamera.setPreviewTexture(surface);
}
catch (IOException t)
{
}
mCamera.startPreview();
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width,
int height)
{
// Ignored, the Camera does all the work for us
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface)
{
Log.i("onSurfaceTextureDestroyed", "onSurfaceTextureDestroyed");
mCamera.stopPreview();
mCamera.release();
return true;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface)
{
// Update your view here!
}
}
I get a "Fail to connect to camera service" exception when calling mCamera.setPreviewTexture().
According to the documentation (http://developer.android.com/reference/android/hardware/Camera.html#setPreviewDisplay%28android.view.SurfaceHolder%29), this is the expected behavior:
setPreviewDisplay()
Setting a preview surface will un-set any preview surface texture that was set via setPreviewTexture(SurfaceTexture).
(and vice-versa). Is there anything I can do to get these both displaying at the same time?
Send the Camera output to a SurfaceTexture, then render the texture wherever you like using GLES.
See e.g. the "texture from camera" activity in Grafika.
This requires some EGL management and a basic familiarity with GLES, though you can get pretty far just using the code in Grafika. This approach will get you much better performance than manipulating pixels with the NDK, because the GPU does all the work.
Camera is a shared hardware resource. I don't think you can access it (Camera.open()) more than once even within the same application i.e even within the same process with same user id. How about accessing the buffers for camera preview natively and then reading bytes from those buffers to render them on as many TextureViews or SurfaceViews or VideoViews as you wish?
Edited:
NDK Resource:
How do I get the raw Android camera buffer in C using JNI?
I am using:
Android 4.0.3
OpenCV 2.4.2
Samsung Galaxy S2
The face-detection example (from the opencv 2.4.2) is working perfectly.
But now, I would like to create a custom layout and actually work with just the data extracted from face detection and build a game on it. Not necessarily having the FdView surface taking the entire screen.
I have done these modifications below, but just a black screen is displayed. Nothing appears on the screen.
Added a fd.xml layout:
<LinearLayout 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:orientation="horizontal">
<org.opencv.samples.fd.FdView android:id="#+id/FdView"
android:layout_width="640dp"
android:layout_height="480dp"
android:visibility="visible"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#FF0000"
android:text="hi"/>
Modified the baseLoaderCallback of FdActivity.java:
private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
#Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
Log.i(TAG, "OpenCV loaded successfully");
// Load native libs after OpenCV initialization
System.loadLibrary("detection_based_tracker");
//EXPERIMENT
setContentView(R.layout.fd);
FdView surface = (FdView) (findViewById(R.id.FdView));
surface = mView;
// Create and set View
mView = new FdView(mAppContext);
mView.setDetectorType(mDetectorType);
mView.setMinFaceSize(0.2f);
//setContentView(mView);
// Check native OpenCV camera
if( !mView.openCamera() ) {
AlertDialog ad = new AlertDialog.Builder(mAppContext).create();
ad.setCancelable(false); // This blocks the 'BACK' button
ad.setMessage("Fatal error: can't open camera!");
ad.setButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
finish();
}
});
ad.show();
}
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
Added constructors in FdView.java:
public FdView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public FdView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
Added constructors in SampleCvViewBase.java:
public SampleCvViewBase(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated constructor stub
}
public SampleCvViewBase(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
I have precisely the same issue. Also trying to figure it out. I'm trying to display the image on a SurfaceView that doesn't take the whole screen. And with that I read that you can't have your Camera handler class and linked SurfaceView in different classes. So smashed everything into one.
So, at the moment I have the camera displaying on the SurfaceView, and copying the frame data to a mFrame variable. Basically just struggling to get the mFrame processed (in the multi-threading, Run(), function) and showing the result on the SurfaceView.
This is the code I have, if you think it would help: (excuse the formatting as my code is also a work in progress)
package org.opencv.samples.tutorial3;
import java.io.IOException;
import java.util.List;
import org.opencv.android.BaseLoaderCallback;
import org.opencv.android.LoaderCallbackInterface;
import org.opencv.android.OpenCVLoader;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ImageFormat;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.hardware.Camera;
import android.hardware.Camera.PreviewCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.Window;
import android.widget.TextView;
public class Sample3Native extends Activity implements SurfaceHolder.Callback,Runnable{
//Camera variables
private Camera cam;
private boolean previewing = false;
private SurfaceHolder mHolder;
private SurfaceView mViewer;
private int mFrameWidth;
private int mFrameHeight;
private byte[] mFrame;
private boolean mThreadRun;
private byte[] mBuffer;
Sample3View viewclass;
TextView text;
int value = 0;
//==========
int framecount = 0;
private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
#Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS:
{
// Load native library after(!) OpenCV initialization
System.loadLibrary("native_sample");
//constructor for viewclass that works on frames
viewclass = new Sample3View();
//setContentView(mView);
//OpenCam();
//setContentView(R.layout.main);
// Create and set View
CameraConstruct();
Camopen();
} break;
default:
{
super.onManagerConnected(status);
} break;
}
}
};
public Sample3Native()
{}
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.main);
OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mOpenCVCallBack);
}
//Camera construction
public void CameraConstruct()
{
mViewer = (SurfaceView)findViewById(R.id.camera_view);
text = (TextView)findViewById(R.id.text);
mHolder = mViewer.getHolder();
mHolder.addCallback(this);
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
//calls camera screen setup when screen surface changes
public void surfaceChanged(SurfaceHolder holder, int format, int width,int height)
{
CamStartDisplay();
}
public void Camclose()
{
if(cam != null && previewing)
{
cam.setPreviewCallback(null);
cam.stopPreview();
cam.release();
cam = null;
previewing = false;
}
mThreadRun = false;
viewclass.PreviewStopped();
}
//only open camera, and get frame data
public void Camopen()
{
if(!previewing){
cam = Camera.open();
//rotate display
cam.setDisplayOrientation(90);
if (cam != null)
{
//copy viewed frame
cam.setPreviewCallbackWithBuffer(new PreviewCallback()
{
public void onPreviewFrame(byte[] data, Camera camera)
{
synchronized (this)
{
System.arraycopy(data, 0, mFrame, 0, data.length);
this.notify();
}
//text.setText(Integer.toString(value++));
camera.addCallbackBuffer(mBuffer);
}
});
}
}//if not previewing
}
//start preview
public void CamStartDisplay()
{
synchronized (this)
{
if(cam != null)
{
//stop previewing till after settings is changed
if(previewing == true)
{
cam.stopPreview();
previewing = false;
}
Camera.Parameters p = cam.getParameters();
for(Camera.Size s : p.getSupportedPreviewSizes())
{
p.setPreviewSize(s.width, s.height);
mFrameWidth = s.width;
mFrameHeight = s.height;
break;
}
p.setPreviewSize(mFrameWidth, mFrameHeight);
List<String> FocusModes = p.getSupportedFocusModes();
if (FocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
{
p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
cam.setParameters(p);
//set the width and height for processing
viewclass.setFrame(mFrameWidth, mFrameHeight);
int size = mFrameWidth*mFrameHeight;
size = size * ImageFormat.getBitsPerPixel(p.getPreviewFormat()) / 8;
mBuffer = new byte[size];
mFrame = new byte [size];
cam.addCallbackBuffer(mBuffer);
viewclass.PreviewStarted(mFrameWidth, mFrameHeight);
//start display streaming
try
{
//cam.setPreviewDisplay(null);
cam.setPreviewDisplay(mHolder);
cam.startPreview();
previewing = true;
}
catch (IOException e)
{
e.printStackTrace();
}
}//end of if cam != null
}//synchronising
}
//thread gets started when the screen surface is created
public void surfaceCreated(SurfaceHolder holder) {
//Camopen();
//CamStartDisplay();
(new Thread(this)).start();
}
//called when the screen surface is stopped
public void surfaceDestroyed(SurfaceHolder holder)
{
Camclose();
}
//this is function that is run by thread
public void run()
{
mThreadRun = true;
while (mThreadRun)
{
//text.setText(Integer.toString(value++));
Bitmap bmp = null;
synchronized (this)
{
try
{
this.wait();
bmp = viewclass.processFrame(mFrame);
}
catch (InterruptedException e) {}
}
if (bmp != null)
{
Canvas canvas = mHolder.lockCanvas();
if (canvas != null)
{
canvas.drawBitmap(bmp, (canvas.getWidth() - mFrameWidth) / 2, (canvas.getHeight() - mFrameHeight) / 2, null);
mHolder.unlockCanvasAndPost(canvas);
}
}//if bmp != null
}// while thread in run
}
}//end class
Sample3View as used in this class just includes the processFrame function as such:
package org.opencv.samples.tutorial3;
import android.content.Context;
import android.graphics.Bitmap;
import android.widget.TextView;
class Sample3View {
private int mFrameSize;
private Bitmap mBitmap;
private int[] mRGBA;
private int frameWidth;
private int frameHeight;
private int count = 0;
Sample3Native samp;
//constructor
public Sample3View()
{
}
public void setFrame(int width,int height)
{
frameWidth = width;
frameHeight = height;
}
public void PreviewStarted(int previewWidtd, int previewHeight) {
mFrameSize = previewWidtd * previewHeight;
mRGBA = new int[mFrameSize];
mBitmap = Bitmap.createBitmap(previewWidtd, previewHeight, Bitmap.Config.ARGB_8888);
}
public void PreviewStopped() {
if(mBitmap != null) {
mBitmap.recycle();
mBitmap = null;
}
mRGBA = null;
}
public Bitmap processFrame(byte[] data) {
int[] rgba = mRGBA;
FindFeatures(frameWidth, frameHeight, data, rgba);
Bitmap bmp = mBitmap;
bmp.setPixels(rgba, 0, frameWidth, 0, 0, frameWidth, frameHeight);
//samp.setValue(count++);
return bmp;
}
public native void FindFeatures(int width, int height, byte yuv[], int[] rgba);
}
So yeah, hope this helps. If I get the complete solution working, I'll post that also. Also post your stuff if you get the solution first please! Enjoy
Sry not a real answer (yet) but also tried to make a custom layout in opencv 2.4.2
i have this perfectly working solution for 2.4.0 if i remember it right it was enough to add the instructors.. but it doesn't work with 2.4.2
i'll try to figure smthg out and let you guys know.
I met the same problem that I wanted to create a custom view using layout. OpenCV 2.4.2 seems not to offer this function.
OpenCV 2.4.3 has the function, but its tutorial doesn't say so (it uses the old example from OpenCV2.4.2). Its Android samples provide some insights. Finally I found the instruction in OpenCV 2.4.9 documentation.
Hope it helps.
Hah, I figured out one way. You could just simply separate the OpenCV Loader and the custom layout.
Define BaseLoaderCallback mOpenCVCallBack.
private BaseLoaderCallback mOpenCVCallBack = new BaseLoaderCallback(this) {
#Override
public void onManagerConnected(int status) {
switch (status) {
case LoaderCallbackInterface.SUCCESS: {
Log.i(TAG, "OpenCV loaded successfully");
// Load native library after(!) OpenCV initialization
System.loadLibrary("native_sample");
}
break;
default: {
super.onManagerConnected(status);
}
break;
}
}
};
In OnCreat, build your custom layout, load the OpenCv Loader,
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "onCreate");
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
// /////////////////////////////////////////////////////////////////////
// // begin:
// // Create and set View
setContentView(R.layout.main);
mView = (Sample3View) findViewById(R.id.sample3view);
mcameraButton = (ImageView) findViewById(R.id.cameraButton);
if (!OpenCVLoader.initAsync(OpenCVLoader.OPENCV_VERSION_2_4_2, this, mOpenCVCallBack)) {
Log.e(TAG, "Cannot connect to OpenCV Manager");
}
}
Just that!
I did that, and it worked very well.