欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 解析二维码图片的两种方法介绍

程序员文章站 2022-11-20 12:24:06
做了一个二维码扫描图片,主要是扫描不出来,看到一篇博客,其中的第二种方法可以扫描到,在此做笔记,以备后用,前面的进入相册,返回,到获取图片路径方法都一样; 1.项目需求:知道项目需求,才知道先从哪里...

做了一个二维码扫描图片,主要是扫描不出来,看到一篇博客,其中的第二种方法可以扫描到,在此做笔记,以备后用,前面的进入相册,返回,到获取图片路径方法都一样;

1.项目需求:知道项目需求,才知道先从哪里入手,见图一。(点击相册,打开图库)

Android 解析二维码图片的两种方法介绍

2.代码:

1.打开图库代码:

mbtnopenpicture.setonclicklistener(new view.onclicklistener() {
@override
public void onclick(view v) {
 //打开相册
 opengallery();
}
  });



/**打开相册*/
private void opengallery() {
  intent picture = new intent(intent.action_pick,android.provider.mediastore.images.media.external_content_uri);
  startactivityforresult(picture, picture);
 }

2.获取图片路径和解析。

(1). 获取图片路径:getpath(uri);由于android版本不同,返回的uri会有所不同,需要做特殊处理。


@suppresslint("newapi")
 private string getpath(uri uri) {
  int sdkversion = build.version.sdk_int;
  if (sdkversion >= 19) {
//log.e("hxy", "uri auth: " + uri.getauthority());
if (isexternalstoragedocument(uri)) {
 string docid = documentscontract.getdocumentid(uri);
 string[] split = docid.split(":");
 string type = split[0];
 if ("primary".equalsignorecase(type)) {
  return environment.getexternalstoragedirectory() + "/" + split[1];
 }
} else if (isdownloadsdocument(uri)) {
 final string id = documentscontract.getdocumentid(uri);
 final uri contenturi = contenturis.withappendedid(uri.parse("content://downloads/public_downloads"),
long.valueof(id));
 return getdatacolumn(this, contenturi, null, null);
} else if (ismediadocument(uri)) {
 final string docid = documentscontract.getdocumentid(uri);
 final string[] split = docid.split(":");
 final string type = split[0];
 uri contenturi = null;
 if ("image".equals(type)) {
  contenturi = mediastore.images.media.external_content_uri;
 } else if ("video".equals(type)) {
  contenturi = mediastore.video.media.external_content_uri;
 } else if ("audio".equals(type)) {
  contenturi = mediastore.audio.media.external_content_uri;
 }
 final string selection = "_id=";
 final string[] selectionargs = new string[]{split[1]};
 return getdatacolumn(this, contenturi, selection, selectionargs);
} else if (ismedia(uri)) {
 string[] proj = {mediastore.images.media.data};
 cursor actualimagecursor = this.managedquery(uri, proj, null, null, null);
 int actual_image_column_index = actualimagecursor.getcolumnindexorthrow(mediastore.images.media.data);
 actualimagecursor.movetofirst();
 return actualimagecursor.getstring(actual_image_column_index);
}
  } else if ("content".equalsignorecase(uri.getscheme())) {
if (isgooglephotosuri(uri))
 return uri.getlastpathsegment();
return getdatacolumn(this, uri, null, null);
  }
  // file
  else if ("file".equalsignorecase(uri.getscheme())) {
return uri.getpath();
  }
  return null;
 }

public static string getdatacolumn(context context, uri uri,
string selection, string[] selectionargs) {

  cursor cursor = null;
  final string column = "_data";
  final string[] projection = { column };

  try {
cursor = context.getcontentresolver().query(uri, projection,
  selection, selectionargs, null);
if (cursor != null && cursor.movetofirst()) {
 final int column_index = cursor.getcolumnindexorthrow(column);
 return cursor.getstring(column_index);
}
  } finally {
if (cursor != null)
 cursor.close();
  }
  return null;
 }

 private static boolean isexternalstoragedocument(uri uri) {
  return "com.android.externalstorage.documents".equals(uri.getauthority());
 }

 public static boolean isdownloadsdocument(uri uri) {
  return "com.android.providers.downloads.documents".equals(uri.getauthority());
 }

 public static boolean ismediadocument(uri uri) {
  return "com.android.providers.media.documents".equals(uri.getauthority());
 }

 public static boolean ismedia(uri uri) {
  return "media".equals(uri.getauthority());
 }

 public static boolean isgooglephotosuri(uri uri) {
  return "com.google.android.apps.photos.content".equals(uri.getauthority());
 }

