在Android中从画廊扫描图像中的条形码

12

我正在创建一个Android项目,主要功能是扫描条形码。

我尝试将Zxing库集成到我的项目中,并且它可以正常工作。

然而,似乎不支持从Android设备的图库中的可用图像扫描条形码。

我该怎么做?或者使用其他条形码库?

4个回答

24
你可以使用来自ZXing库的MultiFormatReader类。
你需要获取相册里的图像并将其转换为位图格式,如下所示:
Bitmap bMap = [...];
String contents = null;

int[] intArray = new int[bMap.getWidth()*bMap.getHeight()];  
//copy pixel data from the Bitmap into the 'intArray' array  
bMap.getPixels(intArray, 0, bMap.getWidth(), 0, 0, bMap.getWidth(), bMap.getHeight());  

LuminanceSource source = new RGBLuminanceSource(bMap.getWidth(), bMap.getHeight(), intArray);
BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

Reader reader = new MultiFormatReader();
Result result = reader.decode(bitmap);
contents = result.getText();

更新1

如需处理大图像,请查看:

https://developer.android.com/training/articles/memory.html

https://developer.android.com/training/displaying-bitmaps/manage-memory.html

你可以使用该属性 android:largeHeap 来增加堆大小。

抱歉回复慢了。我尝试了你的代码,对于一些图片来说,它运行得非常好。但是对于另一些图片,在以下代码处会崩溃:int[] intArray = new int[bitmap.getWidth() * bitmap.getHeight()];错误提示为:Caused by: java.lang.OutOfMemoryError。 - MrSiro
我从相册获取图像后调整了图像位图大小,它起作用了。非常感谢。 - MrSiro
嗨。一周前我已经点了你的答案很有用 :D - MrSiro
嗨@LaurentY, 我从github拉取了Zxing的源代码,但它不支持Android 2.3。 我尝试使用https://github.com/dm77/barcodescanner中的Zxing自定义版本,在Android 2.3上可以工作。 我该如何获取图像位图以显示并将其保存在SD卡中,就像这样: https://github.com/zxing/zxing/blob/master/android/src/com/google/zxing/client/android/CaptureActivity.java#L538-L544 你知道吗? - MrSiro
我尝试了那种方式。但在Android 2.3上,它显示“相机使用中...请重新启动。”(sdk>14可以) 我重新启动了但没有改变。 当我使用上述自定义时,它可以工作。 现在,我只需要获取图像位图并存储在SD卡中。 - MrSiro
显示剩余6条评论

3

我有一个关于如何实现这个功能的工作示例。如果你是在2016年阅读本文,以下是我实现的步骤:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    //initialize variables to make them global
    private ImageButton Scan;
    private static final int SELECT_PHOTO = 100;
  //for easy manipulation of the result
     public String barcode;

//call oncreate method
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //cast neccesary variables to their views
        Scan = (ImageButton)findViewById(R.id.ScanBut);

        //set a new custom listener
        Scan.setOnClickListener(this);
        //launch gallery via intent
        Intent photoPic = new Intent(Intent.ACTION_PICK);
        photoPic.setType("image/*");
        startActivityForResult(photoPic, SELECT_PHOTO);
    }

    //do necessary coding for each ID
    @Override
    public void onClick(View v) {
         switch (v.getId()){
             case R.id.ScanBut:
                 //launch gallery via intent
                 Intent photoPic = new Intent(Intent.ACTION_PICK);
                 photoPic.setType("image/*");
                 startActivityForResult(photoPic, SELECT_PHOTO);
                       break;
         }
    }

