尝试使用“zxing”条形码扫描器解码Android相机拍摄的图像时,我收到“NotFoundException”。
我研究过Stackoverflow网站& zxing docs,帖子提示zxing API找不到条码开始扫描,因为图像可能太大了。 我尝试过扫描不同尺寸,如原始尺寸的50%,30%,25%,我也尝试了10种不同的条形码,相机拍摄图像并保存在SD卡上,但无法扫描图像的位图在记忆中。
负责相机操作的活动&扫描是(ScanVinFromBarcodeActivity.java),负责拍摄图像的方法是 onPictureTaken(byte [] imgData,Camera camera),负责解码图像的方法是public String decodeBitmapToString(BinaryBitmap)位图)即可。
请参阅下面的ScanVinFromBarcodeActivity.java:
package com.ty.tyownerspoc.barcode;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Hashtable;
import com.google.zxing.BinaryBitmap;
import com.google.zxing.ChecksumException;
import com.google.zxing.DecodeHintType;
import com.google.zxing.FormatException;
import com.google.zxing.MultiFormatReader; //found via import at compile time, however was found at run time
import com.google.zxing.NotFoundException;
import com.google.zxing.RGBLuminanceSource;//found via import at compile time, however was found at run time
import com.google.zxing.Reader;
import com.google.zxing.Result;
import com.google.zxing.common.HybridBinarizer;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.Display;
import android.view.SurfaceView;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.TextView;
import android.widget.Toast;
import com.ty.tyownerspoc.R;
public class ScanVinFromBarcodeActivity extends Activity {
private Camera globalCamera;
private int cameraId = 0;
private TextView VINtext = null;
private View scanButton = null;
// bitmap from camera
private Bitmap bmpOfTheImageFromCamera = null;
// global flag whether a camera has been detected
private boolean isThereACamera = false;
// surfaceView for preview object
private FrameLayout frameLayoutBarcodeScanner = null;
private CameraPreview newCameraPreview = null;
private SurfaceView surfaceViewBarcodeScanner = null;
/*
* This method , finds FEATURE_CAMERA, opens the camera, set parameters ,
* add CameraPreview to layout, set camera surface holder, start preview
*/
private void initializeGlobalCamera() {
try {
if (!getPackageManager().hasSystemFeature(
PackageManager.FEATURE_CAMERA)) {
Toast.makeText(this, "No camera on this device",
Toast.LENGTH_LONG).show();
} else { // check for front camera ,and get the ID
cameraId = findFrontFacingCamera();
if (cameraId < 0) {
Toast.makeText(this, "No front facing camera found.",
Toast.LENGTH_LONG).show();
} else {
Log.d("ClassScanViewBarcodeActivity",
"camera was found , ID: " + cameraId);
// camera was found , set global camera flag to true
isThereACamera = true;
// OPEN
globalCamera = Camera.open(cameraId);
// pass surfaceView to CameraPreview
newCameraPreview = new CameraPreview(this, globalCamera);
// pass CameraPreview to Layout
frameLayoutBarcodeScanner.addView(newCameraPreview);
try {
globalCamera
.setPreviewDisplay(surfaceViewBarcodeScanner
.getHolder());
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// PREVIEW
globalCamera.startPreview();
Log.d("ClassScanViewBarcodeActivity",
"camera opened & previewing");
}
}// end else ,check for front camera
}// end try
catch (Exception exc) {
// in case of exception release resources & cleanup
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
Log.d("ClassScanViewBarcodeActivity initializeGlobalCamera() exception:",
exc.getMessage());
}// end catch
}
// onCreate, instantiates layouts & surfaceView used for video preview
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barcode_vin_scanner);
Log.d("ClassScanViewBarcodeActivity", "onCreate ");
// create surfaceView for previewing of camera image
frameLayoutBarcodeScanner = (FrameLayout) findViewById(R.id.FrameLayoutForPreview);
surfaceViewBarcodeScanner = (SurfaceView) findViewById(R.id.surfaceViewBarcodeScanner);
initializeGlobalCamera();
// create text area & scan button
VINtext = (TextView) findViewById(R.id.mytext);
scanButton = findViewById(R.id.webbutton);
// on click listener, onClick take a picture
scanButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
try {
// if true take a picture
if (isThereACamera) {
Log.d("ClassScanViewBarcodeActivity",
"setOnClickListener() isThereACamera: "+ isThereACamera);
//set picture format to JPEG, everytime makesure JPEg callback is called
Parameters parameters = globalCamera.getParameters();
parameters.setPictureFormat(ImageFormat.JPEG);
globalCamera.setParameters(parameters);
//take pic , should call Callback
globalCamera.takePicture(null, null, jpegCallback);
}
}// end try
catch (Exception exc) {
// in case of exception if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
exc.printStackTrace();
}// end catch
}// end on Click
});// end OnClickListener() implementation
}// end onCreate
@Override
protected void onResume() {
Log.d("ClassScanViewBarcodeActivity, onResume() globalCamera:",
String.valueOf(globalCamera));
if (globalCamera != null) {
// START PREVIEW
globalCamera.startPreview();
} else {
initializeGlobalCamera();
}
super.onResume();
}
@Override
protected void onStop() {
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
super.onStop();
}
@Override
protected void onPause() {
if (globalCamera != null) {
globalCamera.stopPreview();
globalCamera.setPreviewCallback(null);
globalCamera.release();
globalCamera = null;
}
super.onPause();
}// end onPause()
//callback used by takePicture()
/*
* Implement PictureCallback's onPictureTaken(byte[] imgData, Camera camera) method.
* 1) downsample the image via options.inSampleSize , in order to make saving faster & lass error prone,
* without downsampling the image takes to long to save & we get memory exceptions.
* 2) create bitmap
* 3) Might scale image to help zxing computationally by reducing image size
* 4)save image to sd card for testing & sanity check to see if actual image was taken
* 5) decode the VIN & set the value of GUI text component*/
PictureCallback jpegCallback = new PictureCallback() {
public void onPictureTaken(byte[] imgData, Camera camera) {
BinaryBitmap bitmap;
try {
Log.d("ClassScanViewBarcodeActivity" ,"onPictureTaken()");
/* Implement PictureCallback's onPictureTaken(byte[] imgData, Camera camera) method.
1) down-sample the image via options.inSampleSize , in order to make image resolution smaller & prevent MemoryOutOfBounds exception.*/
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
// 2) create bitmap
bmpOfTheImageFromCamera = BitmapFactory.decodeByteArray(
imgData, 0, imgData.length,options);
if (bmpOfTheImageFromCamera != null) {
//3) scale image to help zxing computationally by reducing image size
int height = bmpOfTheImageFromCamera.getWidth();
int width = bmpOfTheImageFromCamera.getWidth();
double scale = 0.50; //I used half size 0.5 ,and 1/4 size but still the barcode can't be found
int newHeight = (int) (height * scale);
int newWidth = (int) (width * scale);
Log.d("createScaledBitmap()","old width: "+width +" old height: "+height+" new width: "+newWidth+" new height"+newHeight);
String.valueOf(bmpOfTheImageFromCamera.getByteCount());
//resize
Bitmap resizedBitmap = Bitmap.createScaledBitmap(bmpOfTheImageFromCamera,newWidth , newHeight, true);
Matrix rotationMatrix90CounterClockWise = new Matrix();
rotationMatrix90CounterClockWise.postRotate(90);
//roatate to correct orientation
resizedBitmap = Bitmap.createBitmap(resizedBitmap ,0,0,newWidth , newHeight,rotationMatrix90CounterClockWise,true);
//get back bytes from Bitmap in order to save bytes on sd card
ByteArrayOutputStream baoStream = new ByteArrayOutputStream();
resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baoStream);
byte [] bytesImage = baoStream.toByteArray();
//4) save image to sd card for testing & sanity check to see if actual image was taken
savePicture(bytesImage , scale);
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): bmpOfTheImageFromCamera getByteCount(): ",
String.valueOf(bmpOfTheImageFromCamera.getByteCount()));
//convert Bitmap to BinaryBitmap
bitmap = cameraBytesToBinaryBitmap(resizedBitmap);
if (bitmap != null) {
// 5) decode the VIN & set the value of GUI text component
String VIN = decodeBitmapToString(bitmap);
Log.d("***ClassScanViewBarcodeActivity ,onPictureTaken(): VIN ",
VIN);
VINtext.setText(VIN);
VINtext.refreshDrawableState();
} else {
Log.d("ClassScanViewBarcodeActivity ,onPictureTaken(): bitmap=",String.valueOf(bitmap));
}
} else {
Log.d("ClassScanViewBarcodeActivity , onPictureTaken(): bmpOfTheImageFromCamera = ",
String.valueOf(bmpOfTheImageFromCamera));
}
}// end try
catch (Exception exc) {
exc.getMessage();
Log.d("ClassScanViewBarcodeActivity , scanButton.setOnClickListener(): exception = ",
exc.getMessage());
}
finally
{
globalCamera.stopPreview();
//start previewing again onthe SurfaceView in case use wants to take another pic/scan
globalCamera.startPreview();
}
}// end onPictureTaken()
};// jpegCallback implementation
/*
* savePicture(byte [] data) for testing
*/
public void savePicture(byte [] data , double sizeInPercent)
{
Log.d( "ScanVinFromBarcodeActivity " , "savePicture(byte [] data)");
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyymmddhhmmss");
String date = dateFormat.format(new Date());
String photoFile = "Picture_sample_sizePercent_"+sizeInPercent+"_"+ date + ".jpg";
File sdDir = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
String filename =sdDir + File.separator + photoFile;
File pictureFile = new File(filename);
try {
FileOutputStream fos = new FileOutputStream(pictureFile);
fos.write(data);
fos.close();
Toast.makeText(this, "New Image saved:" + photoFile,
Toast.LENGTH_LONG).show();
} catch (Exception error) {
Log.d( "File not saved: " , error.getMessage());
Toast.makeText(this, "Image could not be saved.",
Toast.LENGTH_LONG).show();
}
}
/* find camera on device */
private int findFrontFacingCamera() {
int cameraId = -1;
// Search for the front facing camera
int numberOfCameras = Camera.getNumberOfCameras();
for (int i = 0; i < numberOfCameras; i++) {
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(i, info);
if (info.facing == CameraInfo.CAMERA_FACING_BACK) {
Log.d("ClassScanViewBarcodeActivity , findFrontFacingCamera(): ",
"Camera found");
cameraId = i;
break;
}
}
return cameraId;
}// end findFrontFacingCamera()
/*
* Attempts to decode barcode data inside the input bitmap.
* Uses zxing API hints such as DecodeHintType.TRY_HARDER ,DecodeHintType.PURE_BARCODE.
*/
@SuppressWarnings("finally")
public String decodeBitmapToString(BinaryBitmap bitmap) {
Log.d("ClassScanViewBarcodeActivity",
"decodeBitmapToString(BinaryBitmap bitmap");
Reader reader = null;
Result result = null;
String textResult = null;
try {
Hashtable<DecodeHintType, Object> decodeHints = new Hashtable<DecodeHintType, Object>();
decodeHints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
decodeHints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
reader = new MultiFormatReader();
if (bitmap != null) {
result = reader.decode(bitmap, decodeHints);
if (result != null) {
Log.d("decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result));
Log.d("decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result.getBarcodeFormat().toString()));
textResult = result.getText();
} else {
Log.d("decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result));
}
} else {
Log.d("decodeBitmapToString (BinaryBitmap bitmap): bitmap = ",
String.valueOf(bitmap));
}
} catch (NotFoundException e) {
// TODO Auto-generated catch block
textResult = "Unable to decode. Please try again.";
e.printStackTrace();
} catch (ChecksumException e) {
textResult = "Unable to decode. Please try again.";
e.printStackTrace();
} catch (FormatException e) {
textResult = "Unable to decode. Please try again.";
e.printStackTrace();
}
finally
{
return textResult;
}
}// end decodeBitmapToString ()
@SuppressWarnings("finally")
public BinaryBitmap cameraBytesToBinaryBitmap(Bitmap bitmap) {
Log.d("ClassScanViewBarcodeActivity , cameraBytesToBinaryBitmap (Bitmap bitmap):" , "");
BinaryBitmap binaryBitmap = null;
try
{
if (bitmap != null) {
int[] pixels = new int[bitmap.getHeight() * bitmap.getWidth()];
bitmap.getPixels(pixels, 0, bitmap.getWidth(),0, 0,bitmap.getWidth(), bitmap.getHeight());
RGBLuminanceSource source = new RGBLuminanceSource(
bitmap.getWidth(), bitmap.getHeight(), pixels);
HybridBinarizer bh = new HybridBinarizer(source);
binaryBitmap = new BinaryBitmap(bh);
} else {
Log.d("ClassScanViewBarcodeActivity , cameraBytesToBinaryBitmap (Bitmap bitmap): bitmap = ",
String.valueOf(bitmap));
}
}
catch (Exception exc)
{
exc.printStackTrace();
}
finally
{
return binaryBitmap;
}
}
@SuppressLint("NewApi")
@SuppressWarnings("deprecation")
/*
* The method getScreenOrientation() return screen orientation either
* landscape or portrait. IF width < height , than orientation = portrait,
* ELSE landscape For backwards compatibility we use to methods to detect
* the orientation. The first method is for API versions prior to 13 or
* HONEYCOMB.
*/
public int getScreenOrientation() {
int currentapiVersion = android.os.Build.VERSION.SDK_INT;
// if API version less than 13
Display getOrient = getWindowManager().getDefaultDisplay();
int orientation = Configuration.ORIENTATION_UNDEFINED;
if (currentapiVersion < android.os.Build.VERSION_CODES.HONEYCOMB) {
// Do something for API version less than HONEYCOMB
if (getOrient.getWidth() == getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_SQUARE;
} else {
if (getOrient.getWidth() < getOrient.getHeight()) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else {
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
} else {
// Do something for API version greater or equal to HONEYCOMB
Point size = new Point();
this.getWindowManager().getDefaultDisplay().getSize(size);
int width = size.x;
int height = size.y;
if (width < height) {
orientation = Configuration.ORIENTATION_PORTRAIT;
} else {
orientation = Configuration.ORIENTATION_LANDSCAPE;
}
}
return orientation;
}// end getScreenOrientation()
}// end class ScanVinFromBarcodeActivity
CameraPreview.java,用于预览来自相机的实时图像
package com.ty.tyownerspoc.barcode;
import java.io.IOException;
import java.util.List;
import android.content.Context;
import android.hardware.Camera;
import android.hardware.Camera.Size;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
/** A basic Camera preview class */
public class CameraPreview extends SurfaceView implements
SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
private Context context;
public CameraPreview(Context context, Camera camera) {
super(context);
mCamera = camera;
this.context = context;
// 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);
}
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the
// preview.
try {
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
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) {
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e) {
}
Camera.Parameters p = mCamera.getParameters();
// get width & height of the SurfaceView
int SurfaceViewWidth = this.getWidth();
int SurfaceViewHeight = this.getHeight();
List<Size> sizes = p.getSupportedPreviewSizes();
Size optimalSize = getOptimalPreviewSize(sizes, SurfaceViewWidth, SurfaceViewHeight);
// set parameters
p.setPreviewSize(optimalSize.width, optimalSize.height);
/*rotate the image by 90 degrees clockwise , in order to correctly displayed*/
mCamera.setDisplayOrientation(90); mCamera.setParameters(P);
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e) {
Log.d("CameraPreview , surfaceCreated() , orientation: ",
String.valueOf(e.getMessage()));
}
}// end surfaceChanged()
static Size getOptimalPreviewSize(List <Camera.Size>sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
final double MAX_DOWNSIZE = 1.5;
double targetRatio = (double) w / h;
if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
// Try to find an size match aspect ratio and size
for (Camera.Size size : sizes) {
double ratio = (double) size.width / size.height;
double downsize = (double) size.width / w;
if (downsize > MAX_DOWNSIZE) {
//if the preview is a lot larger than our display surface ignore it
//reason - on some phones there is not enough heap available to show the larger preview sizes
continue;
}
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
// Cannot find the one match the aspect ratio, ignore the requirement
//keep the max_downsize requirement
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
double downsize = (double) size.width / w;
if (downsize > MAX_DOWNSIZE) {
continue;
}
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
//everything else failed, just take the closest match
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}//end CameraPreview class
activity_barcode_vin_scanner.xml,ScanVinFromBarcodeActivity的布局
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="20dip" >
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="20dip"
android:text="@string/decode_label"
android:textColor="@color/mbackground1" />
<TextView
android:id="@+id/mytext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/mbackground2"
android:gravity="center_horizontal"
android:padding="20dip"
android:textColor="@color/mytextcolor" />
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="20dip"
android:text="@string/continue_label"
android:textColor="@color/mytextcolor" />
<Button
android:id="@+id/webbutton"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="@string/web_button"
android:textColor="@color/mytextcolor" />
<FrameLayout
android:id="@+id/FrameLayoutForPreview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1">
<SurfaceView
android:id="@+id/surfaceViewBarcodeScanner"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</LinearLayout>
我提供的 decodeBitmapToString(BinaryBitmap位图)添加了“DecodeHintTypes”,例如“尝试更难”和“纯单色”,当提供纯黑&amp;白色图像。我是否应该使用其他提示来扫描线性条形码(特别是车辆VIN条形码),我仍然得到NotFoundException?
我编辑的decodeBitmapToString(BinaryBitmap位图)方法:
@SuppressWarnings("finally")
public String decodeBitmapToString(BinaryBitmap bitmap) {
Reader reader = null;
Result result = null;
String textResult = null;
try {
Hashtable<DecodeHintType, Object> decodeHints = new Hashtable<DecodeHintType, Object>();
decodeHints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
decodeHints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);
reader = new MultiFormatReader();
if (bitmap != null) {
result = reader.decode(bitmap, decodeHints);
if (result != null) {
Log.d("decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result));
Log.d("decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result.getBarcodeFormat().toString()));
textResult = result.getText();
} else {
Log.d("decodeBitmapToString (BinaryBitmap bitmap): result = ",
String.valueOf(result));
}
} else {
Log.d("decodeBitmapToString (BinaryBitmap bitmap): bitmap = ",
String.valueOf(bitmap));
}
} catch (NotFoundException e) {
// TODO Auto-generated catch block
textResult = "Unable to decode. Please try again.";
e.printStackTrace();
} catch (ChecksumException e) {
textResult = "Unable to decode. Please try again.";
e.printStackTrace();
} catch (FormatException e) {
textResult = "Unable to decode. Please try again.";
e.printStackTrace();
}
finally
{
return textResult;
}
}// end
测试我正在使用的条形码:
我的日志:
任何帮助将不胜感激。
感谢。
答案 0 :(得分:1)
NotFoundException
表示一帧不包含条形码。这是正常的。您需要处理此问题,不要让异常从您的应用程序传播。继续下一帧;你每秒得到几十。