From 006c627a033dab022c729e87b1d60bcc1dfc3523 Mon Sep 17 00:00:00 2001 From: Gus Class Date: Mon, 31 Jul 2017 16:33:39 -0700 Subject: [PATCH] Upgrades vision to partial-GAPIC (#434) --- vision/detect.js | 188 ++++++++++++-------------- vision/faceDetection.js | 40 ++++-- vision/package.json | 2 +- vision/quickstart.js | 22 +-- vision/system-test/detect.test.js | 4 +- vision/system-test/quickstart.test.js | 43 +----- vision/textDetection.js | 69 +++++----- vision/yarn.lock | 176 +++++++++++++++--------- 8 files changed, 282 insertions(+), 262 deletions(-) diff --git a/vision/detect.js b/vision/detect.js index 6df64401fa..1c38659428 100644 --- a/vision/detect.js +++ b/vision/detect.js @@ -26,18 +26,17 @@ function detectFaces (fileName) { // The path to the local image file, e.g. "/path/to/image.png" // const fileName = '/path/to/image.png'; - // Performs face detection on the local file - vision.detectFaces(fileName) + vision.faceDetection({ source: { filename: fileName } }) .then((results) => { - const faces = results[0]; + const faces = results[0].faceAnnotations; console.log('Faces:'); faces.forEach((face, i) => { console.log(` Face #${i + 1}:`); - console.log(` Joy: ${face.joy}`); - console.log(` Anger: ${face.anger}`); - console.log(` Sorrow: ${face.sorrow}`); - console.log(` Surprise: ${face.surprise}`); + console.log(` Joy: ${face.joyLikelihood}`); + console.log(` Anger: ${face.angerLikelihood}`); + console.log(` Sorrow: ${face.sorrowLikelihood}`); + console.log(` Surprise: ${face.surpriseLikelihood}`); }); }) .catch((err) => { @@ -49,11 +48,9 @@ function detectFaces (fileName) { function detectFacesGCS (bucketName, fileName) { // [START vision_face_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -62,18 +59,20 @@ function detectFacesGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'path/to/image.png'; - // Performs face detection on the remote file - vision.detectFaces(storage.bucket(bucketName).file(fileName)) + const gcsPath = `gs://${bucketName}/${fileName}`; + + // Performs face detection on the gcs file + vision.faceDetection({ source: { imageUri: gcsPath } }) .then((results) => { - const faces = results[0]; + const faces = results[0].faceAnnotations; console.log('Faces:'); faces.forEach((face, i) => { console.log(` Face #${i + 1}:`); - console.log(` Joy: ${face.joy}`); - console.log(` Anger: ${face.anger}`); - console.log(` Sorrow: ${face.sorrow}`); - console.log(` Surprise: ${face.surprise}`); + console.log(` Joy: ${face.joyLikelihood}`); + console.log(` Anger: ${face.angerLikelihood}`); + console.log(` Sorrow: ${face.sorrowLikelihood}`); + console.log(` Surprise: ${face.surpriseLikelihood}`); }); }) .catch((err) => { @@ -94,10 +93,9 @@ function detectLabels (fileName) { // const fileName = '/path/to/image.png'; // Performs label detection on the local file - vision.detectLabels(fileName) + vision.labelDetection({ source: { filename: fileName } }) .then((results) => { - const labels = results[0]; - + const labels = results[0].labelAnnotations; console.log('Labels:'); labels.forEach((label) => console.log(label)); }) @@ -110,11 +108,9 @@ function detectLabels (fileName) { function detectLabelsGCS (bucketName, fileName) { // [START vision_label_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -123,23 +119,23 @@ function detectLabelsGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'path/to/image.png'; - // Performs label detection on the remote file - vision.detectLabels(storage.bucket(bucketName).file(fileName)) - .then((results) => { - const labels = results[0]; + const gcsPath = `gs://${bucketName}/${fileName}`; + // Performs label detection on the gcs file + vision.labelDetection({ source: { imageUri: gcsPath } }) + .then((results) => { + const labels = results[0].labelAnnotations; console.log('Labels:'); labels.forEach((label) => console.log(label)); }) .catch((err) => { console.error('ERROR:', err); }); - // [END vision_label_detection_gcs] + // [END vision_label_detection] } function detectLandmarks (fileName) { // [START vision_landmark_detection] - // Imports the Google Cloud client library const Vision = require('@google-cloud/vision'); // Instantiates a client @@ -149,10 +145,9 @@ function detectLandmarks (fileName) { // const fileName = '/path/to/image.png'; // Performs landmark detection on the local file - vision.detectLandmarks(fileName) + vision.landmarkDetection({ source: {filename: fileName} }) .then((results) => { - const landmarks = results[0]; - + const landmarks = results[0].landmarkAnnotations; console.log('Landmarks:'); landmarks.forEach((landmark) => console.log(landmark)); }) @@ -165,11 +160,9 @@ function detectLandmarks (fileName) { function detectLandmarksGCS (bucketName, fileName) { // [START vision_landmark_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -178,11 +171,12 @@ function detectLandmarksGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'path/to/image.png'; - // Performs landmark detection on the remote file - vision.detectLandmarks(storage.bucket(bucketName).file(fileName)) - .then((results) => { - const landmarks = results[0]; + const gcsPath = `gs://${bucketName}/${fileName}`; + // Performs landmark detection on the gcs file + vision.landmarkDetection({ source: {imageUri: gcsPath} }) + .then((results) => { + const landmarks = results[0].landmarkAnnotations; console.log('Landmarks:'); landmarks.forEach((landmark) => console.log(landmark)); }) @@ -194,7 +188,6 @@ function detectLandmarksGCS (bucketName, fileName) { function detectText (fileName) { // [START vision_text_detection] - // Imports the Google Cloud client library const Vision = require('@google-cloud/vision'); // Instantiates a client @@ -204,10 +197,9 @@ function detectText (fileName) { // const fileName = '/path/to/image.png'; // Performs text detection on the local file - vision.detectText(fileName) + vision.textDetection({ source: { filename: fileName } }) .then((results) => { - const detections = results[0]; - + const detections = results[0].textAnnotations; console.log('Text:'); detections.forEach((text) => console.log(text)); }) @@ -220,11 +212,9 @@ function detectText (fileName) { function detectTextGCS (bucketName, fileName) { // [START vision_text_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -233,11 +223,12 @@ function detectTextGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'path/to/image.png'; - // Performs text detection on the remote file - vision.detectText(storage.bucket(bucketName).file(fileName)) - .then((results) => { - const detections = results[0]; + const gcsPath = `gs://${bucketName}/${fileName}`; + // Performs text detection on the gcs file + vision.textDetection({ source: { imageUri: gcsPath } }) + .then((results) => { + const detections = results[0].textAnnotations; console.log('Text:'); detections.forEach((text) => console.log(text)); }) @@ -249,7 +240,6 @@ function detectTextGCS (bucketName, fileName) { function detectLogos (fileName) { // [START vision_logo_detection] - // Imports the Google Cloud client library const Vision = require('@google-cloud/vision'); // Instantiates a client @@ -259,10 +249,9 @@ function detectLogos (fileName) { // const fileName = '/path/to/image.png'; // Performs logo detection on the local file - vision.detectLogos(fileName) + vision.logoDetection({ source: { filename: fileName } }) .then((results) => { - const logos = results[0]; - + const logos = results[0].logoAnnotations; console.log('Logos:'); logos.forEach((logo) => console.log(logo)); }) @@ -275,11 +264,9 @@ function detectLogos (fileName) { function detectLogosGCS (bucketName, fileName) { // [START vision_logo_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -288,11 +275,12 @@ function detectLogosGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'path/to/image.png'; - // Performs logo detection on the remote file - vision.detectLogos(storage.bucket(bucketName).file(fileName)) - .then((results) => { - const logos = results[0]; + const gcsPath = `gs://${bucketName}/${fileName}`; + // Performs logo detection on the gcs file + vision.logoDetection({ source: { imageUri: gcsPath } }) + .then((results) => { + const logos = results[0].logoAnnotations; console.log('Logos:'); logos.forEach((logo) => console.log(logo)); }) @@ -304,7 +292,6 @@ function detectLogosGCS (bucketName, fileName) { function detectProperties (fileName) { // [START vision_image_property_detection] - // Imports the Google Cloud client library const Vision = require('@google-cloud/vision'); // Instantiates a client @@ -313,13 +300,12 @@ function detectProperties (fileName) { // The path to the local image file, e.g. "/path/to/image.png" // const fileName = '/path/to/image.png'; - // Performs image property detection on the local file - vision.detectProperties(fileName) + // Performs property detection on the local file + vision.imageProperties({ source: { filename: fileName } }) .then((results) => { - const properties = results[0]; - - console.log('Colors:'); - properties.colors.forEach((color) => console.log(color)); + const properties = results[0].imagePropertiesAnnotation; + const colors = properties.dominantColors.colors; + colors.forEach((color) => console.log(color)); }) .catch((err) => { console.error('ERROR:', err); @@ -330,11 +316,9 @@ function detectProperties (fileName) { function detectPropertiesGCS (bucketName, fileName) { // [START vision_image_property_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -343,13 +327,14 @@ function detectPropertiesGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'path/to/image.png'; - // Performs image property detection on the remote file - vision.detectProperties(storage.bucket(bucketName).file(fileName)) - .then((results) => { - const properties = results[0]; + const gcsPath = `gs://${bucketName}/${fileName}`; - console.log('Colors:'); - properties.colors.forEach((color) => console.log(color)); + // Performs property detection on the gcs file + vision.imageProperties({ source: { imageUri: gcsPath } }) + .then((results) => { + const properties = results[0].imagePropertiesAnnotation; + const colors = properties.dominantColors.colors; + colors.forEach((color) => console.log(color)); }) .catch((err) => { console.error('ERROR:', err); @@ -359,7 +344,6 @@ function detectPropertiesGCS (bucketName, fileName) { function detectSafeSearch (fileName) { // [START vision_safe_search_detection] - // Imports the Google Cloud client library const Vision = require('@google-cloud/vision'); // Instantiates a client @@ -368,10 +352,10 @@ function detectSafeSearch (fileName) { // The path to the local image file, e.g. "/path/to/image.png" // const fileName = '/path/to/image.png'; - // Performs safe search property detection on the local file - vision.detectSafeSearch(fileName) + // Performs safe search detection on the local file + vision.safeSearchDetection({ source: { filename: fileName } }) .then((results) => { - const detections = results[0]; + const detections = results[0].safeSearchAnnotation; console.log(`Adult: ${detections.adult}`); console.log(`Spoof: ${detections.spoof}`); @@ -387,11 +371,9 @@ function detectSafeSearch (fileName) { function detectSafeSearchGCS (bucketName, fileName) { // [START vision_safe_search_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -400,10 +382,12 @@ function detectSafeSearchGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'path/to/image.png'; + const gcsPath = `gs://${bucketName}/${fileName}`; + // Performs safe search property detection on the remote file - vision.detectSafeSearch(storage.bucket(bucketName).file(fileName)) + vision.safeSearchDetection({ source: { imageUri: gcsPath } }) .then((results) => { - const detections = results[0]; + const detections = results[0].safeSearchAnnotation; console.log(`Adult: ${detections.adult}`); console.log(`Spoof: ${detections.spoof}`); @@ -429,13 +413,15 @@ function detectCropHints (fileName) { // const fileName = 'my-file.jpg'; // Find crop hints for the local file - vision.detectCrops(fileName) + vision.cropHints({ + source: {filename: fileName}, + options: {aspect_ratios: 0.25} }) .then((results) => { - const cropHints = results[0]; + const cropHints = results[0].cropHintsAnnotation; - cropHints.forEach((hintBounds, hintIdx) => { + cropHints.cropHints.forEach((hintBounds, hintIdx) => { console.log(`Crop Hint ${hintIdx}:`); - hintBounds.forEach((bound, boundIdx) => { + hintBounds.boundingPoly.vertices.forEach((bound, boundIdx) => { console.log(` Bound ${boundIdx}: (${bound.x}, ${bound.y})`); }); }); @@ -450,11 +436,9 @@ function detectCropHintsGCS (bucketName, fileName) { // [START vision_crop_hint_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -463,14 +447,16 @@ function detectCropHintsGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'my-file.jpg'; + const gcsPath = `gs://${bucketName}/${fileName}`; + // Find crop hints for the remote file - vision.detectCrops(storage.bucket(bucketName).file(fileName)) + vision.cropHints({source: {imageUri: gcsPath}}) .then((results) => { - const cropHints = results[0]; + const cropHints = results[0].cropHintsAnnotation; - cropHints.forEach((hintBounds, hintIdx) => { + cropHints.cropHints.forEach((hintBounds, hintIdx) => { console.log(`Crop Hint ${hintIdx}:`); - hintBounds.forEach((bound, boundIdx) => { + hintBounds.boundingPoly.vertices.forEach((bound, boundIdx) => { console.log(` Bound ${boundIdx}: (${bound.x}, ${bound.y})`); }); }); @@ -494,9 +480,9 @@ function detectWeb (fileName) { // const fileName = 'my-file.jpg'; // Detect similar images on the web to a local file - vision.detectSimilar(fileName) + vision.webDetection({ source: { filename: fileName } }) .then((results) => { - const webDetection = results[1].responses[0].webDetection; + const webDetection = results[0].webDetection; if (webDetection.fullMatchingImages.length) { console.log(`Full matches found: ${webDetection.fullMatchingImages.length}`); @@ -532,11 +518,9 @@ function detectWebGCS (bucketName, fileName) { // [START vision_web_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -545,10 +529,12 @@ function detectWebGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'my-file.jpg'; + const gcsPath = `gs://${bucketName}/${fileName}`; + // Detect similar images on the web to a remote file - vision.detectSimilar(storage.bucket(bucketName).file(fileName)) + vision.webDetection({ source: { imageUri: gcsPath } }) .then((results) => { - const webDetection = results[1].responses[0].webDetection; + const webDetection = results[0].webDetection; if (webDetection.fullMatchingImages.length) { console.log(`Full matches found: ${webDetection.fullMatchingImages.length}`); @@ -593,9 +579,9 @@ function detectFulltext (fileName) { // const fileName = 'my-file.jpg'; // Read a local image as a text document - vision.readDocument(fileName) + vision.documentTextDetection({ source: { filename: fileName } }) .then((results) => { - const fullTextAnnotation = results[1].responses[0].fullTextAnnotation; + const fullTextAnnotation = results[0].fullTextAnnotation; console.log(fullTextAnnotation.text); }) .catch((err) => { @@ -608,11 +594,9 @@ function detectFulltextGCS (bucketName, fileName) { // [START vision_fulltext_detection_gcs] // Imports the Google Cloud client libraries - const Storage = require('@google-cloud/storage'); const Vision = require('@google-cloud/vision'); // Instantiates clients - const storage = Storage(); const vision = Vision(); // The name of the bucket where the file resides, e.g. "my-bucket" @@ -621,10 +605,12 @@ function detectFulltextGCS (bucketName, fileName) { // The path to the file within the bucket, e.g. "path/to/image.png" // const fileName = 'my-file.jpg'; + const gcsPath = `gs://${bucketName}/${fileName}`; + // Read a remote image as a text document - vision.readDocument(storage.bucket(bucketName).file(fileName)) + vision.documentTextDetection({ source: { imageUri: gcsPath } }) .then((results) => { - const fullTextAnnotation = results[1].responses[0].fullTextAnnotation; + const fullTextAnnotation = results[0].fullTextAnnotation; console.log(fullTextAnnotation.text); }) .catch((err) => { diff --git a/vision/faceDetection.js b/vision/faceDetection.js index 63d67953c1..6163ccfb7a 100644 --- a/vision/faceDetection.js +++ b/vision/faceDetection.js @@ -33,21 +33,25 @@ var fs = require('fs'); */ function detectFaces (inputFile, callback) { // Make a call to the Vision API to detect the faces - vision.detectFaces(inputFile, function (err, faces) { - if (err) { - return callback(err); - } - var numFaces = faces.length; - console.log('Found ' + numFaces + (numFaces === 1 ? ' face' : ' faces')); - callback(null, faces); - }); + const request = { source: { filename: inputFile } }; + vision.faceDetection(request) + .then((results) => { + const faces = results[0].faceAnnotations; + var numFaces = faces.length; + console.log('Found ' + numFaces + (numFaces === 1 ? ' face' : ' faces')); + callback(null, faces); + }) + .catch((err) => { + console.error('ERROR:', err); + callback(err); + }); } /** * Draws a polygon around the faces, then saves to outputFile. */ function highlightFaces (inputFile, faces, outputFile, Canvas, callback) { - fs.readFile(inputFile, function (err, image) { + fs.readFile(inputFile, (err, image) => { if (err) { return callback(err); } @@ -64,12 +68,18 @@ function highlightFaces (inputFile, faces, outputFile, Canvas, callback) { context.strokeStyle = 'rgba(0,255,0,0.8)'; context.lineWidth = '5'; - faces.forEach(function (face) { + faces.forEach((face) => { context.beginPath(); - face.bounds.face.forEach(function (bounds) { + let origX = 0; + let origY = 0; + face.boundingPoly.vertices.forEach((bounds, i) => { + if (i === 0) { + origX = bounds.x; + origY = bounds.y; + } context.lineTo(bounds.x, bounds.y); }); - context.lineTo(face.bounds.face[0].x, face.bounds.face[0].y); + context.lineTo(origX, origY); context.stroke(); }); @@ -78,7 +88,7 @@ function highlightFaces (inputFile, faces, outputFile, Canvas, callback) { var writeStream = fs.createWriteStream(outputFile); var pngStream = canvas.pngStream(); - pngStream.on('data', function (chunk) { + pngStream.on('data', (chunk) => { writeStream.write(chunk); }); pngStream.on('error', console.log); @@ -89,13 +99,13 @@ function highlightFaces (inputFile, faces, outputFile, Canvas, callback) { // Run the example function main (inputFile, outputFile, Canvas, callback) { outputFile = outputFile || 'out.png'; - detectFaces(inputFile, function (err, faces) { + detectFaces(inputFile, (err, faces) => { if (err) { return callback(err); } console.log('Highlighting...'); - highlightFaces(inputFile, faces, outputFile, Canvas, function (err) { + highlightFaces(inputFile, faces, outputFile, Canvas, (err) => { if (err) { return callback(err); } diff --git a/vision/package.json b/vision/package.json index 10f58666a7..cc676cc244 100644 --- a/vision/package.json +++ b/vision/package.json @@ -19,7 +19,7 @@ }, "dependencies": { "@google-cloud/storage": "1.1.0", - "@google-cloud/vision": "0.11.2", + "@google-cloud/vision": "^0.12.0", "async": "2.3.0", "natural": "0.5.1", "redis": "2.7.1", diff --git a/vision/quickstart.js b/vision/quickstart.js index 16afa9a407..56914608e5 100644 --- a/vision/quickstart.js +++ b/vision/quickstart.js @@ -1,5 +1,5 @@ /** - * Copyright 2016, Google, Inc. + * Copyright 2017, Google, Inc. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -19,24 +19,26 @@ // Imports the Google Cloud client library const Vision = require('@google-cloud/vision'); -// Your Google Cloud Platform project ID -const projectId = 'YOUR_PROJECT_ID'; - // Instantiates a client -const visionClient = Vision({ - projectId: projectId -}); +const vision = Vision(); // The name of the image file to annotate const fileName = './resources/wakeupcat.jpg'; +// Prepare the request object +const request = { + source: { + filename: fileName + } +}; + // Performs label detection on the image file -visionClient.detectLabels(fileName) +vision.labelDetection(request) .then((results) => { - const labels = results[0]; + const labels = results[0].labelAnnotations; console.log('Labels:'); - labels.forEach((label) => console.log(label)); + labels.forEach((label) => console.log(label.description)); }) .catch((err) => { console.error('ERROR:', err); diff --git a/vision/system-test/detect.test.js b/vision/system-test/detect.test.js index f1c563e247..ac6693b97b 100644 --- a/vision/system-test/detect.test.js +++ b/vision/system-test/detect.test.js @@ -113,13 +113,13 @@ test(`should detect logos in a remote file`, async (t) => { test(`should detect properties in a local file`, async (t) => { const output = await tools.runAsync(`${cmd} properties ${files[1].localPath}`, cwd); - t.true(output.includes(`Colors:`)); + t.true(output.includes(`{ color: { red: 69, green: 42, blue: 27`)); t.true(output.split(`\n`).length > 4, `Multiple colors were detected.`); }); test(`should detect properties in a remote file`, async (t) => { const output = await tools.runAsync(`${cmd} properties-gcs ${bucketName} ${files[1].name}`, cwd); - t.true(output.includes(`Colors:`)); + t.true(output.includes(`{ color: { red: 69, green: 42, blue: 27`)); t.true(output.split(`\n`).length > 4, `Multiple colors were detected.`); }); diff --git a/vision/system-test/quickstart.test.js b/vision/system-test/quickstart.test.js index 6a404c840d..e3304caec6 100644 --- a/vision/system-test/quickstart.test.js +++ b/vision/system-test/quickstart.test.js @@ -1,5 +1,5 @@ /** - * Copyright 2016, Google, Inc. + * Copyright 2017, Google, Inc. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -16,46 +16,17 @@ 'use strict'; const path = require(`path`); -const proxyquire = require(`proxyquire`).noPreserveCache(); -const sinon = require(`sinon`); const test = require(`ava`); const tools = require(`@google-cloud/nodejs-repo-tools`); -const vision = proxyquire(`@google-cloud/vision`, {})(); +const cmd = `node quickstart.js`; +const cwd = path.join(__dirname, `..`); test.before(tools.stubConsole); test.after.always(tools.restoreConsole); -test.cb(`should detect labels`, (t) => { - const filePath = path.join(__dirname, `../resources/wakeupcat.jpg`); - const expectedFileName = `./resources/wakeupcat.jpg`; - const visionMock = { - detectLabels: (_fileName) => { - t.is(_fileName, expectedFileName); - - return vision.detectLabels(filePath) - .then(([labels]) => { - t.true(Array.isArray(labels)); - - setTimeout(() => { - try { - t.is(console.log.callCount, 6); - t.deepEqual(console.log.getCall(0).args, [`Labels:`]); - labels.forEach((label, i) => { - t.deepEqual(console.log.getCall(i + 1).args, [label]); - }); - t.end(); - } catch (err) { - t.end(err); - } - }, 200); - - return [labels]; - }); - } - }; - - proxyquire(`../quickstart`, { - '@google-cloud/vision': sinon.stub().returns(visionMock) - }); +test(`should detect labels in a remote file`, async (t) => { + const output = await tools.runAsync(`${cmd}`, cwd); + t.true(output.includes(`Labels:`)); + t.true(output.includes(`cat`)); }); diff --git a/vision/textDetection.js b/vision/textDetection.js index b5bb64bef5..65201266e8 100644 --- a/vision/textDetection.js +++ b/vision/textDetection.js @@ -130,14 +130,14 @@ function lookup (words, callback) { function extractDescription (texts) { var document = ''; texts.forEach(function (text) { - document += (text.desc || ''); + document += (text.description || ''); }); return document; } -function extractDescriptions (filename, index, texts, callback) { - if (texts.length) { - index.add(filename, extractDescription(texts), callback); +function extractDescriptions (filename, index, response, callback) { + if (response.textAnnotations.length) { + index.add(filename, extractDescription(response.textAnnotations), callback); } else { console.log(filename + ' had no discernable text.'); index.setContainsNoText(filename, callback); @@ -147,36 +147,41 @@ function extractDescriptions (filename, index, texts, callback) { // [START get_text] function getTextFromFiles (index, inputFiles, callback) { - var options = { verbose: true }; - // Make a call to the Vision API to detect text - vision.detectText(inputFiles, options, function (err, detections) { - if (err) { - return callback(err); - } - var textResponse = {}; - var tasks = []; - inputFiles.forEach(function (filename, i) { - var response = detections[i]; - if (response.error) { - console.log('API Error for ' + filename, response.error); - return; - } else if (Array.isArray(response)) { - textResponse[filename] = 1; - } else { - textResponse[filename] = 0; - } - tasks.push(function (cb) { - extractDescriptions(filename, index, response, cb); - }); - }); - async.parallel(tasks, function (err) { - if (err) { - return callback(err); - } - callback(null, textResponse); - }); + let requests = []; + inputFiles.forEach((filename) => { + let request = { + image: {content: fs.readFileSync(filename).toString('base64')}, + features: {type: 'TEXT_DETECTION'} + }; + requests.push(request); }); + vision.batchAnnotateImages({requests: requests}) + .then((results) => { + let detections = results[0].responses; + var textResponse = {}; + var tasks = []; + inputFiles.forEach(function (filename, i) { + var response = detections[i]; + if (response.error) { + console.log('API Error for ' + filename, response.error); + return; + } else if (Array.isArray(response)) { + textResponse[filename] = 1; + } else { + textResponse[filename] = 0; + } + tasks.push(function (cb) { + extractDescriptions(filename, index, response, cb); + }); + }); + async.parallel(tasks, function (err) { + if (err) { + return callback(err); + } + callback(null, textResponse); + }); + }); } // Run the example diff --git a/vision/yarn.lock b/vision/yarn.lock index b4df5411d6..f108246e8b 100644 --- a/vision/yarn.lock +++ b/vision/yarn.lock @@ -37,21 +37,6 @@ ansi-styles "^2.2.1" esutils "^2.0.2" -"@google-cloud/common-grpc@^0.3.0": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@google-cloud/common-grpc/-/common-grpc-0.3.1.tgz#c29b8ce83bef2da409eb838fa187960bb8c3df2e" - dependencies: - "@google-cloud/common" "^0.13.0" - dot-prop "^2.4.0" - duplexify "^3.5.0" - extend "^3.0.0" - google-proto-files "^0.11.0" - grpc "^1.2.3" - is "^3.2.0" - modelo "^4.2.0" - retry-request "^1.3.2" - through2 "^2.0.3" - "@google-cloud/common@^0.13.0": version "0.13.0" resolved "https://registry.yarnpkg.com/@google-cloud/common/-/common-0.13.0.tgz#d062439a75b38eb76c4704d20f5b45301a69f17f" @@ -75,21 +60,21 @@ string-format-obj "^1.1.0" through2 "^2.0.3" -"@google-cloud/nodejs-repo-tools@1.4.5": - version "1.4.5" - resolved "https://registry.yarnpkg.com/@google-cloud/nodejs-repo-tools/-/nodejs-repo-tools-1.4.5.tgz#fa2f8fcf29969e05de184fa4b8f03363903566e3" +"@google-cloud/nodejs-repo-tools@1.4.14": + version "1.4.14" + resolved "https://registry.yarnpkg.com/@google-cloud/nodejs-repo-tools/-/nodejs-repo-tools-1.4.14.tgz#b778687bcd798ea172a01d2b92db43ad95a8b0ce" dependencies: ava "0.19.1" colors "1.1.2" - fs-extra "3.0.0" + fs-extra "3.0.1" got "6.7.1" - handlebars "4.0.6" + handlebars "4.0.8" lodash "4.17.4" proxyquire "1.7.11" - sinon "2.1.0" + sinon "2.2.0" string "3.3.3" supertest "3.0.0" - yargs "7.1.0" + yargs "8.0.1" "@google-cloud/storage@1.1.0": version "1.1.0" @@ -112,22 +97,16 @@ string-format-obj "^1.0.0" through2 "^2.0.0" -"@google-cloud/vision@0.11.2": - version "0.11.2" - resolved "https://registry.yarnpkg.com/@google-cloud/vision/-/vision-0.11.2.tgz#95eae7b1b036d66c10014659db36461a6e9c76c7" +"@google-cloud/vision@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@google-cloud/vision/-/vision-0.12.0.tgz#ffab0a18e0e6ea841e6c4e81273cd46809fdf03e" dependencies: "@google-cloud/common" "^0.13.0" - "@google-cloud/common-grpc" "^0.3.0" - arrify "^1.0.0" async "^2.0.1" extend "^3.0.0" - google-gax "^0.13.0" - google-proto-files "^0.11.0" + google-gax "^0.13.2" + google-proto-files "^0.12.0" is "^3.0.1" - prop-assign "^1.0.0" - propprop "^0.3.0" - rgb-hex "^1.0.0" - string-format-obj "^1.0.0" abbrev@1: version "1.0.9" @@ -830,7 +809,7 @@ camelcase@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" -camelcase@^4.0.0: +camelcase@^4.0.0, camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -1092,6 +1071,14 @@ cross-spawn@^4.0.0: lru-cache "^4.0.1" which "^1.2.9" +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cryptiles@2.x.x: version "2.0.5" resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" @@ -1188,12 +1175,6 @@ diff@^3.0.0, diff@^3.0.1, diff@^3.1.0: version "3.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" -dot-prop@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-2.4.0.tgz#848e28f7f1d50740c6747ab3cb07670462b6f89c" - dependencies: - is-obj "^1.0.0" - dot-prop@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-4.1.1.tgz#a8493f0b7b5eeec82525b5c7587fa7de7ca859c1" @@ -1321,6 +1302,18 @@ execa@^0.5.0: signal-exit "^3.0.0" strip-eof "^1.0.0" +execa@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + expand-brackets@^0.1.4: version "0.1.5" resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" @@ -1439,9 +1432,9 @@ formidable@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" -fs-extra@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.0.tgz#244e0c4b0b8818f54040ec049d8a2bddc1202861" +fs-extra@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-3.0.1.tgz#3794f378c58b342ea7dbbb23095109c4b3b62291" dependencies: graceful-fs "^4.1.2" jsonfile "^3.0.0" @@ -1641,9 +1634,9 @@ google-auto-auth@^0.6.0: object-assign "^3.0.0" request "^2.79.0" -google-gax@^0.13.0: - version "0.13.2" - resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-0.13.2.tgz#61a7b55fad465b17d3680f88070b2e419131e98e" +google-gax@^0.13.2: + version "0.13.4" + resolved "https://registry.yarnpkg.com/google-gax/-/google-gax-0.13.4.tgz#462d0cf654b0abeef5ee5f059d0f7b6c0d0a6121" dependencies: extend "^3.0.0" google-auto-auth "^0.5.2" @@ -1661,9 +1654,9 @@ google-p12-pem@^0.1.0: dependencies: node-forge "^0.6.46" -google-proto-files@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/google-proto-files/-/google-proto-files-0.11.0.tgz#3d753120718e5e8574f38def739d54cf7a057553" +google-proto-files@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/google-proto-files/-/google-proto-files-0.12.1.tgz#6434dc7e025a0d0c82e5f04e615c737d6a4c4387" google-proto-files@^0.9.1: version "0.9.1" @@ -1693,7 +1686,7 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.6: version "1.0.1" resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" -grpc@^1.2, grpc@^1.2.3: +grpc@^1.2: version "1.2.3" resolved "https://registry.yarnpkg.com/grpc/-/grpc-1.2.3.tgz#60b4b533a8783fa0b389b6cfa5d5bee34786049f" dependencies: @@ -1712,9 +1705,9 @@ gtoken@^1.1.0, gtoken@^1.2.1: mime "^1.2.11" request "^2.72.0" -handlebars@4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" +handlebars@4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.8.tgz#22b875cd3f0e6cbea30314f144e82bc7a72ff420" dependencies: async "^1.4.0" optimist "^0.6.1" @@ -2377,6 +2370,12 @@ md5-o-matic@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/md5-o-matic/-/md5-o-matic-0.1.1.tgz#822bccd65e117c514fab176b25945d54100a03c3" +mem@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" + dependencies: + mimic-fn "^1.0.0" + meow@^3.7.0: version "3.7.0" resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" @@ -2648,6 +2647,14 @@ os-locale@^1.4.0: dependencies: lcid "^1.0.0" +os-locale@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" + dependencies: + execa "^0.7.0" + lcid "^1.0.0" + mem "^1.1.0" + os-tmpdir@^1.0.0, os-tmpdir@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -2859,14 +2866,6 @@ process-nextick-args@^1.0.7, process-nextick-args@~1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" -prop-assign@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prop-assign/-/prop-assign-1.0.0.tgz#9767a1fbfd7093908647a6e846d31b4feaa70459" - -propprop@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/propprop/-/propprop-0.3.1.tgz#a049a3568b896440067d15d8ec9f33735e570178" - protobufjs@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.1.tgz#589ecdda1a555fd69df4699adc142d36f133aa0b" @@ -3228,10 +3227,6 @@ retry-request@^1.3.2: request "2.76.0" through2 "^2.0.0" -rgb-hex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgb-hex/-/rgb-hex-1.0.0.tgz#bfaf8cd9cd9164b5a26d71eb4f15a0965324b3c1" - right-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" @@ -3270,6 +3265,16 @@ set-immediate-shim@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" +shebang-command@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" + dependencies: + shebang-regex "^1.0.0" + +shebang-regex@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" + signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" @@ -3287,6 +3292,19 @@ sinon@2.1.0: text-encoding "0.6.4" type-detect "^4.0.0" +sinon@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/sinon/-/sinon-2.2.0.tgz#3b1b42ff5defcbf51a52a62aca6d61171b9fd262" + dependencies: + diff "^3.1.0" + formatio "1.2.0" + lolex "^1.6.0" + native-promise-only "^0.8.1" + path-to-regexp "^1.7.0" + samsam "^1.1.3" + text-encoding "0.6.4" + type-detect "^4.0.0" + slash@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" @@ -3718,6 +3736,10 @@ which-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" +which-module@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" + which@^1.2.8, which@^1.2.9: version "1.2.14" resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" @@ -3808,6 +3830,12 @@ yargs-parser@^5.0.0: dependencies: camelcase "^3.0.0" +yargs-parser@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" + dependencies: + camelcase "^4.1.0" + yargs@7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" @@ -3826,6 +3854,24 @@ yargs@7.1.0: y18n "^3.2.1" yargs-parser "^5.0.0" +yargs@8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.1.tgz#420ef75e840c1457a80adcca9bc6fa3849de51aa" + dependencies: + camelcase "^4.1.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^2.0.0" + read-pkg-up "^2.0.0" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^2.0.0" + which-module "^2.0.0" + y18n "^3.2.1" + yargs-parser "^7.0.0" + yargs@^3.10.0, yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"