From fb97b916e4c2b6294caad6045ce6344908dacb63 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Thu, 16 Feb 2017 15:08:08 -0800 Subject: [PATCH 1/4] First + second drafts of the Video API Change-Id: I9bac19d0bf64065c93c6d84ec1edd0cece78af41 --- video/analyze.js | 157 ++++++++++++++++++++++++++++++ video/package.json | 17 ++++ video/system-test/analyze.test.js | 53 ++++++++++ 3 files changed, 227 insertions(+) create mode 100644 video/analyze.js create mode 100644 video/package.json create mode 100644 video/system-test/analyze.test.js diff --git a/video/analyze.js b/video/analyze.js new file mode 100644 index 0000000000..1720b7bba3 --- /dev/null +++ b/video/analyze.js @@ -0,0 +1,157 @@ +/** + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an `AS IS` BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// https://cloud.google.com/video-intelligence/docs/ + +'use strict'; + +function analyzeFaces (gcsPath) { + // [START analyze_faces] + // Imports the Google Cloud Video Intelligence library + const Video = require('@google-cloud/videointelligence').v1beta1(); + + // Instantiates a client + const video = Video.videoIntelligenceServiceClient(); + + // The GCS filepath of the video to analyze + // const gcsPath = 'gs://my-bucket/my-video.mp4' + + const request = { + inputUri: gcsPath, + features: ['FACE_DETECTION'] + }; + + // Detect faces in a video + video.annotateVideo(request) + .then((startResponse) => { + const operation = startResponse[0]; + console.log('Waiting for operation to complete...'); + return operation.promise(); + }) + .then((doneResponse) => { + // Get faces for first video + const faces = doneResponse[0].annotationResults[0].faceAnnotations; + faces.forEach((face, faceIdx) => { + console.log('Thumbnail size:', face.thumbnail.buffer.length); + face.segments.forEach((segment, segmentIdx) => { + console.log(`Track ${segmentIdx} of face ${faceIdx}: frames ${segment.startTimeOffset} to ${segment.endTimeOffset}`); + }); + }); + }); + // [END analyze_faces] +} + +function analyzeLabels (gcsPath) { + // [START analyze_labels] + // Imports the Google Cloud Video Intelligence library + const Video = require('@google-cloud/videointelligence').v1beta1(); + + // Instantiates a client + const video = Video.videoIntelligenceServiceClient(); + + // The GCS filepath of the video to analyze + // const gcsPath = 'gs://my-bucket/my-video.mp4' + + const request = { + inputUri: gcsPath, + features: ['FACE_DETECTION'] + }; + + // Detect labels in a video + video.annotateVideo(request) + .then((startResponse) => { + const operation = startResponse[0]; + console.log('Waiting for operation to complete...'); + return operation.promise(); + }) + .then((doneResponse) => { + // Get labels for first video + const labels = doneResponse[0].annotationResults[0].labelAnnotations; + labels.forEach((label) => { + console.log('Label description:', label.description); + console.log('Locations:'); + label.locations.forEach((location) => { + console.log(`\tFrames ${location.segment.startTimeOffset} to ${location.segment.endTimeOffset}`); + }); + }); + }); + // [END analyze_labels] +} + +function analyzeShots (gcsPath) { + // [START analyze_shots] + // Imports the Google Cloud Video Intelligence library + const Video = require('@google-cloud/videointelligence').v1beta1(); + + // Instantiates a client + const video = Video.videoIntelligenceServiceClient(); + + // The GCS filepath of the video to analyze + // const gcsPath = 'gs://my-bucket/my-video.mp4' + + const request = { + inputUri: gcsPath, + features: ['FACE_DETECTION'] + }; + + // Detect camera shot changes + video.annotateVideo(request) + .then((startResponse) => { + const operation = startResponse[0]; + console.log('Waiting for operation to complete...'); + return operation.promise(); + }) + .then((doneResponse) => { + // Get shot changes for first video + const shotChanges = doneResponse[0].annotationResults[0].shotAnnotations; + shotChanges.forEach((shot, shotIdx) => { + console.log(`Scene ${shotIdx}:`); + console.log(`\tStart: ${shot.startTimeOffset}`); + console.log(`\tEnd: ${shot.endTimeOffset}`); + }); + }); + // [END analyze_shots] +} + +const cli = require(`yargs`) + .demand(1) + .command( + `faces `, + `Analyzes faces in a video using the Cloud Video Intelligence API.`, + {}, + (opts) => analyzeFaces(opts.gcsPath) + ) + .command( + `shots `, + `Analyzes shot angles in a video using the Cloud Video Intelligence API.`, + {}, + (opts) => analyzeShots(opts.gcsPath) + ) + .command( + `labels `, + `Labels objects in a video using the Cloud Video Intelligence API.`, + {}, + (opts) => analyzeLabels(opts.gcsPath) + ) + .example(`node $0 faces gs://my-bucket/my-video.mp4`) + .example(`node $0 shots gs://my-bucket/my-video.mp4`) + .example(`node $0 labels gs://my-bucket/my-video.mp4`) + .wrap(120) + .recommendCommands() + .epilogue(`For more information, see https://cloud.google.com/video-intelligence/docs`); + +if (module === require.main) { + cli.help().strict().argv; +} diff --git a/video/package.json b/video/package.json new file mode 100644 index 0000000000..387b8eb3c3 --- /dev/null +++ b/video/package.json @@ -0,0 +1,17 @@ +{ + "name": "nodejs-docs-samples-videointelligence", + "version": "0.0.1", + "private": true, + "license": "Apache Version 2.0", + "author": "Google Inc.", + "scripts": { + "test": "cd ..; npm run st -- --verbose video/system-test/*.test.js" + }, + "dependencies": { + "@google-cloud/videointelligence": "0.1.0", + "yargs": "6.6.0" + }, + "engines": { + "node": ">=4.3.2" + } +} diff --git a/video/system-test/analyze.test.js b/video/system-test/analyze.test.js new file mode 100644 index 0000000000..44c59ad497 --- /dev/null +++ b/video/system-test/analyze.test.js @@ -0,0 +1,53 @@ +/** + * 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 + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an `AS IS` BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// https://cloud.google.com/video-intelligence/docs/ + +'use strict'; + +require(`../../system-test/_setup`); + +const cmd = `node analyze.js`; + +// analyze_faces +test(`should analyze faces`, async (t) => { + const output = await runAsync(`${cmd} faces gs://nodejs-docs-samples/obama.mp4`); + + t.true(output.includes('Processing video for face annotations:')); + t.true(output.includes('Finished processing.')); + t.true(output.includes('Thumbnail size: 23295')); + t.true(output.includes('Track 0 of face 0: frames 19933270 to 22466722')); +}); + +// analyze_labels +test(`should analyze labels`, async (t) => { + const output = await runAsync(`${cmd} labels gs://nodejs-docs-samples/obama.mp4`); + + t.true(output.includes('Processing video for label annotations:')); + t.true(output.includes('Finished processing.')); + t.true(output.includes('Label description: News conference')); + t.true(output.includes('Frames 33281 to 22466722')); +}); + +// analyze_shots +test(`should analyze shots`, async (t) => { + const output = await runAsync(`${cmd} shots gs://nodejs-docs-samples/obama.mp4`); + + t.true(output.includes('Processing video for shot change annotations:')); + t.true(output.includes('Finished processing.')); + t.true(output.includes('Scene 0:')); + t.true(output.includes('Start: 33281')); + t.true(output.includes('End: 22466722')); +}); From cd71e1d25104ef9346700172f47e387c36cea40d Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Tue, 28 Feb 2017 16:59:59 -0800 Subject: [PATCH 2/4] Fix test failures --- video/analyze.js | 4 ++-- video/system-test/analyze.test.js | 10 ++-------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/video/analyze.js b/video/analyze.js index 1720b7bba3..37bd8f802f 100644 --- a/video/analyze.js +++ b/video/analyze.js @@ -66,7 +66,7 @@ function analyzeLabels (gcsPath) { const request = { inputUri: gcsPath, - features: ['FACE_DETECTION'] + features: ['LABEL_DETECTION'] }; // Detect labels in a video @@ -103,7 +103,7 @@ function analyzeShots (gcsPath) { const request = { inputUri: gcsPath, - features: ['FACE_DETECTION'] + features: ['SHOT_CHANGE_DETECTION'] }; // Detect camera shot changes diff --git a/video/system-test/analyze.test.js b/video/system-test/analyze.test.js index 44c59ad497..12d11d813e 100644 --- a/video/system-test/analyze.test.js +++ b/video/system-test/analyze.test.js @@ -25,18 +25,14 @@ const cmd = `node analyze.js`; test(`should analyze faces`, async (t) => { const output = await runAsync(`${cmd} faces gs://nodejs-docs-samples/obama.mp4`); - t.true(output.includes('Processing video for face annotations:')); - t.true(output.includes('Finished processing.')); - t.true(output.includes('Thumbnail size: 23295')); - t.true(output.includes('Track 0 of face 0: frames 19933270 to 22466722')); + t.true(output.includes('Thumbnail size: 19856')); + t.true(output.includes('Track 0 of face 0: frames 33281 to 1099937')); }); // analyze_labels test(`should analyze labels`, async (t) => { const output = await runAsync(`${cmd} labels gs://nodejs-docs-samples/obama.mp4`); - t.true(output.includes('Processing video for label annotations:')); - t.true(output.includes('Finished processing.')); t.true(output.includes('Label description: News conference')); t.true(output.includes('Frames 33281 to 22466722')); }); @@ -45,8 +41,6 @@ test(`should analyze labels`, async (t) => { test(`should analyze shots`, async (t) => { const output = await runAsync(`${cmd} shots gs://nodejs-docs-samples/obama.mp4`); - t.true(output.includes('Processing video for shot change annotations:')); - t.true(output.includes('Finished processing.')); t.true(output.includes('Scene 0:')); t.true(output.includes('Start: 33281')); t.true(output.includes('End: 22466722')); From 028f2f75fb90940677a0070d58e35967c788a75f Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Tue, 7 Mar 2017 17:58:54 -0800 Subject: [PATCH 3/4] Change video lib to Cloud Storage link --- video/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/video/package.json b/video/package.json index 387b8eb3c3..675f002291 100644 --- a/video/package.json +++ b/video/package.json @@ -8,7 +8,7 @@ "test": "cd ..; npm run st -- --verbose video/system-test/*.test.js" }, "dependencies": { - "@google-cloud/videointelligence": "0.1.0", + "@google-cloud/videointelligence": "https://storage.googleapis.com/videointelligence-alpha/videointelligence-nodejs.tar.gz", "yargs": "6.6.0" }, "engines": { From 895cdd2c5bc83045e088cfa84a3f4d0d640292b7 Mon Sep 17 00:00:00 2001 From: Ace Nassri Date: Wed, 8 Mar 2017 10:57:16 -0800 Subject: [PATCH 4/4] Use videos that pass licensing requirements --- video/system-test/analyze.test.js | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/video/system-test/analyze.test.js b/video/system-test/analyze.test.js index 12d11d813e..9e51554014 100644 --- a/video/system-test/analyze.test.js +++ b/video/system-test/analyze.test.js @@ -23,25 +23,18 @@ const cmd = `node analyze.js`; // analyze_faces test(`should analyze faces`, async (t) => { - const output = await runAsync(`${cmd} faces gs://nodejs-docs-samples/obama.mp4`); - - t.true(output.includes('Thumbnail size: 19856')); - t.true(output.includes('Track 0 of face 0: frames 33281 to 1099937')); + const output = console.log(`${cmd} faces gs://nodejs-docs-samples/video/google_gmail.mp4`); + t.regex(output, 'Thumbnail size: \d+'); }); // analyze_labels test(`should analyze labels`, async (t) => { - const output = await runAsync(`${cmd} labels gs://nodejs-docs-samples/obama.mp4`); - - t.true(output.includes('Label description: News conference')); - t.true(output.includes('Frames 33281 to 22466722')); + const output = await runAsync(`${cmd} labels gs://nodejs-docs-samples/video/cat.mp4`); + t.regex(output, /Label description: Whiskers/); }); // analyze_shots test(`should analyze shots`, async (t) => { - const output = await runAsync(`${cmd} shots gs://nodejs-docs-samples/obama.mp4`); - - t.true(output.includes('Scene 0:')); - t.true(output.includes('Start: 33281')); - t.true(output.includes('End: 22466722')); + const output = await runAsync(`${cmd} shots gs://nodejs-docs-samples/video/gbike_dinosaur.mp4`); + t.regex(output, /Scene 0:/); });