diff --git a/video/README.md b/video/README.md index dd34ee4d55..848c4086dc 100644 --- a/video/README.md +++ b/video/README.md @@ -2,9 +2,9 @@ # Google Cloud Video Intelligence API Node.js Samples -The [Cloud Video Intellience API][video_docs] allows developers to easily -integrate video analysis within applications, including video labeling, face -detection, and shot change detection. +The [Cloud Video Intelligence API][video_docs] allows developers to easily +integrate video analysis within applications, including video labeling, safe search +, and shot change detection. [video_docs]: https://cloud.google.com/video-intelligence/docs/ @@ -41,17 +41,19 @@ __Usage:__ `node analyze.js --help` ``` Commands: - faces Analyzes faces in a video using the Cloud Video Intelligence API. - shots Analyzes shot angles in a video using the Cloud Video Intelligence API. - labels Labels objects in a video using the Cloud Video Intelligence API. + shots Analyzes shot angles in a video stored in Google Cloud Storage using the Cloud Video + Intelligence API. + labels-gcs Labels objects in a video stored in Google Cloud Storage using the Cloud Video Intelligence API. + labels-file Labels objects in a video stored locally using the Cloud Video Intelligence API. Options: --help Show help [boolean] Examples: - node analyze.js faces gs://my-bucket/my-video.mp4 node analyze.js shots gs://my-bucket/my-video.mp4 - node analyze.js labels gs://my-bucket/my-video.mp4 + node analyze.js labels-gcs gs://my-bucket/my-video.mp4 + node analyze.js labels-file my-video.mp4 + node analyze.js unsafe-content gs://my-bucket/my-video.mp4 For more information, see https://cloud.google.com/video-intelligence/docs ``` diff --git a/video/analyze.js b/video/analyze.js index 33f9cb5a67..25330c590b 100644 --- a/video/analyze.js +++ b/video/analyze.js @@ -18,20 +18,20 @@ function analyzeFaces (gcsUri) { // [START analyze_faces] // Imports the Google Cloud Video Intelligence library - const Video = require('@google-cloud/videointelligence').v1beta1(); + const Video = require('@google-cloud/videointelligence'); // Instantiates a client - const video = Video.videoIntelligenceServiceClient(); + const video = Video().videoIntelligenceServiceClient(); // The GCS filepath of the video to analyze - // const gcsUri = 'gs://my-bucket/my-video.mp4' + // const gcsUri = 'gs://my-bucket/my-video.mp4'; const request = { inputUri: gcsUri, features: ['FACE_DETECTION'] }; - // Detect faces in a video + // Detects faces in a video video.annotateVideo(request) .then((results) => { const operation = results[0]; @@ -39,12 +39,13 @@ function analyzeFaces (gcsUri) { return operation.promise(); }) .then((results) => { - // Get faces for first video + // Gets faces const faces = results[0].annotationResults[0].faceAnnotations; + console.log('Faces:'); faces.forEach((face, faceIdx) => { - console.log('Thumbnail size:', face.thumbnail.buffer.length); + console.log('\tThumbnail size:', face.thumbnail.length); face.segments.forEach((segment, segmentIdx) => { - console.log(`Track ${segmentIdx} of face ${faceIdx}: frames ${segment.startTimeOffset} to ${segment.endTimeOffset}`); + console.log(`\tTrack ${segmentIdx} of face ${faceIdx}: frames ${segment.startTimeOffset} to ${segment.endTimeOffset}`); }); }); }) @@ -54,23 +55,23 @@ function analyzeFaces (gcsUri) { // [END analyze_faces] } -function analyzeLabels (gcsUri) { - // [START analyze_labels] +function analyzeLabelsGCS (gcsUri) { + // [START analyze_labels_gcs] // Imports the Google Cloud Video Intelligence library - const Video = require('@google-cloud/videointelligence').v1beta1(); + const Video = require('@google-cloud/videointelligence'); // Instantiates a client - const video = Video.videoIntelligenceServiceClient(); + const video = Video().videoIntelligenceServiceClient(); // The GCS filepath of the video to analyze - // const gcsUri = 'gs://my-bucket/my-video.mp4' + // const gcsUri = 'gs://my-bucket/my-video.mp4'; const request = { inputUri: gcsUri, features: ['LABEL_DETECTION'] }; - // Detect labels in a video + // Detects labels in a video video.annotateVideo(request) .then((results) => { const operation = results[0]; @@ -78,8 +79,9 @@ function analyzeLabels (gcsUri) { return operation.promise(); }) .then((results) => { - // Get labels for first video + // Gets labels const labels = results[0].annotationResults[0].labelAnnotations; + console.log('Labels:'); labels.forEach((label) => { console.log('Label description:', label.description); console.log('Locations:'); @@ -91,26 +93,73 @@ function analyzeLabels (gcsUri) { .catch((err) => { console.error('ERROR:', err); }); - // [END analyze_labels] + // [END analyze_labels_gcs] +} + +function analyzeLabelsLocal (path) { + // [START analyze_labels_local] + // Imports the Google Cloud Video Intelligence library + Node's fs library + const Video = require('@google-cloud/videointelligence'); + const fs = require('fs'); + + // Instantiates a client + const video = Video().videoIntelligenceServiceClient(); + + // The local filepath of the video to analyze + // const path = 'my-file.mp4'; + + // Reads a local video file and converts it to base64 + const file = fs.readFileSync(path); + const inputContent = file.toString('base64'); + + // Constructs request + const request = { + inputContent: inputContent, + features: ['LABEL_DETECTION'] + }; + + // Detects labels in a video + video.annotateVideo(request) + .then((results) => { + const operation = results[0]; + console.log('Waiting for operation to complete...'); + return operation.promise(); + }) + .then((results) => { + // Gets labels for first video + const labels = results[0].annotationResults[0].labelAnnotations; + console.log('Labels:'); + 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}`); + }); + }); + }) + .catch((err) => { + console.error('ERROR:', err); + }); + // [END analyze_labels_local] } function analyzeShots (gcsUri) { // [START analyze_shots] // Imports the Google Cloud Video Intelligence library - const Video = require('@google-cloud/videointelligence').v1beta1(); + const Video = require('@google-cloud/videointelligence'); // Instantiates a client - const video = Video.videoIntelligenceServiceClient(); + const video = Video().videoIntelligenceServiceClient(); // The GCS filepath of the video to analyze - // const gcsUri = 'gs://my-bucket/my-video.mp4' + // const gcsUri = 'gs://my-bucket/my-video.mp4'; const request = { inputUri: gcsUri, features: ['SHOT_CHANGE_DETECTION'] }; - // Detect camera shot changes + // Detects camera shot changes video.annotateVideo(request) .then((results) => { const operation = results[0]; @@ -118,8 +167,9 @@ function analyzeShots (gcsUri) { return operation.promise(); }) .then((results) => { - // Get shot changes for first video + // Gets shot changes const shotChanges = results[0].annotationResults[0].shotAnnotations; + console.log('Shot changes:'); shotChanges.forEach((shot, shotIdx) => { console.log(`Scene ${shotIdx}:`); console.log(`\tStart: ${shot.startTimeOffset}`); @@ -132,29 +182,85 @@ function analyzeShots (gcsUri) { // [END analyze_shots] } +function analyzeSafeSearch (gcsUri) { + // [START analyze_safe_search] + // Imports the Google Cloud Video Intelligence library + const Video = require('@google-cloud/videointelligence'); + + // Instantiates a client + const video = Video().videoIntelligenceServiceClient(); + + // The GCS filepath of the video to analyze + // const gcsUri = 'gs://my-bucket/my-video.mp4'; + + const request = { + inputUri: gcsUri, + features: ['SAFE_SEARCH_DETECTION'] + }; + + // Detects unsafe content + video.annotateVideo(request) + .then((results) => { + const operation = results[0]; + console.log('Waiting for operation to complete...'); + return operation.promise(); + }) + .then((results) => { + // Gets unsafe content + const safeSearchResults = results[0].annotationResults[0].safeSearchAnnotations; + console.log('Safe search results:'); + safeSearchResults.forEach((result) => { + console.log(`Frame ${result.timeOffset}:`); + console.log(`\tAdult: ${result.adult}`); + console.log(`\tSpoof: ${result.spoof}`); + console.log(`\tMedical: ${result.medical}`); + console.log(`\tViolent: ${result.violent}`); + console.log(`\tRacy: ${result.racy}`); + }); + }) + .catch((err) => { + console.error('ERROR:', err); + }); + // [END analyze_safe_search] +} + require(`yargs`) // eslint-disable-line .demand(1) .command( `faces `, - `Analyzes faces in a video using the Cloud Video Intelligence API.`, + `Analyzes faces in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.`, {}, (opts) => analyzeFaces(opts.gcsUri) ) .command( `shots `, - `Analyzes shot angles in a video using the Cloud Video Intelligence API.`, + `Analyzes shot angles in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.`, {}, (opts) => analyzeShots(opts.gcsUri) ) .command( - `labels `, - `Labels objects in a video using the Cloud Video Intelligence API.`, + `labels-gcs `, + `Labels objects in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.`, + {}, + (opts) => analyzeLabelsGCS(opts.gcsUri) + ) + .command( + `labels-file `, + `Labels objects in a video stored locally using the Cloud Video Intelligence API.`, + {}, + (opts) => analyzeLabelsLocal(opts.gcsUri) + ) + .command( + `safe-search `, + `Detects adult content in a video stored in Google Cloud Storage.`, {}, - (opts) => analyzeLabels(opts.gcsUri) + (opts) => analyzeSafeSearch(opts.gcsUri) ) .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`) + .example(`node $0 labels-gcs gs://my-bucket/my-video.mp4`) + .example(`node $0 labels-file my-video.mp4`) + .example(`node $0 safe-search gs://my-bucket/my-video.mp4`) .wrap(120) .recommendCommands() .epilogue(`For more information, see https://cloud.google.com/video-intelligence/docs`) diff --git a/video/quickstart.js b/video/quickstart.js index 72b9028ed8..df537216ad 100644 --- a/video/quickstart.js +++ b/video/quickstart.js @@ -47,7 +47,7 @@ video.annotateVideo(request) // Gets faces for video from its annotations const faces = annotations.faceAnnotations; faces.forEach((face, faceIdx) => { - console.log('Thumbnail size:', face.thumbnail.buffer.length); + console.log('Thumbnail size:', face.thumbnail.length); face.segments.forEach((segment, segmentIdx) => { console.log(`Track ${segmentIdx} of face ${faceIdx}: frames ${segment.startTimeOffset} to ${segment.endTimeOffset}`); }); diff --git a/video/resources/cat.mp4 b/video/resources/cat.mp4 new file mode 100644 index 0000000000..0e071b9ec6 Binary files /dev/null and b/video/resources/cat.mp4 differ diff --git a/video/system-test/analyze.test.js b/video/system-test/analyze.test.js index 830e7a3527..d4cf6e0660 100644 --- a/video/system-test/analyze.test.js +++ b/video/system-test/analyze.test.js @@ -25,19 +25,32 @@ const cmd = `node analyze.js`; const cwd = path.join(__dirname, `..`); // analyze_faces -test(`should analyze faces`, async (t) => { +test(`should analyze faces in a GCS file`, async (t) => { const output = await tools.runAsync(`${cmd} faces gs://nodejs-docs-samples/video/google_gmail.mp4`, cwd); t.regex(output, /Thumbnail size: \d+/); }); -// analyze_labels -test(`should analyze labels`, async (t) => { - const output = await tools.runAsync(`${cmd} labels gs://nodejs-docs-samples/video/cat.mp4`, cwd); +// analyze_labels_gcs +test(`should analyze labels in a GCS file`, async (t) => { + const output = await tools.runAsync(`${cmd} labels-gcs gs://nodejs-docs-samples/video/cat.mp4`, cwd); + t.regex(output, /Label description: Whiskers/); +}); + +// analyze_labels_local +test(`should analyze labels in a local file`, async (t) => { + const output = await tools.runAsync(`${cmd} labels-file resources/cat.mp4`, cwd); t.regex(output, /Label description: Whiskers/); }); // analyze_shots -test(`should analyze shots`, async (t) => { +test(`should analyze shots in a GCS file`, async (t) => { const output = await tools.runAsync(`${cmd} shots gs://nodejs-docs-samples/video/gbike_dinosaur.mp4`, cwd); t.regex(output, /Scene 0:/); }); + +// analyze_safe_search +test(`should analyze safe search results in a GCS file`, async (t) => { + const output = await tools.runAsync(`${cmd} safe-search gs://nodejs-docs-samples/video/google_gmail.mp4`, cwd); + t.regex(output, /Frame \d+/); + t.regex(output, /Spoof: \d+/); +});