用HTML5 可以实现二维码扫描识别的功能吗

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用HTML5 可以实现二维码扫描识别的功能吗相关的知识,希望对你有一定的参考价值。

可以。
实现基本步骤:
操作摄像头,获取图片。
技术要点:MediaStream、GetUserMedia、File api。
利用canvas使用相关算法分析图片识别图片得出结果。
技术要点:getImageData
* 涉及到的技术点浏览器们(包括移动端)支持各异,现阶段要开发并投产还不太现实
* 识别算法是一个难点,不过谢谢github,谢谢开源社区,@Shou Jiesong 的答案中有phonegap的插件地址,支持各种码。
参考技术A 搜索答案
html5 可以实现二维码扫描识别的功能吗

sSAN2vkI | 浏览 5809 次 2015-03-22 13:06

2015-03-22 19:53最佳答案
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Barcode Example</title>
<script type="text/javascript" >
// 扩展API加载完毕后调用onPlusReady回调函数
document.addEventListener( "plusready", onPlusReady, false );
// 扩展API加载完毕,现在可以正常调用扩展API
function onPlusReady()
var e = document.getElementById("scan");
e.removeAttribute( "disabled" );

// 从图片中扫描
function scanImg()
plus.barcode.scan( '_www/barcode.png', function (type,result)
alert( "Scan success:("+type+")"+result );
, function (error)
alert( error.message );
);

</script>
<style type="text/css">
*
-webkit-user-select: none;

html,body
margin: 0px;
padding: 0px;
height: 100%;

</style>
</head>
<body >
<input id="scan" type='button' disabled="disabled" onclick='scanImg()' value='扫描图片'></input>
</body>
</html>
参考技术B 这个如果是手机的上的话,主要是依靠app自身的后台调用实现的,这个主要不是前端的技术。html5 只是网页的标签等展示。但具体的功能还是要依靠js或者是后端来实现。 参考技术C <!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Barcode Example</title>
<script type="text/javascript" >
// 扩展API加载完毕后调用onPlusReady回调函数
document.addEventListener( "plusready", onPlusReady, false );
// 扩展API加载完毕,现在可以正常调用扩展API
function onPlusReady()
var e = document.getElementById("scan");
e.removeAttribute( "disabled" );

// 从图片中扫描
function scanImg()
plus.barcode.scan( '_www/barcode.png', function (type,result)
alert( "Scan success:("+type+")"+result );
, function (error)
alert( error.message );
);

</script>
<style type="text/css">
*
-webkit-user-select: none;

html,body
margin: 0px;
padding: 0px;
height: 100%;

</style>
</head>
<body >
<input id="scan" type='button' disabled="disabled" onclick='scanImg()' value='扫描图片'></input>
</body>
</html>

Android 基于google Zxing实现二维码的生成,识别和长按识别的效果

最近项目用到了二维码的生成与识别,之前没有接触这块,然后就上网搜了搜,发现有好多这方面的资源,特别是google Zxing对二维码的封装,实现的已经不错了,可以直接拿过来引用,下载了他们的源码后,只做了少少的改动,就是在Demo中增加了长按识别的功能,网上虽然也有长按识别的Demo,但好多下载下来却无法运行,然后总结了一下,加在了下面的Demo中。   



如图所示,引用时直接把用红色圈起来的包放在你项目所对应的文件夹下,当然一些资源文件,比如string.xml里项目的引用你自己添加上就是 当然别忘了放架包


下面来介绍这个Demo的主类

public class BarCodeTestActivity extends Activity {
   
private TextView resultTextView;
private EditText qrStrEditText;
private ImageView qrImgImageView;
private String time;
    private File file = null; 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);        
        resultTextView = (TextView) this.findViewById(R.id.tv_scan_result);
        qrStrEditText = (EditText) this.findViewById(R.id.et_qr_string);
        qrImgImageView = (ImageView) this.findViewById(R.id.iv_qr_image);
        
        Button scanBarCodeButton = (Button) this.findViewById(R.id.btn_scan_barcode);
        scanBarCodeButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
