记录一次识别图片中二维码的过程

记录一次识别图片中二维码的过程

点点

2021-03-28 20:36 阅读 433 喜欢 0

思路

对FTP的目录进行监听,上传图,对图片进行处理。 二维码的位置,在图片的右上角和右下角,因此可以通过切图进行位置的确定,然后对图片进行旋转。 将图片切图后,对每一片进行二维码扫描,并获得二维码的内容 将获得的数据通过接口传递到API中,同时上传压缩后的图片 分步进行

一、FTP目录监听 监听这里我用的是chokidar,代码如下:

var chokidar = require('chokidar'); var config = require('./data'); var Util = require('./Util');//工具类,下面放代码 Util.log('服务器启动,开始对目标目录进行监听:'+config.ftp); var watcherIns = null; watcherIns = chokidar.watch(config.ftp,{ persistent : true }); watcherIns.on('add',function(filePath){ //获得图片,再查找data.json 的内容,配置文件在data.json中 Util.log('监测到有文件添加进入,文件路径为:'+filePath); var newName = Util.guid(filePath);//获得名称 Util.log('随机获得文件的新名字:'+newName) exeFileOpt(newName,filePath); }); function loopExe (){ var fileName = Util.getError(); if(fileName){ Util.log('处理错误信息:['+fileName+']'); exeFileOpt(fileName); }

setTimeout(function(){
    loopExe();    
},10000);//每5s执行一次,超过三次放弃

} loopExe();//执行

function exeFileOpt ( newName,filePath ){ if(filePath){ var createTime= Util.getBirthTime(filePath); Util.log('['+newName+']获得文件创建时间:'+createTime); Util.cache[newName].time = createTime; }

//3.对图片进行分割:4部分,r/b/l/t--右上角、右下角、左下角、左上角;
async.waterfall([
    function(callback){
        Util.split(newName,callback);
    },
    function(fileName,callback){
        Util.rotate(fileName,callback);
    },
    function(fileName,callback){
        Util.copy(fileName,callback);
    },
    function(fileName,callback){
        Util.thumb(fileName,callback);
    },
    function(fileName,callback){
        Util.send(fileName,callback);
    }
],function(err,result){
    if(err){
        console.log(err);
        Util.log('['+newName+']执行过程报错');
        Util.addError();
        Util.pushError(newName);
    }else{
        Util.log('['+newName+']执行完毕');
        Util.addSuccess();
        Util.deleteError(newName);
    }
});

}

二、对图片进行切图,然后扫描二维码。

对于图片的处理,使用的是imagemagick,二维码识别使用的是qrcode-reader. 由于是windows系统,所以安装imagemagick比较简单,直接安装就可以了,放个imagemagick 官网地址。 这里面有一个小坑,安装程序的时候,一定要选中install legacy utilities(e.g. convert),我安装的版本是7.

对于二维码识别,是基于zxing的nodejs版本,识别率不是很高,先把图片处理了一下,尽量提高识别率。 工具类代码:

/* 1.处理图片操作 2.处理二维码 3.处理位置 */

var gm = require('gm');//图片操作 var magick = gm.subClass({imageMagick : true});//实体 // var magick =require('gm').subClass({ imageMagick : true }); // var gm = require('gm'); var fs = require('fs');//文件操作 var QrCode = require('qrcode-reader');//二维码读取 var Jimp = require('jimp');//bitmap var moment = require('moment');//moment var path = require('path'); var async = require('async'); var config = require('./data'); var superagent = require('superagent');

