Camera preview slowing down on re-adding callback buffer in onPreviewFrame() - android

I'm trying to capture frames from a camera preview so that I can do some image processing on the frames in the background while the user sees the camera preview.
For that, I'm adding 60 buffers initially using addCallbackBuffer() in surfaceChanged() method of the SurfaceView and then on each onPreviewFrame() call, I'm re-adding the used buffer.
The problem is re-adding the buffer in onPreviewFrame() slows down the preview.
I'm also counting the number of calls to onPreviewFrame() every second. In the first second, I'm getting more than 70 calls to onPreviewFrame() which decreases to less than 25 in the second second and later.
Here is the code
public class MySurfaceView extends SurfaceView implements
SurfaceHolder.Callback, Camera.PreviewCallback {
private static final int BUFFER_COUNT = 60;
private SurfaceHolder mHolder;
private Camera mCamera;
private boolean isPreviewRunning;
private final FPSCounter fpscounter = new FPSCounter();
private int frameWidth, frameHeight;
private byte[] prevFrameByteArr, currFrameByteArr;
public MySurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
mHolder = getHolder();
mHolder.addCallback(this);
}
public byte[] getPrevFrameByteArray() {
return prevFrameByteArr;
}
public byte[] getCurrFrameByteArray() {
return currFrameByteArr;
}
public int getFrameRate() {
return fpscounter.getLastFrameCount();
}
public int getFrameWidth() {
return frameWidth;
}
public int getFrameHeight() {
return frameHeight;
}
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
synchronized (this) {
prevFrameByteArr = currFrameByteArr;
currFrameByteArr = data;
}
mCamera.addCallbackBuffer(data);
fpscounter.logFrame();
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
synchronized (this) {
if (isPreviewRunning)
mCamera.stopPreview();
Camera.Parameters parameters = mCamera.getParameters();
parameters.setRecordingHint(true);
parameters.setPreviewFormat(ImageFormat.NV21);
/* To get better frame rate, get the least resolution that matches the current aspect ratio */
List<Size> sizes = parameters.getSupportedPreviewSizes();
Size currPreviewSize = parameters.getPreviewSize();
float ar = (float) (Math.floor(((float) currPreviewSize.width / currPreviewSize.height) * 10) / 10);
for (Size s : sizes) {
int w = s.width, h = s.height;
float resAr = (float) (Math.floor(((float) w / h) * 10) / 10);
if (ar == resAr) {
this.frameWidth = w;
this.frameHeight = h;
parameters.setPreviewSize(w, h);
currPreviewSize = s;
for (int i = 0; i < BUFFER_COUNT; i++) {
byte[] buffer = new byte[w * h *
ImageFormat.getBitsPerPixel(ImageFormat.NV21) / 8];
mCamera.addCallbackBuffer(buffer);
}
break;
}
}
mCamera.setParameters(parameters);
try {
mCamera.setPreviewDisplay(holder);
mCamera.setPreviewCallbackWithBuffer(this);
mCamera.startPreview();
isPreviewRunning = true;
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
synchronized (this) {
setWillNotDraw(true);
mCamera = Camera.open();
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
synchronized (this) {
try {
if (mCamera != null) {
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
isPreviewRunning = false;
}
} catch (Exception e) {
Log.e("cam error", e.getMessage());
}
}
}
}
and the FPSCounter class
private long startTime;
private int frames, lastFrameCount;
public void logFrame() {
frames++;
if (System.nanoTime() - startTime >= 1000000000) {
lastFrameCount = frames;
frames = 0;
startTime = System.nanoTime();
}
}
public int getLastFrameCount() {
return lastFrameCount;
}
Anybody know how this could be resolved?

I have not seen Android devices that reliably deliver more than 30 FPS. But one caveat that may cause slowdown is when onPreviewFrame() arrives on the main (UI) thread, and thus competes for time with UI events like touch, layout, or even rendering. Please see how you can painlessly offload the preview callbacks to secondary thread: https://stackoverflow.com/a/19154438/192373.
At any rate, pre-allocation of 60 buffers smells wrong. If you capture preview frames for more than a second, you must process and recycle the frames in real time. So, 3 buffers should be enough: one is processed by your program, one is free and can be locked by the camera at any moment, and one is locked by the camera and receives the current frame.

Related

How can I set the custom brightness adjusment in (JavaCameraView) CameraBridgeViewBase?

I want to implement brightness adjustment in an app that uses that uses OpenCV camera. I have found methods and I have found in the documentation of JavaCameraView and CameraViewBridge Base if exists some method to do it directly but I have not found.
Is there a way to do this?
Follow those steps that will help you adjust the exposure of the camera
First create a CustomCameraView java class
public class CustomCameraView extends JavaCameraView implements Camera.PictureCallback {
private String mPictureFileName;
public CustomCameraView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public List<String> getEffectList() {
return mCamera.getParameters().getSupportedFlashModes();
}
public boolean isEffectSupported() {
return (mCamera.getParameters().getFlashMode() != null);
}
public String getEffect() {
return mCamera.getParameters().getFlashMode();
}
public void setEffect(String effect) {
mCamera.getParameters();
Camera.Parameters params = mCamera.getParameters();
params.setFlashMode(effect);
mCamera.setParameters(params);
}
// set Camera Exposure value from input progress (0.0f - 1.0f)
public void setExposure(float progress) {
if (progress < 0.0f && progress > 1.0f) return;
Camera.Parameters params = mCamera.getParameters();
int min = params.getMinExposureCompensation();
int max = params.getMaxExposureCompensation();
float realProgress = progress - 0.5f;
int value;
if (realProgress < 0) {
value = -(int) (realProgress * 2 * min);
} else {
value = (int) (realProgress * 2 * max);
}
// if changed
if (value != params.getExposureCompensation()) {
params.setExposureCompensation(value);
mCamera.setParameters(params);
}
}
public void setPreviewFPS(double min, double max) {
Camera.Parameters params = mCamera.getParameters();
params.setPreviewFpsRange((int) (min * 1000), (int) (max * 1000));
mCamera.setParameters(params);
}
public List<Camera.Size> getResolutionList() {
return mCamera.getParameters().getSupportedPreviewSizes();
}
public void setResolution(int w, int h) {
disconnectCamera();
mMaxHeight = h;
mMaxWidth = w;
connectCamera(getWidth(), getHeight());
}
public Camera.Size getResolution() {
return mCamera.getParameters().getPreviewSize();
}
public void takePicture(final String fileName) {
this.mPictureFileName = fileName;
// 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) {
// The camera preview was automatically stopped. Start it again.
mCamera.startPreview();
mCamera.setPreviewCallback(this);
// Write the image in a file (in jpeg format)
try {
FileOutputStream fos = new FileOutputStream(mPictureFileName);
fos.write(data);
fos.close();
} catch (java.io.IOException e) {
Log.e("PictureDemo", "Exception in photoCallback", e);
}
}
public void cameraRelease() {
if (mCamera != null) {
mCamera.release();
}
}
}
After that in the layout file of the activity where you declared the JavaCameraView , change that with this
<your.package.CustomCameraView
android:id="#+id/cameraViewer"
android:layout_width="380dp"
android:layout_height="450dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:visibility="gone"
opencv:show_fps="false" />
NOTE:change your.package with the appropriate package name
Finally in the activity add this
cameraBridgeViewBase = (CustomCameraView) findViewById(R.id.cameraViewer);
cameraBridgeViewBase.setExposure(1.0f);
NOTE: change this value 1.0f to your desired value.
I hope this helps you.

Media Recorder with Google Vision API

I am using the FaceTracker sample from the Android vision API. However, I am experiencing difficulty in recording videos while the overlays are drawn on them.
One way is to store bitmaps as images and process them using FFmpeg or Xuggler to merge them as videos, but I am wondering if there is a better solution to this problem if we can record video at runtime as the preview is projected.
Update 1:
I updated the following class with media recorder, but the recording is still not working. It is throwing the following error when I call triggerRecording() function:
MediaRecorder: start called in an invalid state: 4
and I have external storage permission in the Manifest file.
Update 2:
I have fixed the above issue in the code and moved the setupMediaRecorder() in the onSurfaceCreated callback. However, when I stop recording it throws the runtime-exception. According to the documentation if there is no video/audio data Runtime exception will be thrown.
So, what am I missing here?
public class CameraSourcePreview extends ViewGroup {
private static final String TAG = "CameraSourcePreview";
private static final SparseIntArray ORIENTATIONS = new SparseIntArray();
static {
ORIENTATIONS.append(Surface.ROTATION_0, 90);
ORIENTATIONS.append(Surface.ROTATION_90, 0);
ORIENTATIONS.append(Surface.ROTATION_180, 270);
ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
private MediaRecorder mMediaRecorder;
/**
* Whether the app is recording video now
*/
private boolean mIsRecordingVideo;
private Context mContext;
private SurfaceView mSurfaceView;
private boolean mStartRequested;
private boolean mSurfaceAvailable;
private CameraSource mCameraSource;
private GraphicOverlay mOverlay;
public CameraSourcePreview(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
mStartRequested = false;
mSurfaceAvailable = false;
mSurfaceView = new SurfaceView(context);
mSurfaceView.getHolder().addCallback(new SurfaceCallback());
addView(mSurfaceView);
mMediaRecorder = new MediaRecorder();
}
private void setUpMediaRecorder() throws IOException {
mMediaRecorder.setPreviewDisplay(mSurfaceView.getHolder().getSurface());
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setOutputFile(Environment.getExternalStorageDirectory() + File.separator + Environment.DIRECTORY_DCIM + File.separator + System.currentTimeMillis() + ".mp4");
mMediaRecorder.setVideoEncodingBitRate(10000000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(480, 640);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
//int rotation = mContext.getWindowManager().getDefaultDisplay().getRotation();
//int orientation = ORIENTATIONS.get(rotation);
mMediaRecorder.setOrientationHint(ORIENTATIONS.get(0));
mMediaRecorder.prepare();
mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
#Override
public void onError(MediaRecorder mr, int what, int extra) {
Timber.d(mr.toString() + " : what[" + what + "]" + " Extras[" + extra + "]");
}
});
}
public void start(CameraSource cameraSource) throws IOException {
if (cameraSource == null) {
stop();
}
mCameraSource = cameraSource;
if (mCameraSource != null) {
mStartRequested = true;
startIfReady();
}
}
public void start(CameraSource cameraSource, GraphicOverlay overlay) throws IOException {
mOverlay = overlay;
start(cameraSource);
}
public void stop() {
if (mCameraSource != null) {
mCameraSource.stop();
}
}
public void release() {
if (mCameraSource != null) {
mCameraSource.release();
mCameraSource = null;
}
}
private void startIfReady() throws IOException {
if (mStartRequested && mSurfaceAvailable) {
mCameraSource.start(mSurfaceView.getHolder());
if (mOverlay != null) {
Size size = mCameraSource.getPreviewSize();
int min = Math.min(size.getWidth(), size.getHeight());
int max = Math.max(size.getWidth(), size.getHeight());
if (isPortraitMode()) {
// Swap width and height sizes when in portrait, since it will be rotated by
// 90 degrees
mOverlay.setCameraInfo(min, max, mCameraSource.getCameraFacing());
} else {
mOverlay.setCameraInfo(max, min, mCameraSource.getCameraFacing());
}
mOverlay.clear();
}
mStartRequested = false;
}
}
private class SurfaceCallback implements SurfaceHolder.Callback {
#Override
public void surfaceCreated(SurfaceHolder surface) {
mSurfaceAvailable = true;
surface.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
// setup the media recorder
try {
setUpMediaRecorder();
} catch (IOException e) {
e.printStackTrace();
}
try {
startIfReady();
} catch (IOException e) {
Timber.e(TAG, "Could not start camera source.", e);
}
}
#Override
public void surfaceDestroyed(SurfaceHolder surface) {
mSurfaceAvailable = false;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int width = 320;
int height = 240;
if (mCameraSource != null) {
Size size = mCameraSource.getPreviewSize();
if (size != null) {
width = size.getWidth();
height = size.getHeight();
}
}
// Swap width and height sizes when in portrait, since it will be rotated 90 degrees
if (isPortraitMode()) {
int tmp = width;
width = height;
height = tmp;
}
final int layoutWidth = right - left;
final int layoutHeight = bottom - top;
// Computes height and width for potentially doing fit width.
int childWidth = layoutWidth;
int childHeight = (int) (((float) layoutWidth / (float) width) * height);
// If height is too tall using fit width, does fit height instead.
if (childHeight > layoutHeight) {
childHeight = layoutHeight;
childWidth = (int) (((float) layoutHeight / (float) height) * width);
}
for (int i = 0; i < getChildCount(); ++i) {
getChildAt(i).layout(0, 0, childWidth, childHeight);
}
try {
startIfReady();
} catch (IOException e) {
Timber.e(TAG, "Could not start camera source.", e);
}
}
private boolean isPortraitMode() {
int orientation = mContext.getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
return false;
}
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
return true;
}
Timber.d(TAG, "isPortraitMode returning false by default");
return false;
}
private void startRecordingVideo() {
try {
// Start recording
mMediaRecorder.start();
mIsRecordingVideo = true;
} catch (IllegalStateException e) {
e.printStackTrace();
}
}
private void stopRecordingVideo() {
// UI
mIsRecordingVideo = false;
// Stop recording
mMediaRecorder.stop();
mMediaRecorder.reset();
}
public void triggerRecording() {
if (mIsRecordingVideo) {
stopRecordingVideo();
Timber.d("Recording stopped");
} else {
startRecordingVideo();
Timber.d("Recording starting");
}
}
}
Solution 1: From Android Lollipop, a MediaProjection API was introduced which in conjunction with MediaRecorder can be used to save a SurfaceView to a video file. This example shows how to output a SurfaceView to a video file.
Solution 2: Alternatively, you can use one of the neat Encoder classes provided in the Grafika repository. Note that this will require you to port the FaceTracker application so that it is using OpenGL to perform all rendering. This is because Grafika samples utilise the OpenGL pipeline for fast read and write of texture data.
There is a minimal example which achieves exactly what you want using a CircularEncoder in the ContinuousCaptureActivity class. This provides an example of Frame Blitting, simultaneously displaying frame buffer data to the screen and outputting to a video.
The major change would be to utilise a Grafika WindowSurface instead of a SurfaceView for the FaceTracker application, this sets up the EGL Context allowing you to save frame buffer data to a file via the Encoder. Once you can render everything to the WindowSurface, it is trivial to set up recording in the same way as the ContinuousCaptureActivity class.