//打开扫描界面扫描条形码或二维码
Intent openCameraIntent = new Intent(BarCodeTestActivity.this,CaptureActivity.class);
startActivityForResult(openCameraIntent, 0);
}
});
        
        qrImgImageView.setOnLongClickListener(new OnLongClickListener() {

@Override
public boolean onLongClick(View v) {
// 长按识别二维码

  saveCurrentImage();
return true;
}
});
        
        


        
        Button generateQRCodeButton = (Button) this.findViewById(R.id.btn_add_qrcode);
        generateQRCodeButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
try {
String contentString = qrStrEditText.getText().toString();
if (!contentString.equals("")) {
//根据字符串生成二维码图片并显示在界面上,第二个参数为图片的大小(350*350)
Bitmap qrCodeBitmap = EncodingHandler.createQRCode(contentString, 350);
qrImgImageView.setImageBitmap(qrCodeBitmap);
}else {
//提示文本不能是空的
Toast.makeText(BarCodeTestActivity.this, "Text can not be empty", Toast.LENGTH_SHORT).show();
}

} catch (WriterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
    }
 
    //这种方法状态栏是空白,显示不了状态栏的信息
    private void saveCurrentImage()
    {
        //获取当前屏幕的大小
        int width = getWindow().getDecorView().getRootView().getWidth();
        int height = getWindow().getDecorView().getRootView().getHeight();
        //生成相同大小的图片
        Bitmap temBitmap = Bitmap.createBitmap( width, height, Bitmap.Config.ARGB_8888 );
        //找到当前页面的根布局
        View view =  getWindow().getDecorView().getRootView();
        //设置缓存
        view.setDrawingCacheEnabled(true);
        view.buildDrawingCache();
        //从缓存中获取当前屏幕的图片,创建一个DrawingCache的拷贝,因为DrawingCache得到的位图在禁用后会被回收
        temBitmap = view.getDrawingCache();
        SimpleDateFormat df = new SimpleDateFormat("yyyymmddhhmmss");
        time = df.format(new Date());
        if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())){
            file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/screen",time + ".png");
            if(!file.exists()){
                file.getParentFile().mkdirs();
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            FileOutputStream fos = null;
            try {
                fos = new FileOutputStream(file);
                temBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
                fos.flush();
                fos.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            new Thread(new Runnable() {
                @Override
                public void run() {
                    String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/screen/" + time + ".png";
                    final Result result = parseQRcodeBitmap(path);
                    runOnUiThread(new Runnable() {
                        public void run() {
                        if(null!=result){
                        resultTextView.setText(result.toString());
                        }else{
                            Toast.makeText(BarCodeTestActivity.this, "无法识别", Toast.LENGTH_LONG).show();
                        }
                        }
                    });
                }
            }).start();
            //禁用DrawingCahce否则会影响性能 ,而且不禁止会导致每次截图到保存的是缓存的位图
            view.setDrawingCacheEnabled(false);
        }
    }
    
    
    
    
    
    //解析二维码图片,返回结果封装在Result对象中  
    private com.google.zxing.Result  parseQRcodeBitmap(String bitmapPath){  
        //解析转换类型UTF-8  
        Hashtable<DecodeHintType, String> hints = new Hashtable<DecodeHintType, String>();  
        hints.put(DecodeHintType.CHARACTER_SET, "utf-8");  
        //获取到待解析的图片  
        BitmapFactory.Options options = new BitmapFactory.Options();   
        //如果我们把inJustDecodeBounds设为true,那么BitmapFactory.decodeFile(String path, Options opt)  
        //并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你  
        options.inJustDecodeBounds = true;  
        //此时的bitmap是null,这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了  
        Bitmap bitmap = BitmapFactory.decodeFile(bitmapPath,options);  
        //我们现在想取出来的图片的边长(二维码图片是正方形的)设置为400像素  
        /** 
            options.outHeight = 400; 
            options.outWidth = 400; 
            options.inJustDecodeBounds = false; 
            bitmap = BitmapFactory.decodeFile(bitmapPath, options); 
        */  
        //以上这种做法,虽然把bitmap限定到了我们要的大小,但是并没有节约内存,如果要节约内存,我们还需要使用inSimpleSize这个属性  
        options.inSampleSize = options.outHeight / 400;  
        if(options.inSampleSize <= 0){  
            options.inSampleSize = 1; //防止其值小于或等于0  
        }  
        /** 
         * 辅助节约内存设置 
         *  
         * options.inPreferredConfig = Bitmap.Config.ARGB_4444;    // 默认是Bitmap.Config.ARGB_8888 
         * options.inPurgeable = true;  
         * options.inInputShareable = true;  
         */  
        options.inJustDecodeBounds = false;  
        bitmap = BitmapFactory.decodeFile(bitmapPath, options);   
        //新建一个RGBLuminanceSource对象,将bitmap图片传给此对象  
        RGBLuminanceSource rgbLuminanceSource = new RGBLuminanceSource(bitmap);  
        //将图片转换成二进制图片  
        BinaryBitmap binaryBitmap = new BinaryBitmap(new HybridBinarizer(rgbLuminanceSource));  
        //初始化解析对象  
        QRCodeReader reader = new QRCodeReader();  
        //开始解析  
        Result result = null;  
        try {  
            result = reader.decode(binaryBitmap, hints);  
        } catch (Exception e) {  
            // TODO: handle exception  
        }  
          
        return result;  
    }  
  
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//处理扫描结果(在界面上显示)
if (resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
String scanResult = bundle.getString("result");
resultTextView.setText(scanResult);
}
}
}