//call the onactivity result method
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent imageReturnedIntent) {
        super.onActivityResult(requestCode, resultCode, imageReturnedIntent);
        switch (requestCode) {
            case SELECT_PHOTO:
                if (resultCode == RESULT_OK) {
//doing some uri parsing
                    Uri selectedImage = imageReturnedIntent.getData();
                    InputStream imageStream = null;
                    try {
                        //getting the image
                        imageStream = getContentResolver().openInputStream(selectedImage);
                    } catch (FileNotFoundException e) {
                        Toast.makeText(getApplicationContext(), "File not found", Toast.LENGTH_SHORT).show();
                        e.printStackTrace();
                    }
                    //decoding bitmap
                    Bitmap bMap = BitmapFactory.decodeStream(imageStream);
                    Scan.setImageURI(selectedImage);// To display selected image in image view
                    int[] intArray = new int[bMap.getWidth() * bMap.getHeight()];
                    // copy pixel data from the Bitmap into the 'intArray' array
                    bMap.getPixels(intArray, 0, bMap.getWidth(), 0, 0, bMap.getWidth(),
                            bMap.getHeight());

                    LuminanceSource source = new RGBLuminanceSource(bMap.getWidth(),
                            bMap.getHeight(), intArray);
                    BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));

                    Reader reader = new MultiFormatReader();// use this otherwise
                    // ChecksumException
                    try {
                        Hashtable<DecodeHintType, Object> decodeHints = new Hashtable<DecodeHintType, Object>();
                        decodeHints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
                        decodeHints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);

                        Result result = reader.decode(bitmap, decodeHints);
         //*I have created a global string variable by the name of barcode to easily manipulate data across the application*//
                        barcode =  result.getText().toString();

                           //do something with the results for demo i created a popup dialog
                        if(barcode!=null){
                            AlertDialog.Builder builder = new AlertDialog.Builder(this);
                            builder.setTitle("Scan Result");
                            builder.setIcon(R.mipmap.ic_launcher);
                            builder.setMessage("" + barcode);
                            AlertDialog alert1 = builder.create();
                            alert1.setButton(DialogInterface.BUTTON_POSITIVE, "Done", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    Intent i = new Intent (getBaseContext(),MainActivity.class);
                                    startActivity(i);
                                }
                            });

                            alert1.setCanceledOnTouchOutside(false);

                            alert1.show();}
                        else
                        {
                            AlertDialog.Builder builder = new AlertDialog.Builder(this);
                            builder.setTitle("Scan Result");
                            builder.setIcon(R.mipmap.ic_launcher);
                            builder.setMessage("Nothing found try a different image or try again");
                            AlertDialog alert1 = builder.create();
                            alert1.setButton(DialogInterface.BUTTON_POSITIVE, "Done", new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    Intent i = new Intent (getBaseContext(),MainActivity.class);
                                    startActivity(i);
                                }
                            });

                            alert1.setCanceledOnTouchOutside(false);

                            alert1.show();

                        }
                     //the end of do something with the button statement.

                    } catch (NotFoundException e) {
                        Toast.makeText(getApplicationContext(), "Nothing Found", Toast.LENGTH_SHORT).show();
                        e.printStackTrace();
                    } catch (ChecksumException e) {
                        Toast.makeText(getApplicationContext(), "Something weird happen, i was probably tired to solve this issue", Toast.LENGTH_SHORT).show();
                        e.printStackTrace();
                    } catch (FormatException e) {
                        Toast.makeText(getApplicationContext(), "Wrong Barcode/QR format", Toast.LENGTH_SHORT).show();
                        e.printStackTrace();
                    } catch (NullPointerException e) {
                        Toast.makeText(getApplicationContext(), "Something weird happen, i was probably tired to solve this issue", Toast.LENGTH_SHORT).show();
                        e.printStackTrace();
                    }
                }
        }
    }

}  

在代码中添加如何回答问题将有助于未来的访问者。 - JAL
1
@LucienMendela 谢谢。它可以工作...但它只能扫描黑白PNG...如何扫描具有不同颜色的QR码? - Abed Putra
问题是相同的,它适用于大多数图片,但对于某些图片不起作用。为了验证生成的QR图片是否正确,我使用了在线工具(http://qrlogo.kaarposoft.dk/qrdecode.html) 进行检查,并且它能够成功地解码。我强烈感觉在Zxing的移动SDK方面有一些遗漏。 - CoDe

1

首先,当然要从图库中读取图片(这可以在你的活动中完成):Help by

Intent pickIntent = new Intent(Intent.ACTION_PICK);
    pickIntent.setDataAndType( android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");

    startActivityForResult(pickIntent, 111);

之后,只需在活动结果中获取图像URI,然后ZXing就会执行其魔力:

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            //the case is because you might be handling multiple request codes here
            case 111:
                if(data == null || data.getData()==null) {
                    Log.e("TAG", "The uri is null, probably the user cancelled the image selection process using the back button.");
                    return;
                }
                Uri uri = data.getData();
                try
                {
                    InputStream inputStream = getContentResolver().openInputStream(uri);
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                    if (bitmap == null)
                    {
                        Log.e("TAG", "uri is not a bitmap," + uri.toString());
                        return;
                    }
                    int width = bitmap.getWidth(), height = bitmap.getHeight();
                    int[] pixels = new int[width * height];
                    bitmap.getPixels(pixels, 0, width, 0, 0, width, height);
                    bitmap.recycle();
                    bitmap = null;
                    RGBLuminanceSource source = new RGBLuminanceSource(width, height, pixels);
                    BinaryBitmap bBitmap = new BinaryBitmap(new HybridBinarizer(source));
                    MultiFormatReader reader = new MultiFormatReader();
                    try
                    {
                        Result result = reader.decode(bBitmap);
                        Toast.makeText(this, "The content of the QR image is: " + result.getText(), Toast.LENGTH_SHORT).show();
                    }
                    catch (NotFoundException e)
                    {
                        Log.e("TAG", "decode exception", e);
                    }
                }
                catch (FileNotFoundException e)
                {
                    Log.e("TAG", "can not open file" + uri.toString(), e);
                }
                break;
        }
    }

0

我也遇到了同样的情况,它抛出了NotFoundException异常,官方文档中说:

当图像中未找到条形码时抛出。可能已经部分检测到但无法确认。

第一级解决方案在某种程度上,@Laurent的答案几乎适用于我所拥有的每个样本,但对于少数样本失败了。

下一级解决方案在reader.decode(..)之前添加decodeHints,由@Lucien Mendela建议,解决了问题。

Hashtable<DecodeHintType, Object> decodeHints = new Hashtable<DecodeHintType, Object>();
decodeHints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE); // Not required in my case
decodeHints.put(DecodeHintType.PURE_BARCODE, Boolean.TRUE);

但最终适用于我的事情

  • 如果您有大图像,可能需要缩小。
  • 它还需要一个 ~矩形形状(在我的情况下为421*402)。

参考资料 跨越的几个相似问题:

您可以使用以下工具验证您拥有的图像:


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接