基于Google Vision API和Ionic實(shí)現(xiàn)Web開(kāi)發(fā)中圖像識(shí)別
譯文一、 簡(jiǎn)介
圖像識(shí)別允許計(jì)算機(jī)以一種類似于人類的方式識(shí)別圖像。在過(guò)去,開(kāi)發(fā)人員必須借助于模式識(shí)別這樣復(fù)雜的圖像識(shí)別技術(shù)與算法來(lái)實(shí)現(xiàn)這一目的。如今,借助于Google Vision API,程序員們可以直接使用這些現(xiàn)成工具中提供的圖像識(shí)別功能。
本文將通過(guò)一個(gè)基于Ionic框架構(gòu)建的JavaScript Web應(yīng)用向你展示如何使用Google Vision API實(shí)現(xiàn)圖片識(shí)別。
二、 開(kāi)始工作
要想使用Google Vision API需要上傳一個(gè)JSON文件,此文件中要包含想要檢測(cè)的圖像類型以及該圖像的base64編碼。你需要把這兩部分信息上傳到到Google Vision API端。
下面給出一個(gè)JSON文件的示例︰
- {
- "requests":[
- {
- "image":{
- "content":"base64-encoded-image"
- },
- "features":[
- {
- "type":"LABEL_DETECTION",
- "maxResults":1
- }
- ]
- }
- ]
- }
在此示例中,您必須使用圖像的實(shí)際base64編碼字符串來(lái)代替上面的base64-encoded-image部分。此外,你還需要在屬性features處提供一個(gè)對(duì)象數(shù)組,該數(shù)組包含你想要檢測(cè)的圖像類型信息。其中,LABEL_DETECTION屬性值的作用是通過(guò)指定一個(gè)標(biāo)簽或說(shuō)明信息來(lái)對(duì)圖像進(jìn)行分類。
一旦從服務(wù)器端返回信息,那么返回信息的形式應(yīng)當(dāng)類似如下︰
- {
- "responses": [
- {
- "labelAnnotations": [
- {
- "mid": "/m/0bt9lr",
- "description": "dog",
- "score": 0.89208293
- }
- ]
- }
- ]
- }
因?yàn)樵谇懊娴恼?qǐng)求格式中你指定了LABEL_DETECTION并給maxResults賦值為1;所以,在響應(yīng)數(shù)組中返回的是單個(gè)對(duì)象。在上面的示例中,數(shù)組名是labelAnnotations。
除了使用前面例子中的LABEL_DETECTION外,你還可以使用如下枚舉數(shù)據(jù):
l FACE_DETECTION:檢測(cè)照片中的人臉部分,返回相應(yīng)的坐標(biāo)值,用于根據(jù)檢測(cè)到范圍來(lái)繪制臉部分。
l LANDMARK_DETECTION:檢測(cè)標(biāo)志性建筑,例如悉尼的歌劇院或威爾特郡的巨石陣等。
l LOGO_DETECTION:檢測(cè)不同的公司徽標(biāo)。
l TEXT_DETECTION:采用光學(xué)字符識(shí)別(OCR)技術(shù)從圖像中提取文本。
l SAFE_SEARCH_DETECTION:基于安全搜索參數(shù)對(duì)圖像進(jìn)行分類。這里提供的圖像分類分為:成人類、惡搞類、醫(yī)療類和暴力類等。
三、 注冊(cè)云Vision API
在本文寫(xiě)作的此時(shí),谷歌云Vision API尚處于beta階段,這意味著開(kāi)發(fā)人員可以免費(fèi)嘗試使用。為此,你可以導(dǎo)航到谷歌云平臺(tái)網(wǎng)站(https://cloud.google.com/vision/),點(diǎn)擊按鈕“try to free”進(jìn)行操作。上述操作會(huì)把你導(dǎo)航到一個(gè)頁(yè)面,詢問(wèn)為您的業(yè)務(wù)和信用信息;但別擔(dān)心,谷歌不會(huì)收取你任何超過(guò)300美元費(fèi)用的。
一旦上述操作完成,你就可以在谷歌控制臺(tái)創(chuàng)建一個(gè)新的項(xiàng)目,并支持項(xiàng)目中進(jìn)行付費(fèi),并啟用云Vision API。你建議你跳過(guò)通常的操作過(guò)程,但使用選項(xiàng)“API Key”。請(qǐng)參考下面的圖片:
四、 構(gòu)建應(yīng)用程序
現(xiàn)在,你已準(zhǔn)備好要構(gòu)建應(yīng)用程序了。但首先,我想簡(jiǎn)要概述一下你要構(gòu)建的應(yīng)用程序。該應(yīng)用程序提供了一個(gè)頁(yè)面,其中包含與云Vision API進(jìn)行交互所需的所有元素。其中,提供了一個(gè)下拉列表用于選擇用戶想使用哪種類型的圖像檢測(cè),用于拍照的按鈕,用于顯示要拍的照片,一個(gè)標(biāo)題部分用于描述顯示的圖片信息。
下面給出的是最終的應(yīng)用程序的外觀形式︰
你可以在GitHub網(wǎng)站上找到本示例工程的源碼(https://github.com/sitepoint-editors/ionic-vision)。
(一) 安裝依賴性
在你的工作目錄中,打開(kāi)一個(gè)新的終端窗口并建議通過(guò)如下方式安裝Cordova和Ionic︰
- npm install -g cordova ionic
然后,通過(guò)如下命令使用空白模板創(chuàng)建一個(gè)新的Ionic項(xiàng)目︰
- ionic start ionic-vision blank
接下來(lái),添加您想要使用的平臺(tái)。我只想安裝Android系統(tǒng),但是這些代碼應(yīng)該也可以工作在iOS平臺(tái)上。命令如下:
- ionic platform add android
接下來(lái),你需要安裝幾個(gè)插件,以便與設(shè)備API進(jìn)行交互,從而可以使用相機(jī)、文件和及上傳文件等功能。相關(guān)命令如下:
- cordova plugin add cordova-plugin-camera
- cordova plugin add cordova-plugin-file
- cordova plugin add cordova-plugin-file-transfer
接下來(lái)再使用bower安裝ngCordova:
- bower install ngCordova
注意:NgCordova庫(kù)提供了針對(duì)要安裝的插件的AngularJS包裝器。這些包裝器使得在一個(gè)Ionic應(yīng)用程序中使用這些插件更為容易。
(二) 添加控制器
現(xiàn)在,打開(kāi)目錄www并在js目錄下創(chuàng)建一個(gè)文件controllers/HomeController.js。最后添加如下代碼:
- (function(){
- angular.module('starter')
- .controller('HomeController', ['$scope', '$ionicModal', '$cordovaFile', '$cordovaFileTransfer', '$cordovaCamera', HomeController]);
- function HomeController($scope, $ionicModal, $cordovaFile, $cordovaFileTransfer, $cordovaCamera){
- var me = this;
- me.current_image = 'img/koro-sensei.png';
- me.image_description = '';
- me.detection_type = 'LABEL_DETECTION';
- me.detection_types = {
- LABEL_DETECTION: 'label',
- TEXT_DETECTION: 'text',
- LOGO_DETECTION: 'logo',
- LANDMARK_DETECTION: 'landmark'
- };
- var api_key = 'your-google-api-key';
- $scope.takePicture = function(){
- var options = {
- destinationType: Camera.DestinationType.DATA_URL,
- sourceType: Camera.PictureSourceType.CAMERA,
- targetWidth: 500,
- targetHeight: 500,
- correctOrientation: true,
- cameraDirection: 0,
- encodingType: Camera.EncodingType.JPEG
- };
- $cordovaCamera.getPicture(options).then(function(imagedata){
- me.current_image = "data:image/jpeg;base64," + imagedata;
- me.image_description = '';
- me.locale = '';
- var vision_api_json = {
- "requests":[
- {
- "image":{
- "content": imagedata
- },
- "features":[
- {
- "type": me.detection_type,
- "maxResults": 1
- }
- ]
- }
- ]
- };
- var file_contents = JSON.stringify(vision_api_json);
- $cordovaFile.writeFile(
- cordova.file.applicationStorageDirectory,
- 'file.json',
- file_contents,
- true
- ).then(function(result){
- var headers = {
- 'Content-Type': 'application/json'
- };
- options.headers = headers;
- var server = 'https://vision.googleapis.com/v1/images:annotate?key=' + api_key;
- var filePath = cordova.file.applicationStorageDirectory + 'file.json';
- $cordovaFileTransfer.upload(server, filePath, options, true)
- .then(function(result){
- var res = JSON.parse(result.response);
- var key = me.detection_types[me.detection_type] + 'Annotations';
- me.image_description = res.responses[0][key][0].description;
- }, function(err){
- alert('An error occurred while uploading the file');
- });
- }, function(err){
- alert('An error occurred while trying to write the file');
- });
- }, function(err){
- alert('An error occurred getting the picture from the camera');
- });
- }
- }
- })();
現(xiàn)在,讓我們來(lái)分片斷介紹上面的代碼。首先,創(chuàng)建控制器并導(dǎo)入需要的庫(kù):
- (function(){
- angular.module('starter')
- .controller('HomeController', ['$scope', '$cordovaFile', '$cordovaFileTransfer', '$cordovaCamera', HomeController]);
- function HomeController($scope, $cordovaFile, $cordovaFileTransfer, $cordovaCamera){
- ...
- }
在控制器中設(shè)置視圖所需要的默認(rèn)數(shù)據(jù)。這包括:要顯示的圖像的占位符,一個(gè)空的描述位置,默認(rèn)的檢測(cè)類型,等等。通常我們都使用LABEL_DETECTION這一選項(xiàng),因?yàn)樗啾扔谄渌x項(xiàng)更具通用性。請(qǐng)參考如下代碼:
- var me = this;
- me.current_image = 'img/koro-sensei.png';
- me.image_description = '';
- me.detection_type = 'LABEL_DETECTION';
接下來(lái)定義一個(gè)對(duì)象,它包含所有要檢測(cè)的類型以及谷歌提供的API鍵:
- me.detection_types = {
- LABEL_DETECTION: 'label',
- TEXT_DETECTION: 'text',
- LOGO_DETECTION: 'logo',
- LANDMARK_DETECTION: 'landmark'
- };
- var api_key = 'your-google-api-key';
接下來(lái),創(chuàng)建一個(gè)當(dāng)按下相機(jī)按鈕時(shí)要執(zhí)行的方法,代碼如下:
- $scope.takePicture = function(){
- ...
- };
在該方法中,首先聲明相機(jī)插件有關(guān)的選項(xiàng),把destinationType設(shè)置為Camera.DestinationType.DATA_URL。這意味著:一旦選中圖片,回調(diào)函數(shù)中將會(huì)擁有圖像的URI數(shù)據(jù)。因?yàn)榇薝RI已經(jīng)是base64編碼的數(shù)據(jù)了,所以不再需要轉(zhuǎn)換。
sourceType被指定為Camera.PictureSourceType.CAMERA,這樣便可以使用照相機(jī)拍攝的圖像作為數(shù)據(jù)源。然后,targetWidth和targetHeight兩個(gè)值分別設(shè)置恰當(dāng)?shù)膱D像尺寸。correctOrientation值被設(shè)置為true,這樣它會(huì)自動(dòng)把圖像的方向修改為縱向方式。把cameraDirection指定為0,這樣便可以使用后置攝像頭了。最后,把encodingType指定為Camera.EncodingType.JPEG,從而允許你把數(shù)據(jù)URI預(yù)置為data:image/jpeg;base64,從而顯示圖像。
- var options = {
- destinationType: Camera.DestinationType.DATA_URL,
- sourceType: Camera.PictureSourceType.CAMERA,
- targetWidth: 500,
- targetHeight: 500,
- correctOrientation: true,
- cameraDirection: 0,
- encodingType: Camera.EncodingType.JPEG
- };
通過(guò)調(diào)用$cordovaCamera.getPicture將打開(kāi)設(shè)備上默認(rèn)的相機(jī)應(yīng)用程序。它使用options作為參數(shù),然后調(diào)用then,并提供成功和錯(cuò)誤操作對(duì)應(yīng)的回調(diào)函數(shù)。同樣的設(shè)計(jì)適用于后面你會(huì)使用的所有插件。
- $cordovaCamera.getPicture(options).then(function(imagedata){
- ...
- }, function(err){
- alert('An error occurred getting the picture from the camera');
- });
接下來(lái),在成功操作回調(diào)函數(shù)中更新圖像源(current_image)并把描述內(nèi)容重置為一個(gè)空串:
- me.current_image = "data:image/jpeg;base64," + imagedata;
- me.image_description = '';
然后,使用URI從相機(jī)插件得到的數(shù)據(jù)URI和用戶所選的檢測(cè)類型(me.detection_type) 數(shù)據(jù)一個(gè)構(gòu)建對(duì)象。然后,把該對(duì)象轉(zhuǎn)換為一個(gè)字符串;這樣一來(lái),你就可以在發(fā)送到API的JSON文件中使用它作為內(nèi)容數(shù)據(jù)。
- var vision_api_json = {
- "requests":[
- {
- "image":{
- "content": imagedata
- },
- "features":[
- {
- "type": me.detection_type,
- "maxResults": 1
- }
- ]
- }
- ]
- };
- var file_contents = JSON.stringify(vision_api_json);
接下來(lái),使用Cordova文件插件把file_contents寫(xiě)到存儲(chǔ)在應(yīng)用程序沙箱根目錄下的文件file.json中。另外,注意writeFile方法的第三個(gè)參數(shù)是一個(gè)布爾類型,用于指定如果文件不存在的話是否創(chuàng)建一個(gè)新文件:
- $cordovaFile.writeFile(
- cordova.file.applicationStorageDirectory,
- 'file.json',
- file_contents,
- true
- ).then(function(result){
- ...
- }, function(err){
- alert('An error occurred while writing to the file');
- });
當(dāng)把內(nèi)容寫(xiě)入到文件中時(shí),需要聲明文件傳輸插件所需要使用的變量。下面代碼片斷中的headers變量對(duì)應(yīng)于請(qǐng)求的http頭部。因?yàn)槟惆l(fā)送一個(gè)JSON文件,您必須將Content-Type設(shè)置為application/json。另外,server中使用了一個(gè)完整路徑形式的URL來(lái)向API發(fā)送請(qǐng)求; filePath中也使用了一個(gè)完整路徑形式來(lái)指定要發(fā)送的JSON文件。
- var headers = {
- 'Content-Type': 'application/json'
- };
- options.headers = headers;
- var server = 'https://vision.googleapis.com/v1/images:annotate?key=' + api_key;
- var filePath = cordova.file.applicationStorageDirectory + 'file.json';
接下來(lái),使用文件傳輸插件的upload方法將文件發(fā)送到服務(wù)器。這里,提供給upload方法的第四個(gè)參數(shù)是一個(gè)布爾值,用于設(shè)置是否接受來(lái)自所有主機(jī)的安全證書(shū)。一旦你得到一個(gè)服務(wù)器端的響應(yīng),便需要使用JSON.parse方法將其轉(zhuǎn)換為JavaScript對(duì)象。通過(guò)串聯(lián)當(dāng)前的檢測(cè)類型和Annotations一詞來(lái)構(gòu)建鍵(key)。這允許你形成字符串labelAnnotations,如果用戶選擇LABEL_DETECTION作為檢測(cè)類型的話。然后,你可以使用此字符串提取圖像的實(shí)際描述。
- $cordovaFileTransfer.upload(server, filePath, options, true)
- .then(function(result){
- var res = JSON.parse(result.response);
- var key = me.detection_types[me.detection_type] + 'Annotations';
- me.image_description = res.responses[0][key][0].description;
- }, function(err){
- alert('An error occured while uploading the file');
- });
(三) 添加視圖
現(xiàn)在,需要?jiǎng)?chuàng)建文件template/home.html,并添加如下代碼:
- <ion-view title="IonicVision" ng-controller="HomeController as home_ctrl">
- <header class="bar bar-header bar-stable">
- <h1 class="title">Ionic Vision</h1>
- </header>
- <ion-content class="has-header padding">
- <img src="{{ home_ctrl.current_image }}" class="picture">
- <h3 class="text-center" ng-show="home_ctrl.image_description">{{ home_ctrl.image_description }}</h3>
- <label class="item item-input item-select">
- <div class="input-label">
- Detection Type
- </div>
- <select ng-model="home_ctrl.detection_type">
- <option value="{{detection_type}}" ng-repeat="(detection_type, detection_type_value) in home_ctrl.detection_types">{{detection_type_value}}</option>
- </select>
- </label>
- <button class="button button-positive button-block" ng-click="takePicture()">
- Take Picture
- </button>
- </ion-content>
- </ion-view>
接下來(lái),對(duì)上面的代碼進(jìn)行逐片分析。
首先,創(chuàng)建一個(gè)新的ion-view并指定要使用的控制器:
- <ion-view title="IonicVision" ng-controller="HomeController as home_ctrl">
- </ion-view>
在這個(gè)io-view里面是頁(yè)眉部分(header)和ion-content。其中,ion-content是你在頁(yè)眉部分下部要看到的UI元素部分。再往里面是圖像、圖像描述、檢測(cè)類型列表和拍照按鈕等信息。
- <header class="bar bar-header bar-stable">
- <h1 class="title">Ionic Vision</h1>
- </header>
- <ion-content class="has-header padding">
- <img src="{{ home_ctrl.current_image }}" class="picture">
- <h3 class="text-center" ng-show="home_ctrl.image_description">{{ home_ctrl.image_description }}</h3>
- <label class="item item-input item-select">
- <div class="input-label">
- Detection Type
- </div>
- <select ng-model="home_ctrl.detection_type">
- <option value="{{detection_type}}" ng-repeat="(detection_type, detection_type_value) in home_ctrl.detection_types">{{detection_type_value}}</option>
- </select>
- </label>
- <button class="button button-positive button-block" ng-click="takePicture()">
- Take Picture
- </button>
- </ion-content>
五、 格式化
大多數(shù)的樣式化工作由Ionic自動(dòng)完成,因此你僅需要聲明一組樣式即可。為此,需要把下面內(nèi)容添加到css/style.css文件中:
- .text-center {
- text-align: center;
- }
- .picture {
- max-width: 100%;
- max-height: 100%;
- }
(一) 組合到一起
打開(kāi)js/app.js文件,其中包含用于初始化Ionic和ngCordova的代碼。如果您選擇使用Ionic提供的空白啟動(dòng)模板,那么你會(huì)注意到大部分的代碼已經(jīng)填寫(xiě)了。你僅需要指定ngCordova的用途并編輯config方法的內(nèi)容為指向文件home.html即可。
- angular.module('starter', ['ionic', 'ngCordova'])
- .run(function($ionicPlatform) {
- $ionicPlatform.ready(function() {
- if(window.cordova && window.cordova.plugins.Keyboard) {
- cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
- cordova.plugins.Keyboard.disableScroll(true);
- }
- if(window.StatusBar) {
- StatusBar.styleDefault();
- }
- });
- })
- .config(function($stateProvider, $urlRouterProvider) {
- $stateProvider
- .state('home', {
- url: '/home',
- templateUrl: 'templates/home.html'
- });
- $urlRouterProvider.otherwise('/home');
- });
接下來(lái),打開(kāi)文件index.html并鏈接到ng-cordova.js文件(接在文件ionic.bundle.js后面即可)。然后,在app.js文件的后面還要鏈接到文件HomeController.js。
別忘記把starter指定為ng-app的值,并在body標(biāo)記的內(nèi)部添加ion-nav-view,以便顯示home.html視圖。
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="utf-8">
- <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
- <title></title>
- <link href="lib/ionic/css/ionic.css" rel="stylesheet">
- <link href="css/style.css" rel="stylesheet">
- <!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
- <link href="css/ionic.app.css" rel="stylesheet">
- -->
- <!-- ionic/angularjs js -->
- <script src="lib/ionic/js/ionic.bundle.js"></script>
- <script src="lib/ngCordova/dist/ng-cordova.js"></script>
- <!-- cordova script (this will be a 404 during development) -->
- <script src="cordova.js"></script>
- <!-- your app's js -->
- <script src="js/app.js"></script>
- <script src="js/controllers/HomeController.js"></script>
- </head>
- <body ng-app="starter">
- <ion-nav-view></ion-nav-view>
- </body>
- </html>
六、 運(yùn)行應(yīng)用程序
現(xiàn)在,你可以在你的設(shè)備或者模擬器上運(yùn)行上面的應(yīng)用程序了,這只需要執(zhí)行如下命令即可:
- ionic run android
七、 小結(jié)
在本教程中,你使用ionic并借助云Vision API構(gòu)建了一個(gè)圖像識(shí)別軟件。其中,我討論了使用不同的圖像檢測(cè)類型,例如標(biāo)簽、標(biāo)志、徽標(biāo)和文本檢測(cè)等。但是,文章中我并沒(méi)有涵蓋人臉檢測(cè)或安全搜索檢測(cè)知識(shí)。不過(guò),對(duì)于人臉檢測(cè)技術(shù),您可以使用像Fabric.js這種框架。此工具會(huì)把圖像轉(zhuǎn)換成一個(gè)畫(huà)布(Canvas)對(duì)象并在檢測(cè)到的臉上畫(huà)圓。
有關(guān)云Vision API更多的知識(shí),請(qǐng)自行閱讀官方文檔(https://cloud.google.com/vision/docs/)。當(dāng)然,本人也非常希望了解到你的使用體驗(yàn)與思考結(jié)果。
原文標(biāo)題:Image Recognition with the Google Vision API and Ionic
【51CTO譯稿,合作站點(diǎn)轉(zhuǎn)載請(qǐng)注明原文譯者和出處為51CTO.com】