(2).开辟线程,解析图片,封装到result中:我看了网上的demo,大致上有2种方法。如下是第一种,稍后会把第二种方法也贴出来。


/**
  * 解析二维码图片
  * @param path
  * @return
  */
 protected result scanningimage(string path) {
  if (textutils.isempty(path)) {
return null;

  }
  hashtable hints = new hashtable();
  hints.put(decodehinttype.character_set, "utf-8"); // 设置二维码内容的编码
  bitmapfactory.options options = new bitmapfactory.options();
  options.injustdecodebounds = true; // 先获取原大小
  scanbitmap = bitmapfactory.decodefile(path,options);
  options.injustdecodebounds = false;
  int samplesize = (int) (options.outheight / (float) 200);
  if (samplesize <= 0)
samplesize = 1;

  options.insamplesize = samplesize;

  scanbitmap = bitmapfactory.decodefile(path, options);
  int[] data = new int[scanbitmap.getwidth() * scanbitmap.getheight()];
  scanbitmap.getpixels(data, 0, scanbitmap.getwidth(), 0, 0, scanbitmap.getwidth(), scanbitmap.getheight());
  rgbluminancesource rgbluminancesource = new rgbluminancesource(scanbitmap.getwidth(),scanbitmap.getheight(),data);
  binarybitmap binarybitmap = new binarybitmap(new hybridbinarizer(rgbluminancesource));
  qrcodereader reader = new qrcodereader();
  result result = null;
  try {
result = reader.decode(binarybitmap, hints);
  } catch (notfoundexception e) {
log.e("hxy","notfoundexception");
  }catch (checksumexception e){
log.e("hxy","checksumexception");
  }catch(formatexception e){
log.e("hxy","formatexception");
  }

  return result;


 }

注意:上面方法中有个关键的类,rgbluminancesource。 我把这个类的构造方法贴出来,因为我做的时候,发现网上demo 这个类中构造传递的都是bitmap, 而我这个类却不是。分析传递的参数之后,我做了个转化:见如下,然后会发现报:notfoundexception. 这个异常是在qrcodereader类:private static bitmatrix extractpurebits(bitmatrix image) throws notfoundexception 。(到这里我已经不是很懂了,然后又去网上搜索了下,最后自己探索出加scanbitmap.getpixels(data, 0, scanbitmap.getwidth(), 0, 0, scanbitmap.getwidth(), scanbitmap.getheight());),运行正常,能解析出来。


int[] data = new int[scanbitmap.getwidth() * scanbitmap.getheight()];
//一定要加以下这个代码:
//scanbitmap.getpixels(data, 0, scanbitmap.getwidth(), 0, 0, scanbitmap.getwidth(), //scanbitmap.getheight());
rgbluminancesource rgbluminancesource = new rgbluminancesource(scanbitmap.getwidth(),scanbitmap.getheight(),data);

public rgbluminancesource(int width, int height, int[] pixels) {
  super(width, height);
  this.datawidth = width;
  this.dataheight = height;
  this.left = 0;
  this.top = 0;
  this.luminances = new byte[width * height];

  for(int y = 0; y < height; ++y) {
int offset = y * width;

for(int x = 0; x < width; ++x) {
 int pixel = pixels[offset + x];
 int r = pixel >> 16 & 255;
 int g = pixel >> 8 & 255;
 int b = pixel & 255;
 if(r == g && g == b) {
  this.luminances[offset + x] = (byte)r;
 } else {
  this.luminances[offset + x] = (byte)((r + 2 * g + b) / 4);
 }
}
  }

 }

 

现在来看第二种解析方法:


protected result scanningimage(string path) {
  if (textutils.isempty(path)) {
return null;

  }

  bitmapfactory.options options = new bitmapfactory.options();
  options.injustdecodebounds = true; // 先获取原大小
  scanbitmap = bitmapfactory.decodefile(path,options);
  options.injustdecodebounds = false;
  int samplesize = (int) (options.outheight / (float) 200);
  if (samplesize <= 0)
samplesize = 1;

  options.insamplesize = samplesize;
  scanbitmap = bitmapfactory.decodefile(path, options);
  byte[] data = getyuv420sp(scanbitmap.getwidth(), scanbitmap.getheight(), scanbitmap);

  hashtable hints = new hashtable();
  hints.put(decodehinttype.character_set, "utf-8"); // 设置二维码内容的编码
  hints.put(decodehinttype.try_harder,boolean.true);
  hints.put(decodehinttype.possible_formats, barcodeformat.qr_code);
  planaryuvluminancesource source = new planaryuvluminancesource(data,
 scanbitmap.getwidth(),
 scanbitmap.getheight(),
 0, 0,
 scanbitmap.getwidth(),
 scanbitmap.getheight(),
 false);

  binarybitmap bitmap1 = new binarybitmap(new hybridbinarizer(source));
  qrcodereader reader2= new qrcodereader();
  result result = null;
  try {
result = reader2.decode(bitmap1, hints);
log.e("hxy",result.gettext());
  } catch (notfoundexception e) {
log.e("hxy","notfoundexception");
  }catch (checksumexception e){
log.e("hxy","checksumexception");
  }catch(formatexception e){
log.e("hxy","formatexception");
  }

  return result;


 }