Why continuous auto focusing camera with handler dont allow to toggle camera flash?

What I have done so far:
I have implemented custom camera for reading qr code which need to continue focus the camera for better qr reading.
My problem is when I use to focus in every one second with the handler the camera flash on\off button dont works or it takes too much time to turning on and off the camera flash light. Every thing works fine when I remove the code of auto focusing the camera every second (The runnable and the handler).
What I want is to focus automatically and quickly whenever camera moves and also able to turn on and off the flash on demand quickly without using Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE because its not available for API<14.
I have used Camera.Parameters.FOCUS_MODE_AUTO but its only focusing the camera once when started thats why i used handler to focus camera every second.
Min SDK Version of project is 9.
My Camera Activity is
public class CameraActivityNew extends Activity implements OnClickListener,
Camera.PreviewCallback {
CameraPreviewNew mPreview;
FrameLayout flCameraPreview;
ImageButton ibFlashButton;
Boolean isFlashOn = false;
Camera mCamera;
private Handler mAutoFocusHandler;
private boolean mPreviewing = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
mAutoFocusHandler = new Handler();
setContentView(R.layout.activity_camera);
findSetupViews();
mPreview = new CameraPreviewNew(getApplicationContext(), this,
autoFocusCB);
flCameraPreview.addView(mPreview);
}
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (mCamera != null && mPreviewing) {
mCamera.autoFocus(autoFocusCB);
}
}
};
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
#Override
protected void onResume() {
super.onResume();
try {
mCamera = Camera.open();
if (mCamera == null) {
return;
}
} catch (Exception e) {
e.printStackTrace();
return;
}
mPreview.setCamera(mCamera);
mPreview.showSurfaceView();
mPreviewing = true;
}
#Override
protected void onPause() {
super.onPause();
if (mCamera != null) {
mPreview.setCamera(null);
mCamera.cancelAutoFocus();
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
mCamera.release();
mPreview.hideSurfaceView();
mPreviewing = false;
mCamera = null;
}
}
private void findSetupViews() {
flCameraPreview = (FrameLayout) findViewById(R.id.flCameraPreview);
ibFlashButton = (ImageButton) findViewById(R.id.ibFlash);
ibFlashButton.setOnClickListener(this);
if (getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA_FLASH)) {
ibFlashButton.setVisibility(View.VISIBLE);
ibFlashButton.setOnClickListener(this);
} else {
ibFlashButton.setVisibility(View.GONE);
}
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.ibFlash:
if (isFlashOn) {
mPreview.setCameraFlashLight(false);
isFlashOn = false;
ibFlashButton.setImageResource(R.drawable.flashoff);
} else {
mPreview.setCameraFlashLight(true);
ibFlashButton.setImageResource(R.drawable.flashon);
isFlashOn = true;
}
break;
}
}
#Override
public void onPreviewFrame(final byte[] data, final Camera camera) {
// processed here qr code and works fine if camera focus
//now removed to narrow the code for posting the question
}
}
And the Camera Preview class is:
public class CameraPreviewNew extends ViewGroup implements Callback {
public static final int CAMERA_BACK = 0;
public static final int CAMERA_FRONT = 1;
public Camera mCamera = null;
private Context context = null;
SurfaceView mSurfaceView;
SurfaceHolder mSurfaceHolder;
Size mPreviewSize;
List<Size> mSupportedPreviewSizes;
PreviewCallback mPreviewCallback;
AutoFocusCallback mAutoFocusCallback;
public CameraPreviewNew(Context context,
PreviewCallback previewCallback, AutoFocusCallback autoFocusCb) {
super(context);
mPreviewCallback = previewCallback;
mAutoFocusCallback = autoFocusCb;
this.context = context;
mSurfaceView = new SurfaceView(context);
addView(mSurfaceView);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
}
public void setCamera(Camera camera) {
mCamera = camera;
if (mCamera != null) {
mSupportedPreviewSizes = mCamera.getParameters()
.getSupportedPreviewSizes();
requestLayout();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int width = resolveSize(getSuggestedMinimumWidth(),
widthMeasureSpec);
final int height = resolveSize(getSuggestedMinimumHeight(),
heightMeasureSpec);
setMeasuredDimension(width, height);
}
public void hideSurfaceView() {
mSurfaceView.setVisibility(View.INVISIBLE);
}
public void showSurfaceView() {
mSurfaceView.setVisibility(View.VISIBLE);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
if (mCamera != null) {
mCamera.setPreviewDisplay(holder);
}
} catch (IOException exception) {
Log.e("logtag", "IOException caused by setPreviewDisplay()",
exception);
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.cancelAutoFocus();
mCamera.stopPreview();
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (holder.getSurface() == null) {
return;
}
if (mCamera != null) {
Camera.Parameters parameters = mCamera.getParameters();
mPreviewSize = getBestPreviewSize(mCamera.getParameters(), w, h);
parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
requestLayout();
mCamera.setParameters(parameters);
mCamera.setPreviewCallback(mPreviewCallback);
mCamera.startPreview();
mCamera.autoFocus(mAutoFocusCallback);
setCameraDisplayOrientation(0);
}
}
private void setCameraDisplayOrientation(int cameraId) {
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = ((WindowManager) context
.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
mCamera.setDisplayOrientation(result);
}
protected static Comparator<Size> newSizeComparator() {
return new Comparator<Size>() {
#Override
public int compare(Size lhs, Size rhs) {
return Integer.valueOf(rhs.height * rhs.width).compareTo(
lhs.height * lhs.width);
}
};
}
private Size getBestPreviewSize(Parameters parameters, int screenWidth,
int screenHeight) {
List<Size> supportedSizes = parameters.getSupportedPreviewSizes();
Collections.sort(supportedSizes, newSizeComparator());
int previewHeight = screenHeight;
int previewWidth = screenWidth;
if (previewHeight > previewWidth) {
int swap = previewWidth;
previewWidth = previewHeight;
previewHeight = swap;
}
Size bestSize = null;
float bestRatio = 999;
for (Size s : supportedSizes) {
if (s.height > s.width) {
int swap = s.width;
s.width = s.height;
s.height = swap;
}
float cameraRatio = ((float) s.height / (float) s.width);
float screenRatio = ((float) previewHeight)
/ ((float) previewWidth);
if ((s.height >= previewHeight) && (s.width >= previewWidth)) {
float ratioDiff = cameraRatio - screenRatio;
if ((ratioDiff < 0.19) && (ratioDiff > -0.19)
&& (Math.abs(bestRatio) > Math.abs(ratioDiff))) {
bestSize = s;
bestRatio = ratioDiff;
}
}
}
return bestSize;
}
public void setCameraFlashLight(Boolean setFlash) {
Parameters _parameters = mCamera.getParameters();
if (setFlash) {
_parameters.setFlashMode(Parameters.FLASH_MODE_TORCH);
} else {
_parameters.setFlashMode(Parameters.FLASH_MODE_OFF);
}
mCamera.setParameters(_parameters);
mCamera.startPreview();
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
if (changed && getChildCount() > 0) {
final View child = getChildAt(0);
final int width = r - l;
final int height = b - t;
int previewWidth = width;
int previewHeight = height;
if (mPreviewSize != null) {
previewWidth = mPreviewSize.width;
previewHeight = mPreviewSize.height;
}
if (width * previewHeight > height * previewWidth) {
final int scaledChildWidth = previewWidth * height
/ previewHeight;
child.layout((width - scaledChildWidth) / 2, 0,
(width + scaledChildWidth) / 2, height);
} else {
final int scaledChildHeight = previewHeight * width
/ previewWidth;
child.layout(0, (height - scaledChildHeight) / 2, width,
(height + scaledChildHeight) / 2);
}
}
}
}
I see some issue with your AutoFocus handling code.
Analysis Result
There is cycle in your autofocus.
Explanation
a) Camera Preview Class mAutoFocusCallback is set with the autoFocusCb of the Camera Activity.
public CameraPreviewNew(Context context,...,AutoFocusCallback autoFocusCb)
{
super(context);
mAutoFocusCallback = autoFocusCb;
...
}
b)surfaceChanged is called once, at the time of loading the activity. The camera is requested to Auto Focus.
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h)
{
if (mCamera != null)
{
...
mCamera.startPreview();
/*Auto focus camera and call <code>mAutoFocusCallback</code> after autofocus.*/
mCamera.autoFocus(mAutoFocusCallback);
...
}
}
c) On completion of the autofocus the mAutoFocusCallback callback is called. mAutoFocusCallback->autoFocusCb->onAutoFocus()
Camera Activity
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
d)onAutoFocus schedules one more autoFocus after 1000 millisecons, 1 sec.
Camera Activity
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
e)After one second the messages is passed to handler that calles the runnable doAutoFocus requesting camera to auto focus, similar to b) above.
private Runnable doAutoFocus = new Runnable() {
public void run() {
if (mCamera != null && mPreviewing) {
mCamera.autoFocus(autoFocusCB);
}
}
};
f) After completion of the autoFocus, the autoFocusCB is called again, similar to c) above. and cycle continues.
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
Solution
I am confused why such implementation. The cycle may be reason behind not listening to the flash enable/disable calls. You need to remove the code below and do something meaningful else leave the onAutoFocus() empty.
Camera.AutoFocusCallback autoFocusCB = new Camera.AutoFocusCallback() {
public void onAutoFocus(boolean success, Camera camera) {
/*REMOVE LINE BELOW*/
mAutoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
For auto focusing every time the camera moves you need to take help of the motion sensors provided with the phone. You can google it
Hope that helps.
Happy Coding...
It seems that you dont need to use AutoFocusCallBack for your app, because you did nothing else than delay 1 second.
What you can do to focus all the time is using FOCUS_MODE_CONTINUOUS_PICTURE(read more here) like that(setFocus method is in CameraPreview, not in Activity):
public void setFocus() {
Camera.Parameters p = mCamera.getParameters();
p.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
mCamera.setParameters(p);
mCamera.startPreview();
}
And call it in SurfaceChanged:
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// Now that the size is known, set up the camera parameters and begin
// the preview.
Camera.Parameters parameters = mCamera.getParameters();
List<Camera.Size> previewSizes = parameters.getSupportedPreviewSizes();
// You need to choose the most appropriate previewSize for your app
Camera.Size previewSize = previewSizes.get(0);
parameters.setPreviewSize(previewSize.width, previewSize.height);
parameters.setRotation(90);
mCamera.setParameters(parameters);
mCamera.startPreview();
setFlash(true);
setZoomLevel(5);
setFocus();
Log.w(TAG, "surfaceChanged()");
}
For flash you can use this method from CameraPreview:
public void setFlash(boolean isFlashOn) {
Camera.Parameters p = mCamera.getParameters();
if (isFlashOn) {
p.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
mCamera.setParameters(p);
mCamera.startPreview();
} else {
p.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
mCamera.setParameters(p);
mCamera.startPreview();
}
Log.w(TAG, "setFlash()");
}
Hope it helps you! If you have any questions about my answer feel free to comment.
While this must be late, if you are toggling Camera parameters, it is preferable to do the sequence of operation as below
mCamera.stopPreview();
mCamera.setParameters(params); // set flash on in Camera parameters
mCamera.startPreview();

Imageformat set in surfaceChanged changes to something else in onPreviewFrame

Below attached is the code I am trying. The CamPreview class is being used by the launcher activity. I was able to get preview fine before implementing PreviewCallback. When i try PreviewCallback by implementing onPreviewFrame, i am totally confused how it works internally. Below are the following things that are confusing me. Kindly clarify them.
1) Though i set camera parameters like ImageFormat and Previewsize, they don't seem to persist till the invokation of method onPreviewFrame. For example, the Log.i statements in surfaceChanged method (called immediately atleast once after surfaceCreated as per my understanding) prints the preview size as 1056x864. However, onPreviewFrame reports that the preview size as 1920x1080.
Even the picture format changes from NV21(17 in surfaceChanged) to JPEG(256 in onPreviewFrame).
I have verified and confirmed that the Camera instance passed to onPreviewFrame is same as the member variable mCamera declared in CamPreview class.
If i am able to successfully get preview format as NV21 in onPreviewFrame, how do i convert that to ARGB format ? I have tried the methods posted in stackoverflow but the data passed to onPreviewFrame fails due to index out of bounds, which lead to me to check the image formats in the first place. If anyone has tried something similar, pls do let me know what was i missing during the creation that is causing this mess :(.
I have tried to create bitmap by initially creating YuvImage from byte[] passed to onPreviewFrame which gave me green latern images(all green or garbage some times)!
2) You can see other Log.i stmts next to the ones i mentioned in point (1). They print out bits per pixel and bytes per pixel information of the preview in the methods surfaceChanged and onPreviewFrame. They turn out to be 12 and 1 respectively. How is that even possible ? Again, this could be a side effect of what is happening in (1)
public class CamPreview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {
private static final String TAG = "CamPreview";
private SurfaceHolder mHolder;
private Camera mCamera;
private byte[] mVideoSource;
private Bitmap mBackBuffer;
private Paint mPaint;
private Context mContext;
public CamPreview(Context context) {
super(context);
mContext = context;
mCamera = getCameraInstance();
mHolder = getHolder();
mHolder.addCallback(this);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(null);
mCamera.setPreviewCallbackWithBuffer(this);
this.setWillNotDraw(false);
Log.i(TAG, "#SurfaceCreated: initilization finished");
} catch (IOException eIOException) {
Log.i(TAG, "Error setting camera preview: " + eIOException.getMessage());
throw new IllegalStateException();
}
}
private Size findBestResolution(int pWidth, int pHeight) {
List<Size> lSizes = mCamera.getParameters().getSupportedPreviewSizes();
Size lSelectedSize = mCamera.new Size(0, 0);
for (Size lSize : lSizes) {
if ((lSize.width <= pWidth)
&& (lSize.height <= pHeight)
&& (lSize.width >= lSelectedSize.width)
&& (lSize.height >= lSelectedSize.height)) {
lSelectedSize = lSize;
}
}
if ((lSelectedSize.width == 0)
|| (lSelectedSize.height == 0)) {
lSelectedSize = lSizes.get(0);
}
return lSelectedSize;
}
private void createBuffers(String caller, Size prefSize) {
Camera.Parameters camParams = mCamera.getParameters();
int previewWidth = prefSize.width;
int previewHeight = prefSize.height;
mBackBuffer = Bitmap.createBitmap(previewWidth,
previewHeight,
Bitmap.Config.ARGB_8888);
Log.i(TAG,"#"+caller+": Piture Width " + Integer.toString(previewWidth));
Log.i(TAG,"#"+caller+": Piture Height " + Integer.toString(previewHeight));
Log.i(TAG,"#"+caller+": Piture format " + Integer.toString(ImageFormat.NV21));
camParams.setPreviewSize(previewWidth,previewHeight);
camParams.setPreviewFormat(ImageFormat.NV21);
mCamera.setParameters(camParams);
PixelFormat pxlFrmt = new PixelFormat();
PixelFormat.getPixelFormatInfo(camParams.getPreviewFormat(), pxlFrmt);
Log.i(TAG,"#"+caller+": Bits per pixel " + Integer.toString(pxlFrmt.bitsPerPixel));
Log.i(TAG,"#"+caller+": Bytes per pixel " + Integer.toString(pxlFrmt.bytesPerPixel));
int sz = previewWidth * previewHeight * pxlFrmt.bitsPerPixel/8;
mVideoSource = new byte[sz];
mCamera.addCallbackBuffer(mVideoSource);
Log.i(TAG, "#"+caller+": backbuffer initilization finished");
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
Log.i(TAG, "#SurfaceCreated: preview started");
} catch (Exception e){
Log.d(TAG, "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// 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) {
Log.i(TAG,"No proper holder");
return;
}
try {
mCamera.stopPreview();
} catch (Exception e){
Log.i(TAG,"tried to stop a non-existent preview");
return;
}
createBuffers("surfaceChanged",findBestResolution(w, h));
}
public void onPreviewFrame(byte[] data, Camera camera) {
Log.i(TAG,"#onPreviewFrame: Invoked");
Camera.Parameters params = camera.getParameters();
Camera.Size camSize = params.getPictureSize();
int w = camSize.width;
int h = camSize.height;
Log.i(TAG,"#onPreviewFrame: Piture Width " + Integer.toString(w));
Log.i(TAG,"#onPreviewFrame: Piture Height " + Integer.toString(h));
Log.i(TAG,"#onPreviewFrame: Piture format " + Integer.toString(params.getPictureFormat()));
PixelFormat pxlFrmt = new PixelFormat();
PixelFormat.getPixelFormatInfo(params.getPreviewFormat(), pxlFrmt);
Log.i(TAG,"#onPreviewFrame: Bits per pixel " + Integer.toString(pxlFrmt.bitsPerPixel));
Log.i(TAG,"#onPreviewFrame: Bytes per pixel " + Integer.toString(pxlFrmt.bytesPerPixel));
mBackBuffer = BitmapFactory.decodeByteArray(data, 0, data.length);
Log.i(TAG,"#onPreviewFrame: Back buffer set.");
invalidate();
}
#Override
protected void onDraw(Canvas pCanvas) {
super.onDraw(pCanvas);
Log.i(TAG,"#onDraw: Invoked");
if (mCamera != null) {
Log.i(TAG,"#onDraw: Bbefore draw call to canvas");
pCanvas.drawBitmap(mBackBuffer, 0, 0, mPaint);
mCamera.addCallbackBuffer(mVideoSource);
Log.i(TAG,"#onDraw: Draw finished");
}
}
/** 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 {
// no camera on this device
return false;
}
}
/** A safe way to get an instance of the Camera object. */
private Camera getCameraInstance(){
Camera c = null;
if(checkCameraHardware(mContext)) {
try {
Log.i(TAG, "Trying to open the camera");
c = Camera.open(0);
Log.i(TAG, "Camera opened successfully.");
}
catch (Exception e){
Log.i(TAG, e.getMessage());
}
}
return c;
}
private void releaseCamera(){
if (mCamera != null){
mCamera.release(); // release the camera for other applications
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
releaseCamera();
mVideoSource = null;
mBackBuffer = null;
}
}
}
Ok, Figured out couple of things after careful reiteration of my reading through the android docs. Apparently preview[Size|Format] is completely different from picture[Size/Format], which i assumed to be one and the same earlier. So, that fixed my rendering issues and crashes due incorrect data format. It also clarifed my confusion of change in camera parameters automatically.
The whole example is working now. However, i am seeing two layers of preview, the one which directly rendered by the camera, and the one i am rendering though onDraw. I am not sure whether i should see both of them or not. Below is the fixed code.
Thank you if anyone who might have spent time on this. Now, i will work on moving the whole onPreviewFrame logic to native code to speed up things! :)
public class CamPreview extends SurfaceView implements SurfaceHolder.Callback, Camera.PreviewCallback {
private static final String TAG = "CamPreview";
private static final int mPreviewWidth = 1280;
private static final int mPreviewHeight = 720;
private static final int mFPSXOff = 72;
private static final int mFPSYOff = 72;
private static final int mFPSSize = 64;
private float mTotalTime;
private float mFrameCount;
private String mFPS;
private long mStart;
private Context mContext;
private SurfaceHolder mHolder;
private Camera mCamera;
private byte[] mVideoSource;
private Bitmap mBackBuffer;
private Paint mPaint;
public CamPreview(Context context) {
super(context);
mContext = context;
mHolder = getHolder();
mCamera = getCameraInstance();
mHolder.addCallback(this);
mFrameCount = 0;
mTotalTime = 0;
mFPS = "0 FPS";
mStart = 0;
mPaint = new Paint();
mPaint.setColor(0xFFFF0000);
mPaint.setTextSize(mFPSSize);
}
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(null);
mCamera.setPreviewCallbackWithBuffer(this);
this.setWillNotDraw(false);
} catch (IOException eIOException) {
Log.i(TAG, "Error setting camera preview: " + eIOException.getMessage());
throw new IllegalStateException();
}
}
private Size findBestResolution(int pWidth, int pHeight) {
List<Size> lSizes = mCamera.getParameters().getSupportedPreviewSizes();
Size lSelectedSize = mCamera.new Size(0, 0);
for (Size lSize : lSizes) {
if ((lSize.width <= pWidth)
&& (lSize.height <= pHeight)
&& (lSize.width >= lSelectedSize.width)
&& (lSize.height >= lSelectedSize.height)) {
lSelectedSize = lSize;
}
}
if ((lSelectedSize.width == 0)
|| (lSelectedSize.height == 0)) {
lSelectedSize = lSizes.get(0);
}
return lSelectedSize;
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
if (mHolder.getSurface() == null) {
Log.i(TAG,"No proper holder");
return;
}
try {
mCamera.stopPreview();
} catch (Exception e) {
Log.i(TAG,"tried to stop a non-existent preview");
return;
}
PixelFormat pxlFrmt = new PixelFormat();
Camera.Parameters camParams = mCamera.getParameters();
Size previewSize = findBestResolution(w, h);
int previewWidth = previewSize.width;
int previewHeight = previewSize.height;
camParams.setPreviewSize(previewWidth,previewHeight);
camParams.setPreviewFormat(ImageFormat.NV21);
mCamera.setParameters(camParams);
mCamera.setDisplayOrientation(90);
mBackBuffer = Bitmap.createBitmap(previewWidth, previewHeight, Bitmap.Config.ARGB_8888);
PixelFormat.getPixelFormatInfo(camParams.getPreviewFormat(), pxlFrmt);
int sz = previewWidth * previewHeight * pxlFrmt.bitsPerPixel/8;
mVideoSource = new byte[sz];
mCamera.addCallbackBuffer(mVideoSource);
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
Log.i(TAG, "#SurfaceChanged: preview started");
} catch (Exception e){
Log.d(TAG, "#SurfaceChanged:Error starting camera preview: " + e.getMessage());
}
mFrameCount = 0;
mTotalTime = 0;
mStart = SystemClock.elapsedRealtime();
}
public void onPreviewFrame(byte[] data, Camera camera) {
Log.i(TAG,"#onPreviewFrame: Invoked");
Camera.Parameters params = camera.getParameters();
Camera.Size camSize = params.getPreviewSize();
int w = camSize.width;
int h = camSize.height;
PixelFormat pxlFrmt = new PixelFormat();
PixelFormat.getPixelFormatInfo(params.getPreviewFormat(), pxlFrmt);
try {
YuvImage yuv = new YuvImage(data,ImageFormat.NV21,w,h,null);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
yuv.compressToJpeg(new Rect(0,0,w,h), 100, baos);
byte[] jpgData = baos.toByteArray();
mBackBuffer = BitmapFactory.decodeByteArray(jpgData, 0, jpgData.length);
} catch (Exception e) {
;
}
Log.i(TAG,"#onPreviewFrame: Backbuffer set.");
postInvalidate();
mFrameCount++;
long end = SystemClock.elapsedRealtime();
mTotalTime += (end-mStart);
mStart = end;
mFPS = Float.toString((1000*mFrameCount/mTotalTime))+" fps";
}
#Override
protected void onDraw(Canvas pCanvas) {
Log.i(TAG,"#onDraw: Invoked");
if (mCamera != null) {
if(mBackBuffer==null) {
Log.i(TAG, "Back buffer is null :((((((( ");
} else {
pCanvas.drawBitmap(mBackBuffer, 0, 0, null);
pCanvas.drawText(mFPS, mFPSXOff, mFPSYOff, mPaint);
mCamera.addCallbackBuffer(mVideoSource);
}
}
}
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)){
return true;
} else {
return false;
}
}
private Camera getCameraInstance(){
Camera c = null;
if(checkCameraHardware(mContext)) {
try {
c = Camera.open(0);
Log.i(TAG, "Camera opened successfully");
Camera.Parameters params = c.getParameters();
params.setPreviewFormat(ImageFormat.NV21);
params.setPreviewSize(mPreviewWidth, mPreviewHeight);
c.setParameters(params);
Log.i(TAG, "NV21 format set to camera with resolution 1280x720");
}
catch (Exception e){
Log.i(TAG, e.getMessage());
}
}
return c;
}
private void releaseCamera(){
if (mCamera != null){
mCamera.release();
Log.i(TAG,"#releaseCamera:");
mCamera = null;
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera != null) {
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
releaseCamera();
mVideoSource = null;
mBackBuffer = null;
}
}
}