然后长按识别二维码调用了RGBLuminanceSource这个类

public class RGBLuminanceSource  extends LuminanceSource {


private byte bitmapPixels[];


protected RGBLuminanceSource(Bitmap bitmap) {
super(bitmap.getWidth(), bitmap.getHeight());


// 首先,要取得该图片的像素数组内容
int[] data = new int[bitmap.getWidth() * bitmap.getHeight()];
this.bitmapPixels = new byte[bitmap.getWidth() * bitmap.getHeight()];
bitmap.getPixels(data, 0, getWidth(), 0, 0, getWidth(), getHeight());


// 将int数组转换为byte数组,也就是取像素值中蓝色值部分作为辨析内容
for (int i = 0; i < data.length; i++) {
this.bitmapPixels[i] = (byte) data[i];
}
}


@Override
public byte[] getMatrix() {
// 返回我们生成好的像素数据
return bitmapPixels;
}


@Override
public byte[] getRow(int y, byte[] row) {
// 这里要得到指定行的像素数据
System.arraycopy(bitmapPixels, y * getWidth(), row, 0, getWidth());
return row;
}
}
相机识别二维码调用了CaptureActivity这个类
public class CaptureActivity extends Activity implements Callback {


private CaptureActivityHandler handler;
private ViewfinderView viewfinderView;
private boolean hasSurface;
private Vector<BarcodeFormat> decodeFormats;
private String characterSet;
private InactivityTimer inactivityTimer;
private MediaPlayer mediaPlayer;
private boolean playBeep;
private static final float BEEP_VOLUME = 0.10f;
private boolean vibrate;
private Button cancelScanButton;


/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.camera);

CameraManager.init(getApplication());
viewfinderView = (ViewfinderView) findViewById(R.id.viewfinder_view);
cancelScanButton = (Button) this.findViewById(R.id.btn_cancel_scan);
hasSurface = false;
inactivityTimer = new InactivityTimer(this);
}


@Override
protected void onResume() {
super.onResume();
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.preview_view);
SurfaceHolder surfaceHolder = surfaceView.getHolder();
if (hasSurface) {
initCamera(surfaceHolder);
} else {
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
decodeFormats = null;
characterSet = null;


playBeep = true;
AudioManager audioService = (AudioManager) getSystemService(AUDIO_SERVICE);
if (audioService.getRingerMode() != AudioManager.RINGER_MODE_NORMAL) {
playBeep = false;
}
initBeepSound();
vibrate = true;

//quit the scan view
cancelScanButton.setOnClickListener(new OnClickListener() {

@Override
public void onClick(View v) {
CaptureActivity.this.finish();
}
});
}


@Override
protected void onPause() {
super.onPause();
if (handler != null) {
handler.quitSynchronously();
handler = null;
}
CameraManager.get().closeDriver();
}


@Override
protected void onDestroy() {
inactivityTimer.shutdown();
super.onDestroy();
}