var Util = { cache : {},//存放每一个图片的具体信息 error : {},//存放错误的具体信息 delay : {}//存放延迟处理的文件夹集合 };

/**

//添加错误信息 Util.pushError = function( fileName ){ var info = Util.cache[fileName]; if(info){ //判断error中是否存在 if(Util.error[fileName]){ info.error = info.error + 1; Util.error[fileName] = info; if(info.error > 3){ Util.deleteError(fileName); } }else{ info.error = 1; Util.error[fileName] = info; } } }; //删除错误信息 Util.deleteError = function( fileName ){ var info = Util.error[fileName]; if(info){ Util.error[fileName] = null; delete Util.error[fileName]; } }; //获得第一个错误信息 Util.getError = function(){ var first = null; for(var key in Util.error){ if(key && Util.error[key]){ first =key; break; } } return first; };

//成功+1 Util.addSuccess = function(){ var data = require('./analysis'); var all = data.all || 0; all ++ ; var now = moment(new Date()).format('YYYY-MM-DD'); var today = data[now] || 0; today ++ ; data.all = all; data[now] = today; fs.writeFileSync('./analysis.json',JSON.stringify(data)); }; //失败+1 Util.addError = function(){ var data = require('./analysis'); var all = data.all || 0; all ++ ; var error = data.error || 0; error ++ ; data.all = all; data.error = error; fs.writeFileSync('./analysis.json',JSON.stringify(data)); } //日志处理 Util.log = function(str){ var now = moment(new Date()).format('YYYY-MM-DD HH:mm:ss'); //1.打印 str = typeof str === 'string' ? str : str.toString(); var info = now +'\t'+str+'\n'; // console.log(info); //2.写入文件 var nowDate = moment(new Date()).format('YYYY-MM-DD'); var logPath = path.join(__dirname,'logs',nowDate+'.log'); if(fs.existsSync(logPath)){ fs.appendFile(logPath,info,function(err){ if(err){ console.log(err); } }); }else{ fs.writeFile(logPath,info,function(err){ if(err){ console.log(err); } }); } }; //对时间进行格式化 Util.format = function(time){ return moment(time).format('YYYY-MM-DD HH:mm:ss'); }; //获得文件的创建时间 Util.getBirthTime = function(filePath){ var stats = fs.statSync(filePath); return Util.format(stats.birthtime); }; //获得随机ID Util.guid = function(filePath){ var guid = (+new Date()).toString( 32 ),i = 0; for ( ; i < 5; i++ ) { guid += Math.floor( Math.random() * 65535 ).toString( 32 ); } while(Util.cache[guid]){ guid = Util.guid(); } if(filePath){ Util.cache[guid] = { filePath : filePath, extname : path.extname(filePath) }; } return guid; }; //创建目录 Util.mkdirs = function( dirpath ){ dirpath = path.dirname(dirpath); try{ if (!fs.existsSync(dirpath)) { var pathtmp; dirpath.split(/[/\]/).forEach(function (dirname) { //这里指用/ 或\ 都可以分隔目录 如 linux的/usr/local/services 和windows的 d:\temp\aaaa if (pathtmp) { pathtmp = path.join(pathtmp, dirname); }else { pathtmp = dirname; } if (!fs.existsSync(pathtmp)) { if (!fs.mkdirSync(pathtmp, 0777)) { return false; } } }); } return true; }catch(e){ Util.log('文件夹目录创建失败:'+dirpath+'\n'+e.toString()); return false; } } //对图片进行压缩 Util.thumb = function( fileName ,callback){ var info = Util.cache[fileName]; //将图片压缩 var filePath = info.filePath; var date = moment(new Date()).format('YYYYMMDD'); var extname = path.extname(filePath); //目标路径 var newPath = '/byymoral/'+date+'/'+fileName+'-thumb'+extname; var targetPath = path.join(config.target,newPath); magick(filePath).resize(config.thumbWidth).density(config.dpi).write(targetPath,function(err){ if(err){ Util.log(err.toString()); } info.thumb = newPath; Util.cache[fileName] = info; callback(null,fileName); }); }; //复制图片 Util.copy = function(fileName,callback){ var info = Util.cache[fileName]; var filePath = info.filePath; //给定时间 var date = moment(new Date()).format('YYYYMMDD'); var extname = path.extname(filePath);

var newPath = '/byymoral/'+date+'/'+fileName+extname;
//如果路径不存在,则创建

Util.log('['+fileName+']移动后新路径为'+newPath);
info.newPath = newPath;
Util.cache[fileName] = info;
//将图片移动到某位置
var movePath = path.join(config.target,newPath);
if(Util.mkdirs(movePath)){
    //复制过去,压缩后
    magick(filePath).density(config.dpi).quality(config.compressQuality).write(movePath,function(err){
        if(err){
            var data = fs.readFileSync(filePath);
            fs.writeFileSync(movePath,data);
            Util.log(err.toString());
        }
        Util.log('['+fileName+']复制成功,由'+filePath+'复制到'+newPath);
        callback(null,fileName);
    });
}

}; //对buffer读取二维码内容 Util.readSimple = function(buffer){ var qr = new QrCode(); qr.callback = function(err2, value) { if (err2) { //没有二维码 Util.log('['+fileName+']'+(['右上角','右下角','左下角','左上角'][pos])+'无二维码'); rs.has = false; rs.msg = '图片没有二维码'; cb(null,rs); }else{ rs.has = true; Util.log('['+fileName+']'+(['右上角','右下角','左下角','左上角'][pos])+'二维码内容:'+value.result); rs.result = value.result; //根据宽高计算位置和大小 cb(null,rs); } }; qr.decode(blockimg.bitmap); } //识别二维码 Util.readCode = function(fileName,filePath,pos,cb){ var rs = { has : false, pos : pos }; //读取图片是否有二维码 var buffer = fs.readFileSync(filePath); fs.unlinkSync(filePath); Jimp.read(buffer).then(function(blockimg){ var qr = new QrCode(); qr.callback = function(err2, value) { if (err2) { //没有二维码 Util.log('['+fileName+']'+(['右上角','右下角','左下角','左上角'][pos])+'无二维码'); rs.has = false; rs.msg = '图片没有二维码'; cb(null,rs); }else{ rs.has = true; Util.log('['+fileName+']'+(['右上角','右下角','左下角','左上角'][pos])+'二维码内容:'+value.result); rs.result = value.result; //根据宽高计算位置和大小 cb(null,rs); } }; qr.decode(blockimg.bitmap); }).catch(function(err2){ if(err2){ Util.log(err2.toString()); cb(err2); } }); }; //对图片进行裁切 Util.crop = function(fileName,filePath,targetPath,width,height,x,y,pos,callback){ magick(filePath).crop(width,height,x,y) .contrast(0) .resize(1500) // .sharpen(0,10) // .whiteThreshold(50,50,50) .threshold('75%') .write(targetPath,function(err){ if(err){ Util.log(err.toString()); callback(err); }else{ Util.log('['+fileName+']截取'+(['右上角','右下角','左下角','左上角'][pos])+'图片成功'); Util.readCode(fileName,targetPath,pos,function(err2,rs){ callback(err2,rs); }); } }) }; //获得旋转度数 Util.getDegree = function( arr ){ //四个角,右上角和右下角有--,可能存在的情况 01 12 23 30,如果是单独的则是0 1 2 3 var target = ['01','12','23','03']; var num = 0; var ts = []; var str = [];//内容 //排序 arr.sort(function(a,b){ return a.pos > b.pos; }); arr.forEach(function( item ){ if(item.has){ ts.push(item.pos); str.push(item.result); num ++ ; } }); // if(num == 1){//只有一个,肯定在右上角 var temp = ts[0]; var rs = { degree : -temp * 90, result : str }; return rs; }else if(num == 2){ var temp = ts.sort().join(''); if(target.indexOf(temp)>-1){ var rs = { degree : - target.indexOf(temp) * 90, result : temp === '03' ? str.reverse() : str }; return rs; } } return { degree : 0, result : str }; }; //整理数据,提交到接口 Util.send = function(fileName,callback){ /* * 整理数据: * 1.图片的创建时间 * 2.图片转移后的路径 * 3.二维码的内容 * 4. */ var info = Util.cache[fileName]; var degree = info.degree; var result = ['']; if(degree.result){ result = degree.result; } var data = { createTime : info.time, content0 : result[0], content1 : result.length > 1 ? result[1] : '', filePath : info.newPath, thumb : info.thumb }; Util.log('['+fileName+']调用接口发送数据:'+JSON.stringify(data)); superagent .post(config.api) // .send(data) .field('createTime',info.time) .field('content0',result[0]) .field('content1',result.length >1 ? result[1] : '') //attach file .attach('file',path.join(config.target,info.newPath)) .attach('thumb',path.join(config.target,info.thumb)) .end(function(err,res){ if(err){ Util.log('['+fileName+']调用接口失败'+err.toString()); callback(err); }else{ // var txt = res.text; Util.log('['+fileName+']调用接口成功'); //删除源文件 fs.unlinkSync(info.filePath); Util.log('['+fileName+']删除源文件成功'+info.filePath); callback(null); } }); } //旋转 Util.rotate = function(fileName,callback){ var info = Util.cache[fileName]; if(info && info.degree){ var filePath = info.filePath,degree = info.degree.degree; if(degree !== 0){ magick(filePath).rotate('width',degree).write(filePath,function(err){ if(err){ Util.log('['+fileName+']旋转图片角度失败'); callback(err);//即便是失败也要上传 }else{ Util.log('['+fileName+']旋转图片角度'+degree+'° 成功'); callback(null,fileName); } }); }else{ callback(null,fileName); } }else{ callback(null,fileName);
} }; //图片分割并识别二维码 Util.split = function( fileName,callback ){ var info = Util.cache[fileName]; var filePath = info.filePath; //通过gm对文件进行分割成四个分片并保存 setTimeout(function(){ Jimp.read(filePath).then(function(image){ var width = image.bitmap.width,height = image.bitmap.height; Util.log('['+fileName+']获得图片宽高:w='+width+'px;h='+height+'px'); var hwidth = width /2 ,hheight = height /2 ; var targetPath = path.join(__dirname,'crop'); var ext = path.extname(fileName);

        var rpath = path.join(targetPath,fileName+'-r'+info.extname);
        var bpath = path.join(targetPath,fileName+'-b'+info.extname);
        var lpath = path.join(targetPath,fileName+'-l'+info.extname);
        var tpath = path.join(targetPath,fileName+'-t'+info.extname);

        var hasCode = false;
        //同时进行
        async.parallel([
            function(callback){//右上角,根据宽高比对
                var a,b,c,d;
                if(width >= height){//横向
                    a = width / 4;
                    b = height /2 ;
                    c = width /4 * 3;
                    d = 0;
                }else{
                    a = width /2;
                    b = height /4;
                    c = width /2;
                    d = 0;
                }
                Util.crop(fileName,filePath,rpath,a,b,c,d,0,callback);
            },
            function(callback){//右下角
                var a,b,c,d;
                if(width >= height){//横向
                    a = width /4;
                    b = height /2;
                    c = width /4 * 3;
                    d = height /2;
                }else{
                    a = width /2;
                    b = height /4;
                    c = width /2;
                    d = height/4*3;
                }
                Util.crop(fileName,filePath,bpath,a,b,c,d,1,callback);
            },
            function(callback){//左下角
                var a,b,c,d;
                if(width >= height){//横向
                    a = width /4;
                    b = height /2;
                    c = 0;
                    d = height /2;
                }else{
                    a = width /2;
                    b = height /4;
                    c = 0;
                    d = height /4 *3;
                }
                Util.crop(fileName,filePath,lpath,a,b,c,d,2,callback);
            },
            function(callback){//左上角
                var a,b,c,d;
                if(width >= height){//横向
                    a = width /4;
                    b = height/2;
                    c = 0;
                    d = 0;
                }else{
                    a = width /2;
                    b = height/4;
                    c = 0;
                    d =0;
                }
                Util.crop(fileName,filePath,tpath,a,b,c,d,3,callback);
            }
        ],function(err3,res){
            if(err3){
                Util.log(err3.toString());
                callback(err3);
            }else{
                var degree = Util.getDegree(res);//获得度数
                info.degree = degree;//设置度数信息和二维码内容
                Util.cache[fileName] = info;
                callback(null,fileName);    
            }
        });
    }).catch(function(err){
        if(err){
            Util.log(err.toString())
            callback(err);
        }
    });
},500);

};