How to change area of scan Zbar?

I want to change the area of camera scan. Now I take image to scan as big as screen of device. I'm trying to crop image to analyze. So just the center of preview will be source to scan. Is there any option to set captured preview to be smaller or creating Bitmap from byte[] data and crop it is the only way to get smaller area? I was trying to read something about it but documentation for Zbar Android is very poor (comparing to iOS).
Picture here:
https://postimg.cc/image/4wk4u0mln/
MainActivity
public class MainActivity extends Activity
{
private Camera mCamera;
private Context context;
private CameraPreview mPreview;
private Handler autoFocusHandler;
TextView scanText;
Button scanButton;
ImageScanner scanner;
private PowerManager.WakeLock wl;
private boolean barcodeScanned = false;
private boolean previewing = true;
static {
System.loadLibrary("iconv");
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
autoFocusHandler = new Handler();
mCamera = getCameraInstance();
context = getApplicationContext();
/* Instance barcode scanner */
scanner = new ImageScanner();
scanner.setConfig(0, Config.X_DENSITY, 3);
scanner.setConfig(0, Config.Y_DENSITY, 3);
mPreview = new CameraPreview(this, mCamera, previewCb, autoFocusCB);
FrameLayout preview = (FrameLayout)findViewById(R.id.cameraPreview);
preview.addView(mPreview);
scanText = (TextView)findViewById(R.id.scanText);
}
#Override
public void onPause() {
super.onPause();
releaseCamera();
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
onStart();
}
#Override
protected void onStop() {
// TODO Auto-generated method stub
super.onStop();
finish();
}
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open();
} catch (Exception e){
}
return c;
}
private void releaseCamera() {
if (mCamera != null) {
previewing = false;
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}
private Runnable doAutoFocus = new Runnable() {
#Override
public void run() {
if (previewing)
mCamera.autoFocus(autoFocusCB);
}
};
PreviewCallback previewCb = new PreviewCallback() {
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
Camera.Parameters parameters = camera.getParameters();
Size size = parameters.getPreviewSize();
//HERE we read taken picture from prieview
Image barcode = new Image(size.width, size.height, "Y800");
barcode.setData(data);
int result = scanner.scanImage(barcode);
if (result != 0) {
previewing = false;
mCamera.setPreviewCallback(null);
mCamera.stopPreview();
SymbolSet syms = scanner.getResults();
for (Symbol sym : syms) {
if (sym.getType() == Symbol.CODE128) {
sym.getData());
MediaPlayer mp = MediaPlayer.create(context, R.raw.beep_ok);
mp.start();
} else {
MediaPlayer mp = MediaPlayer.create(context, R.raw.beep_wrong);
mp.start();
}
mCamera.setPreviewCallback(previewCb);
mCamera.startPreview();
previewing = true;
mCamera.autoFocus(autoFocusCB);
}
}
}
};
// Mimic continuous auto-focusing
AutoFocusCallback autoFocusCB = new AutoFocusCallback() {
#Override
public void onAutoFocus(boolean success, Camera camera) {
autoFocusHandler.postDelayed(doAutoFocus, 1000);
}
};
//Method to crop Bitmap in case of use
public Bitmap scaleCenterCrop(Bitmap source, int newHeight, int newWidth) {
int sourceWidth = source.getWidth();
int sourceHeight = source.getHeight();
float xScale = (float) newWidth / sourceWidth;
float yScale = (float) newHeight / sourceHeight;
float scale = Math.max(xScale, yScale);
float scaledWidth = scale * sourceWidth;
float scaledHeight = scale * sourceHeight;
float left = (newWidth - scaledWidth) / 2;
float top = (newHeight - scaledHeight) / 2;
RectF targetRect = new RectF(left, top, left + scaledWidth, top + scaledHeight);
Bitmap dest = Bitmap.createBitmap(newWidth, newHeight, source.getConfig());
Canvas canvas = new Canvas(dest);
canvas.drawBitmap(source, null, targetRect, null);
return dest;
}
}
CameraPrieview.java
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private PreviewCallback previewCallback;
private AutoFocusCallback autoFocusCallback;
public CameraPreview(Context context, Camera camera,
PreviewCallback previewCb,
AutoFocusCallback autoFocusCb) {
super(context);
mCamera = camera;
previewCallback = previewCb;
autoFocusCallback = autoFocusCb;
/*
* Set camera to continuous focus if supported, otherwise use
* software auto-focus. Only works for API level >=9.
*/
/*
Camera.Parameters parameters = camera.getParameters();
for (String f : parameters.getSupportedFocusModes()) {
if (f == Parameters.FOCUS_MODE_CONTINUOUS_PICTURE) {
mCamera.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
autoFocusCallback = null;
break;
}
}
*/
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
// deprecated setting, but required on Android versions prior to 3.0
mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
Log.d("DBG", "Error setting camera preview: " + e.getMessage());
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Camera preview released in activity
}
#Override
public 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;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
try {
// Hard code camera surface rotation 90 degs to match Activity view in portrait
mCamera.setDisplayOrientation(90);
mCamera.setPreviewDisplay(mHolder);
mCamera.setPreviewCallback(previewCallback);
mCamera.startPreview();
mCamera.autoFocus(autoFocusCallback);
} catch (Exception e){
Log.d("DBG", "Error starting camera preview: " + e.getMessage());
}
}
}
In the PreviewCallback you can actually crop the scanning area. In the onPreviewFrame method, after calling barcode.setData() you can call barcode.setCrop(left,top,width,height). All measurements in pixels.
Also make sure to set the crop size with respect to the preview image size and not the device screen size.
Note: Please make sure that the x-axis is always the longest side even though you orient the phone in portrait mode.
Please refer below Image for better understanding of the arguments.
This solution is specific to zbar for android.

Categories

Resources