public  byte[] getyuv420sp(int inputwidth, int inputheight,
 bitmap scaled) {
  int[] argb = new int[inputwidth * inputheight];

  scaled.getpixels(argb, 0, inputwidth, 0, 0, inputwidth, inputheight);

  byte[] yuv = new byte[inputwidth * inputheight * 3 / 2];

  encodeyuv420sp(yuv, argb, inputwidth, inputheight);

  scaled.recycle();

  return yuv;
 }


 private void encodeyuv420sp(byte[] yuv420sp, int[] argb, int width,
int height) {
  // 帧图片的像素大小
  final int framesize = width * height;
  // ---yuv数据---
  int y, u, v;
  // y的index从0开始
  int yindex = 0;
  // uv的index从framesize开始
  int uvindex = framesize;

  // ---颜色数据---
//int a, r, g, b;
  int r, g, b;
  //
  int argbindex = 0;
  //

  // ---循环所有像素点,rgb转yuv---
  for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {

 // a is not used obviously
//  a = (argb[argbindex] & 0xff000000) >> 24;
 r = (argb[argbindex] & 0xff0000) >> 16;
 g = (argb[argbindex] & 0xff00) >> 8;
 b = (argb[argbindex] & 0xff);
 //
 argbindex++;

 // well known rgb to yuv algorithm
 y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
 u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
 v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;

 //
 y = math.max(0, math.min(y, 255));
 u = math.max(0, math.min(u, 255));
 v = math.max(0, math.min(v, 255));

 // nv21 has a plane of y and interleaved planes of vu each
 // sampled by a factor of 2
 // meaning for every 4 y pixels there are 1 v and 1 u. note the
 // sampling is every other
 // pixel and every other scanline.
 // ---y---
 yuv420sp[yindex++] = (byte) y;

 // ---uv---
//  if ((j % 2 == 0) && (i % 2 == 0)) {
//
//
// 
//yuv420sp[uvindex++] = (byte) v;
//
//yuv420sp[uvindex++] = (byte) u;
//  }
}
  }
 }

最后2行代码在实际运行的时候,如果是拿一个二维码图片,能正常解析,但是如果不是二维码图片,数组越界。然后我将其注释掉之后,一切正常了。在这里的转化,我没有看懂,只是提供一种解析方案,期待对这方面了解之人能我和探讨。

3.将解析结果回调给调用activity.:


@override
 protected void onactivityresult(int requestcode, int resultcode, intent data) {
  if(resultcode==result_ok&&data != null&&requestcode == picture){
uri selectedimage = data.getdata();
final string pathresult = getpath(selectedimage);
log.e("hxy","pathresult:"+pathresult);

new thread(new runnable() {
 @override
 public void run() {
  result result = scanningimage(pathresult);
  if(result==null){
looper.prepare();
toast.maketext(captureactivity.this, "未识别到二维码",toast.length_long)
  .show();
looper.loop();
  }else{

handledecode(result,new bundle());
// string recode = recode(result.tostring());
// log.e("hxy","recode:"+recode);
// intent data = new intent();
// data.putextra("result", recode);
// setresult(300, data);
//finish();
  }

 }
}).start();
  }
 }

在这里调用handledecode。是由于captureactivity中,将结果就是通过handledecode回调的


/**
  * a valid barcode has been found, so give an indication of success and show
  * the results.
  * 
  * @param rawresult
  *the contents of the barcode.
  * 
  * @param bundle
  *the extras
  */
 public void handledecode(result rawresult, bundle bundle) {
  inactivitytimer.onactivity();
  beepmanager.playbeepsoundandvibrate();

  bundle.putint("width", mcroprect.width());
  bundle.putint("height", mcroprect.height());
  bundle.putstring("result", rawresult.gettext());

//startactivity(new intent(captureactivity.this, resultactivity.class).putextras(bundle));
  setresult(result_ok, new intent().putextras(bundle));
//toast.maketext(this, rawresult.gettext(), toast.length_long);


  finish();
 }