module.exports = Util;

主要的处理是:图片切割、二维码识别、图片处理、接口调用。

转载请注明出处: http://sdxlp.cn/article/erweimashibie.html


如果对你有用的话,请赏给作者一个馒头吧 ...或帮点下页面底部的广告,感谢!!

赞赏支持
提交评论
评论信息(请文明评论)
暂无评论,快来快来写想法...
推荐
钉钉电子签名基于钉钉强大的安全保障体系,确保用户签名的真实性和不可篡改性。用户可以在钉钉应用中轻松完成签名操作,无论是审批流程、合同签署还是其他需要签名的场景,都能快速完成签名任务。可以帮到小伙伴们涨知识呦!
随着网络普及,视频、语音聊天成了小伙伴们又一种不可或缺的通讯方式,但是很多的时候,会出现这样或者那样的问题,用QQ语音聊天时对方听不到我的声音,而我能听到对方的声音。针对这个问题,作如下详细解答,帮助小伙伴们轻松解决这个难题。
随着电子技术的不断发展,即时通许软件已经成为我们工作和生活的一部分了。那在电脑上使用微信接收的图片,都保存在哪个文件夹呢?如果想要清理这些图片的话,又有哪些微信图片的清理方法呢?
微信中打开Word、Excel等文档,会保存在一个目录下。但是,微信把这个目录隐藏很深,路径不易记住,所以,我把这个路径记下来。 在安卓系统中可以安装ES文件浏览器来获得。如果找不到这个路径,或者显示为空,也可以转发到另一台安卓机上的微信号,在那台打开后再保存下来。
一个切小图标的方法
现如今智能手机中华为手机成为了主流之一,用华为手机如何保存微信聊天记录?在华为帐号里面,进入到云备份功能,选择好要恢复的微信数据,进行恢复成功即可恢复微信聊天记录。
如果在没有wifi的情况下,我们接收qq信息就要流量。尤其是图片,QQ里加入的群很多,接收图片就很费流量。在这样的情况下我们可以设置屏蔽群图片,手机qq怎么设置屏蔽群图片呢?下面为小伙伴们介绍。
苹果最新的iPhone13系列手机都是支持NFC功能,但是很多小伙伴在想要使用的同时,不知道这个功能怎么才能开启,想要使用但是不知道怎么操作,那么下面就让点点给小伙伴们介绍一下具体的功能开启方式,详细的展示一下操作步骤。