百度OCR文字識別實現
阿新 • • 發佈:2019-01-24
需求:老婆工作需要,要做一個貼上或上傳圖片後,能自動識別圖片中的文字和數字以及英文的功能。
思考及調研:查閱了一些相關資料,發現很多大的廠商(百度、阿里、騰訊等)已有相關的開放介面供應,可滿足不同客戶需要。主要就是OCR功能,不瞭解的可以自行度娘。
話不多說,開始編碼。(因該功能我是整合到了自己的一個自有專案中,不便貼完整,所以本文貼的程式碼都為主體程式碼,是完全可以實現的。)
我已放到了自己的阿里雲伺服器上,想看效果的朋友可以看看: 文字識別 賬戶:ocr 密碼:123456
一、前端頁面
使用的是百度的webuploader,可直接截圖貼上,可拖拽式上傳
index.html
<!DOCTYPE html> <html> <head> <meta charset="utf-8"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/> <title>文字識別</title> <link rel="shortcut icon" href="favicon.ico"/> <link href="/css/bootstrap.min.css?v=3.3.6" rel="stylesheet"/> <link href="/css/font-awesome.css?v=4.4.0" rel="stylesheet"/> <link href="/css/animate.css" rel="stylesheet"/> <link href="/css/style.css?v=4.1.0" rel="stylesheet"/> <link rel="stylesheet" type="text/css" href="/css/plugins/webuploader/webuploader.css"/> <link rel="stylesheet" type="text/css" href="/css/demo/webuploader-demo.css"/> </head> <body class="gray-bg"> <div class="wrapper wrapper-content animated fadeInRight"> <div class="row"> <div class="ibox"> <div class="ibox-title"> <h5>圖片上傳</h5> </div> <div class="ibox-content"> <div class="row row-lg"> <div class="alert alert-success" id="ocrResult"> <p> 您可以嘗試檔案拖拽,使用QQ截圖工具,然後啟用視窗後貼上,或者點選新增圖片按鈕,來上傳圖片。</p> </div> <div id="uploader" class="wu-example"> <div class="queueList"> <div id="dndArea" class="placeholder"> <div id="filePicker"></div> <p>將照片拖到這裡,一次只可上傳一張。</p> </div> </div> <div class="statusBar" style="display:none;"> <div class="progress"> <span class="text">0%</span> <span class="percentage"></span> </div><div class="info"></div> <div class="btns"> <div id="filePicker2"></div><div class="uploadBtn">開始上傳</div> </div> </div> </div> </div> </div> </div> </div> <div class="row"> <div class="ibox"> <div class="ibox-title"> <h5>轉換結果</h5> </div> <div class="ibox-content"> <div class="row row-lg"> <div class="col-sm-12"> <div class="alert alert-info"> <p id="orcResult">...</p> </div> </div> </div> </div> </div> </div> </div> <!-- 全域性js --> <script src="/js/jquery.min.js?v=2.1.4"></script> <script src="/js/bootstrap.min.js?v=3.3.6"></script> <!-- 自定義js --> <script src="/js/content.js?v=1.0.0"></script> <script type="text/javascript"> // 新增全域性站點資訊 var BASE_URL = '/js/plugins/webuploader'; </script> <script src="/js/plugins/webuploader/webuploader.min.js"></script> <script src="/js/webuploader/webuploader-demo.js"></script> </body> </html>
webuploader-demo.js
jQuery(function() { var $ = jQuery, // just in case. Make sure it's not an other libaray. $wrap = $('#uploader'), // 圖片容器 $queue = $('<ul class="filelist"></ul>') .appendTo( $wrap.find('.queueList') ), // 狀態列,包括進度和控制按鈕 $statusBar = $wrap.find('.statusBar'), // 檔案總體選擇資訊。 $info = $statusBar.find('.info'), // 上傳按鈕 $upload = $wrap.find('.uploadBtn'), // 沒選擇檔案之前的內容。 $placeHolder = $wrap.find('.placeholder'), // 總體進度條 $progress = $statusBar.find('.progress').hide(), // 新增的檔案數量 fileCount = 0, // 新增的檔案總大小 fileSize = 0, // 優化retina, 在retina下這個值是2 ratio = window.devicePixelRatio || 1, // 縮圖大小 thumbnailWidth = 110 * ratio, thumbnailHeight = 110 * ratio, // 可能有pedding, ready, uploading, confirm, done. state = 'pedding', // 所有檔案的進度資訊,key為file id percentages = {}, supportTransition = (function(){ var s = document.createElement('p').style, r = 'transition' in s || 'WebkitTransition' in s || 'MozTransition' in s || 'msTransition' in s || 'OTransition' in s; s = null; return r; })(), // WebUploader例項 uploader; if ( !WebUploader.Uploader.support() ) { alert( 'Web Uploader 不支援您的瀏覽器!如果你使用的是IE瀏覽器,請嘗試升級 flash 播放器'); throw new Error( 'WebUploader does not support the browser you are using.' ); } // 例項化 uploader = WebUploader.create({ pick: { id: '#filePicker', label: '點選選擇圖片' }, dnd: '#uploader .queueList', paste: document.body, accept: { title: 'Images', extensions: 'jpg,jpeg,bmp,png,JPG,JPEG,BMP,PNG', mimeTypes: 'image/*' }, // swf檔案路徑 swf: BASE_URL + '/Uploader.swf', disableGlobalDnd: true, chunked: false, server: '/ocr/upload', fileNumLimit: 1, //檔案數 fileSizeLimit: 5 * 1024 * 1024, // 50 M fileSingleSizeLimit: 3 * 1024 * 1024, // 3 M }); // 新增“新增檔案”的按鈕, /*uploader.addButton({ id: '#filePicker2', label: '繼續新增' });*/ // 當有檔案新增進來時執行,負責view的建立 function addFile( file ) { var $li = $( '<li id="' + file.id + '">' + '<p class="title">' + file.name + '</p>' + '<p class="imgWrap"></p>'+ '<p class="progress"><span></span></p>' + '</li>' ), $btns = $('<div class="file-panel">' + '<span class="cancel">刪除</span>' + '<span class="rotateRight">向右旋轉</span>' + '<span class="rotateLeft">向左旋轉</span></div>').appendTo( $li ), $prgress = $li.find('p.progress span'), $wrap = $li.find( 'p.imgWrap' ), $info = $('<p class="error"></p>'), showError = function( code ) { switch( code ) { case 'exceed_size': text = '檔案大小超出'; break; case 'interrupt': text = '上傳暫停'; break; default: text = '上傳失敗,請重試'; break; } $info.text( text ).appendTo( $li ); }; if ( file.getStatus() === 'invalid' ) { showError( file.statusText ); } else { // @todo lazyload $wrap.text( '預覽中' ); uploader.makeThumb( file, function( error, src ) { if ( error ) { $wrap.text( '不能預覽' ); return; } var img = $('<img src="'+src+'">'); $wrap.empty().append( img ); }, thumbnailWidth, thumbnailHeight ); percentages[ file.id ] = [ file.size, 0 ]; file.rotation = 0; } file.on('statuschange', function( cur, prev ) { if ( prev === 'progress' ) { $prgress.hide().width(0); } else if ( prev === 'queued' ) { $li.off( 'mouseenter mouseleave' ); $btns.remove(); } // 成功 if ( cur === 'error' || cur === 'invalid' ) { console.log( file.statusText ); showError( file.statusText ); percentages[ file.id ][ 1 ] = 1; } else if ( cur === 'interrupt' ) { showError( 'interrupt' ); } else if ( cur === 'queued' ) { percentages[ file.id ][ 1 ] = 0; } else if ( cur === 'progress' ) { $info.remove(); $prgress.css('display', 'block'); } else if ( cur === 'complete' ) { $li.append( '<span class="success"></span>' ); } $li.removeClass( 'state-' + prev ).addClass( 'state-' + cur ); }); $li.on( 'mouseenter', function() { $btns.stop().animate({height: 30}); }); $li.on( 'mouseleave', function() { $btns.stop().animate({height: 0}); }); $btns.on( 'click', 'span', function() { var index = $(this).index(), deg; switch ( index ) { case 0: uploader.removeFile( file ); return; case 1: file.rotation += 90; break; case 2: file.rotation -= 90; break; } if ( supportTransition ) { deg = 'rotate(' + file.rotation + 'deg)'; $wrap.css({ '-webkit-transform': deg, '-mos-transform': deg, '-o-transform': deg, 'transform': deg }); } else { $wrap.css( 'filter', 'progid:DXImageTransform.Microsoft.BasicImage(rotation='+ (~~((file.rotation/90)%4 + 4)%4) +')'); } }); $li.appendTo( $queue ); } // 負責view的銷燬 function removeFile( file ) { var $li = $('#'+file.id); delete percentages[ file.id ]; updateTotalProgress(); $li.off().find('.file-panel').off().end().remove(); } function updateTotalProgress() { var loaded = 0, total = 0, spans = $progress.children(), percent; $.each( percentages, function( k, v ) { total += v[ 0 ]; loaded += v[ 0 ] * v[ 1 ]; } ); percent = total ? loaded / total : 0; spans.eq( 0 ).text( Math.round( percent * 100 ) + '%' ); spans.eq( 1 ).css( 'width', Math.round( percent * 100 ) + '%' ); updateStatus(); } function updateStatus() { var text = '', stats; if ( state === 'ready' ) { text = '選中' + fileCount + '張圖片,共' + WebUploader.formatSize( fileSize ) + '。'; } else if ( state === 'confirm' ) { stats = uploader.getStats(); if ( stats.uploadFailNum ) { text = '已成功上傳' + stats.successNum+ '張照片至伺服器,'+ stats.uploadFailNum + '張照片上傳失敗,<a class="retry" href="#">重新上傳</a>失敗圖片或<a class="ignore" href="#">忽略</a>' } } else { stats = uploader.getStats(); text = '共' + fileCount + '張(' + WebUploader.formatSize( fileSize ) + '),已上傳' + stats.successNum + '張'; if ( stats.uploadFailNum ) { text += ',失敗' + stats.uploadFailNum + '張'; } } $info.html( text ); } function setState( val ) { var file, stats; if ( val === state ) { return; } $upload.removeClass( 'state-' + state ); $upload.addClass( 'state-' + val ); state = val; switch ( state ) { case 'pedding': $placeHolder.removeClass( 'element-invisible' ); $queue.parent().removeClass('filled'); $queue.hide(); $statusBar.addClass( 'element-invisible' ); uploader.refresh(); break; case 'ready': $placeHolder.addClass( 'element-invisible' ); $( '#filePicker2' ).removeClass( 'element-invisible'); $queue.parent().addClass('filled'); $queue.show(); $statusBar.removeClass('element-invisible'); uploader.refresh(); break; case 'uploading': $( '#filePicker2' ).addClass( 'element-invisible' ); $progress.show(); $upload.text( '暫停上傳' ); break; case 'paused': $progress.show(); $upload.text( '繼續上傳' ); break; case 'confirm': $progress.hide(); $upload.text( '開始上傳' ).addClass( 'disabled' ); stats = uploader.getStats(); if ( stats.successNum && !stats.uploadFailNum ) { setState( 'finish' ); return; } break; case 'finish': stats = uploader.getStats(); if ( stats.successNum ) { $upload.text( '上傳成功' ); //alert( '上傳成功' ); } else { // 沒有成功的圖片,重設 state = 'done'; location.reload(); } break; } updateStatus(); } uploader.onUploadProgress = function( file, percentage ) { var $li = $('#'+file.id), $percent = $li.find('.progress span'); $percent.css( 'width', percentage * 100 + '%' ); percentages[ file.id ][ 1 ] = percentage; updateTotalProgress(); }; uploader.onFileQueued = function( file ) { fileCount++; fileSize += file.size; if ( fileCount === 1 ) { $placeHolder.addClass( 'element-invisible' ); $statusBar.show(); } addFile( file ); setState( 'ready' ); updateTotalProgress(); }; uploader.onFileDequeued = function( file ) { fileCount--; fileSize -= file.size; if ( !fileCount ) { setState( 'pedding' ); } removeFile( file ); updateTotalProgress(); }; uploader.on( 'all', function( type ) { switch( type ) { case 'uploadFinished': setState( 'confirm' ); break; case 'startUpload': setState( 'uploading' ); break; case 'stopUpload': setState( 'paused' ); break; } }); uploader.on( 'uploadSuccess', function( file,response ) { var resultStr = response.data; //alert(resultStr); $( '#orcResult' ).html(resultStr); }); uploader.onError = function( code ) { if(code == 'Q_EXCEED_NUM_LIMIT'){ alert( 'Eroor: 只允許上傳一張圖!' ); }else{ alert( 'Eroor: ' + code ); } }; $upload.on('click', function() { if ( $(this).hasClass( 'disabled' ) ) { return false; } if ( state === 'ready' ) { uploader.upload(); } else if ( state === 'paused' ) { uploader.upload(); } else if ( state === 'uploading' ) { uploader.stop(); } }); $info.on( 'click', '.retry', function() { uploader.retry(); } ); $info.on( 'click', '.ignore', function() { alert( 'todo' ); } ); $upload.addClass( 'state-' + state ); updateTotalProgress(); });
css用的bootstrap的及百度webuploader 中demo自帶的css,這裡就不貼出來了。
二、後端程式碼
這裡因有涉及專案程式碼,不便貼全,至貼出程式碼實現主體部分。
接受前端頁面的圖片資料儲存至伺服器:
OcrController.java
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public void savePic(HttpServletRequest request, HttpServletResponse response){
AjaxRetBean<Object> returnBean = new AjaxRetBean<> ();
try {
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
String picture = "";
MultipartHttpServletRequest muRequest = (MultipartHttpServletRequest)request;
Map<String, MultipartFile> files = muRequest.getFileMap();//得到檔案map物件
String uploadUrl = request.getSession().getServletContext().getRealPath("/")+"upload/";//得到當前工程路徑拼接上檔名
File dir = new File(uploadUrl);
if(!dir.exists())//目錄不存在則建立
dir.mkdirs();
for(MultipartFile file :files.values()){
String fileName= new Date().getTime() + "_" + file.getOriginalFilename();
picture = uploadUrl+fileName;
File upFile = new File(uploadUrl+fileName);//建立檔案物件
if(!upFile.exists()){//檔名不存在 則新建檔案,並將檔案複製到新建檔案中
upFile.createNewFile();
file.transferTo(upFile);
}
}
String str = AipOcrUtil.UploadFile(picture);
returnBean.setSuccess(true);
returnBean.setData(str);
} catch (IOException e) {
logger.error("檔案上傳錯誤:"+e.getMessage());
} catch (Exception e){
logger.error(e.getMessage());
returnBean.setSuccess(false);
returnBean.setMessage(e.getMessage());
}
ResWriteUtil.writeObject(response,returnBean);
}
AipOcrUtil.java
直接使用百度OCR提供的Api介面,介面詳細:百度OCR-java
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baidu.aip.ocr.AipOcr;
import org.json.JSONException;
import java.util.HashMap;
/**
* Author: IrisNew
* Description:百度Ocr文字識別
* Date: 2018/5/8 15:23
*/
public class AipOcrUtil {
//設定APPID/AK/SK 需自行去百度申請哦
private static final String APP_ID = "xxxx";
private static final String API_KEY = "xxxxxx";
private static final String SECRET_KEY = "xxxxxxx";
public static String UploadFile(String path) throws JSONException {
// 初始化一個AipOcr
AipOcr client = new AipOcr(APP_ID, API_KEY, SECRET_KEY);
// 可選:設定網路連線引數
client.setConnectionTimeoutInMillis(2000);
client.setSocketTimeoutInMillis(60000);
// 呼叫介面
String resultStr = "";
org.json.JSONObject res = client.basicGeneral(path, new HashMap<String, String>());
JSONObject jsonObject = JSONObject.parseObject(res.toString());
JSONArray resultList = jsonObject.getJSONArray("words_result");
for(Object result:resultList){
JSONObject temp = (JSONObject) result;
resultStr += temp.getString("words")+"<br/>";
}
return resultStr;
}
}
至此,大功告成。看下效果吧。
三、效果展示
將這個圖貼上試試
成功!
央視網訊息:“綠水青山就是金山銀山”,這是習近平對綠色發展理念的一句經典闡述,如 今這一理念已然沁入民心,深入生活,乃至造福世界 這句話是科學論斷,是治國方略,更是中國智慧的體現和中國方案的亮點,讓世界矚目 萌芽,始於安吉 2005年8月15日,時任淅江省委書記的習近平來到湖州N市安吉縣天荒坪鎮餘村,他以充滿 前瞻性的戰略眼光首次提出“綠水青山就是金山銀山 周後的8月24日,習近平在《淅江日報》發表專欄評 綠水青山也是金山銀山 在文中,習近平這樣說 我們追求人與自然的和諧,經濟與社會的和諧,通俗地講,就是既要綠水青山,又 |