/**
* Handler scan result
* @param result
* @param barcode
*/
public void handleDecode(Result result, Bitmap barcode) {
inactivityTimer.onActivity();
playBeepSoundAndVibrate();
String resultString = result.getText();
//FIXME
if (resultString.equals("")) {
//扫描失败
Toast.makeText(CaptureActivity.this, "Scan failed!", Toast.LENGTH_SHORT).show();
}else {
// System.out.println("Result:"+resultString);
Intent resultIntent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("result", resultString);
resultIntent.putExtras(bundle);
this.setResult(RESULT_OK, resultIntent);
}
CaptureActivity.this.finish();
}

private void initCamera(SurfaceHolder surfaceHolder) {
try {
CameraManager.get().openDriver(surfaceHolder);
} catch (IOException ioe) {
return;
} catch (RuntimeException e) {
return;
}
if (handler == null) {
handler = new CaptureActivityHandler(this, decodeFormats,
characterSet);
}
}


@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {


}


@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!hasSurface) {
hasSurface = true;
initCamera(holder);
}


}


@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;


}


public ViewfinderView getViewfinderView() {
return viewfinderView;
}


public Handler getHandler() {
return handler;
}


public void drawViewfinder() {
viewfinderView.drawViewfinder();


}


private void initBeepSound() {
if (playBeep && mediaPlayer == null) {
// The volume on STREAM_SYSTEM is not adjustable, and users found it
// too loud,
// so we now play on the music stream.
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setOnCompletionListener(beepListener);


AssetFileDescriptor file = getResources().openRawResourceFd(
R.raw.beep);
try {
mediaPlayer.setDataSource(file.getFileDescriptor(),
file.getStartOffset(), file.getLength());
file.close();
mediaPlayer.setVolume(BEEP_VOLUME, BEEP_VOLUME);
mediaPlayer.prepare();
} catch (IOException e) {
mediaPlayer = null;
}
}
}


private static final long VIBRATE_DURATION = 200L;


private void playBeepSoundAndVibrate() {
if (playBeep && mediaPlayer != null) {
mediaPlayer.start();
}
if (vibrate) {
Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
vibrator.vibrate(VIBRATE_DURATION);
}
}


/**
* When the beep has finished playing, rewind to queue up another one.
*/
private final OnCompletionListener beepListener = new OnCompletionListener() {
public void onCompletion(MediaPlayer mediaPlayer) {
mediaPlayer.seekTo(0);
}
};


}


下面是主布局mian文件

<?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:background="@android:color/white"
    android:orientation="vertical" >


    <Button
        android:id="@+id/btn_scan_barcode"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:text="Open camera" />
    
    <LinearLayout 
        android:orientation="horizontal"
        android:layout_marginTop="10dp"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content">
        
        <TextView 
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="18sp"
        android:text="Scan result:" />
        
        <TextView 
        android:id="@+id/tv_scan_result"
        android:layout_width="fill_parent"
        android:textSize="18sp"
        android:textColor="@android:color/black"
        android:layout_height="wrap_content" />
    </LinearLayout>
    
    <EditText 
        android:id="@+id/et_qr_string"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:hint="Input the text"/>
    
    <Button
        android:id="@+id/btn_add_qrcode"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Generate QRcode" />
    
    <ImageView 
        android:id="@+id/iv_qr_image"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:scaleType="fitXY"
        android:layout_marginTop="10dp"
        android:layout_gravity="center"/>


</LinearLayout>


详细了解 的请下载demo自己看,Demo中解决了在竖拍解码时二维码被拉伸的现象。

不过我遇到了一个问题是 二维码的扫描框调大后,扫描的灵敏度降低了,希望知道的朋友给指导下

有兴趣的可以下载Demo看一看

http://download.csdn.net/detail/shihuiyun/9537714

以上是关于用HTML5 可以实现二维码扫描识别的功能吗的主要内容,如果未能解决你的问题,请参考以下文章

.net 手机扫描二维码功能

发票二维码怎么识别?发票二维码识别的办法是?

html5怎么做扫描二维码的功能

Android 基于google Zxing实现二维码的生成,识别和长按识别的效果

二维码的生成

html5怎么扫描二维码