Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Address comments #388

Merged
merged 3 commits into from
May 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 19 additions & 19 deletions video/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,27 @@

# Google Cloud Video Intelligence API Node.js Samples

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.
[![Build](https://storage.googleapis.com/cloud-docs-samples-badges/GoogleCloudPlatform/nodejs-docs-samples/nodejs-docs-samples-videointelligence.svg)]()

[video_docs]: https://cloud.google.com/video-intelligence/docs/
The [Cloud Video Intelligence API](https://cloud.google.com/video-intelligence) allows developers to use Google video analysis technology as part of their applications.

## Table of Contents

* [Setup](#setup)
* [Samples](#samples)
* [Analyze](#analyze)
* [Video Intelligence](#video-intelligence)
* [Running the tests](#running-the-tests)

## Setup

1. Read [Prerequisites][prereq] and [How to run a sample][run] first.
1. Install dependencies:

With `npm`:
With **npm**:

npm install

With `yarn`:
With **yarn**:

yarn install

Expand All @@ -33,45 +31,47 @@ integrate video analysis within applications, including video labeling, safe sea

## Samples

### Analyze
### Video Intelligence

View the [documentation][analyze_docs] or the [source code][analyze_code].
View the [documentation][video_0_docs] or the [source code][video_0_code].

__Usage:__ `node analyze.js --help`

```
Commands:
faces <gcsUri> Analyzes faces in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.
shots <gcsUri> Analyzes shot angles in a video stored in Google Cloud Storage using the Cloud Video
Intelligence API.
labels-gcs <gcsUri> Labels objects in a video stored in Google Cloud Storage using the Cloud Video Intelligence API.
labels-file <gcsUri> Labels objects in a video stored locally using the Cloud Video Intelligence API.
safe-search <gcsUri> Detects adult content in a video stored in Google Cloud Storage.

Options:
--help Show help [boolean]

Examples:
node analyze.js shots 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
node analyze.js faces gs://demomaker/volleyball_court.mp4
node analyze.js shots gs://demomaker/volleyball_court.mp4
node analyze.js labels-gcs gs://demomaker/volleyball_court.mp4
node analyze.js labels-file cat.mp4
node analyze.js safe-search gs://demomaker/volleyball_court.mp4

For more information, see https://cloud.google.com/video-intelligence/docs
```

[analyze_docs]: https://cloud.google.com/video-intelligence/docs
[analyze_code]: analyze.js
[video_0_docs]: https://cloud.google.com/video-intelligence/docs
[video_0_code]: analyze.js

## Running the tests

1. Set the `GCLOUD_PROJECT` and `GOOGLE_APPLICATION_CREDENTIALS` environment
variables.
1. Set the **GCLOUD_PROJECT** and **GOOGLE_APPLICATION_CREDENTIALS** environment variables.

1. Run the tests:

With `npm`:
With **npm**:

npm test

With `yarn`:
With **yarn**:

yarn test
54 changes: 38 additions & 16 deletions video/analyze.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,12 @@ function analyzeFaces (gcsUri) {
faces.forEach((face, faceIdx) => {
console.log('\tThumbnail size:', face.thumbnail.length);
face.segments.forEach((segment, segmentIdx) => {
console.log(`\tTrack ${segmentIdx} of face ${faceIdx}: frames ${segment.startTimeOffset} to ${segment.endTimeOffset}`);
if (segment.startTimeOffset === -1 && segment.endTimeOffset === -1) {
console.log(`\tEntire video`);
} else {
console.log(`\tStart: ${segment.startTimeOffset / 1e6}s`);
console.log(`\tEnd: ${segment.endTimeOffset / 1e6}s`);
}
});
});
})
Expand Down Expand Up @@ -86,7 +91,12 @@ function analyzeLabelsGCS (gcsUri) {
console.log('Label description:', label.description);
console.log('Locations:');
label.locations.forEach((location) => {
console.log(`\tFrames ${location.segment.startTimeOffset} to ${location.segment.endTimeOffset}`);
if (location.segment.startTimeOffset === -1 && location.segment.endTimeOffset === -1) {
console.log(`\tEntire video`);
} else {
console.log(`\tStart: ${location.segment.startTimeOffset / 1e6}s`);
console.log(`\tEnd: ${location.segment.endTimeOffset / 1e6}s`);
}
});
});
})
Expand Down Expand Up @@ -133,7 +143,12 @@ function analyzeLabelsLocal (path) {
console.log('Label description:', label.description);
console.log('Locations:');
label.locations.forEach((location) => {
console.log(`\tFrames ${location.segment.startTimeOffset} to ${location.segment.endTimeOffset}`);
if (location.segment.startTimeOffset === -1 && location.segment.endTimeOffset === -1) {
console.log(`\tEntire video`);
} else {
console.log(`\tStart: ${location.segment.startTimeOffset / 1e6}s`);
console.log(`\tEnd: ${location.segment.endTimeOffset / 1e6}s`);
}
});
});
})
Expand Down Expand Up @@ -172,8 +187,12 @@ function analyzeShots (gcsUri) {
console.log('Shot changes:');
shotChanges.forEach((shot, shotIdx) => {
console.log(`Scene ${shotIdx}:`);
console.log(`\tStart: ${shot.startTimeOffset}`);
console.log(`\tEnd: ${shot.endTimeOffset}`);
if (shot.startTimeOffset === -1 && shot.endTimeOffset === -1) {
console.log(`\tEntire video`);
} else {
console.log(`\tStart: ${shot.startTimeOffset}`);
console.log(`\tEnd: ${shot.endTimeOffset}`);
}
});
})
.catch((err) => {
Expand All @@ -198,6 +217,9 @@ function analyzeSafeSearch (gcsUri) {
features: ['SAFE_SEARCH_DETECTION']
};

// Human-readable likelihoods
const likelihoods = ['UNKNOWN', 'VERY_UNLIKELY', 'UNLIKELY', 'POSSIBLE', 'LIKELY', 'VERY_LIKELY'];

// Detects unsafe content
video.annotateVideo(request)
.then((results) => {
Expand All @@ -210,12 +232,12 @@ function analyzeSafeSearch (gcsUri) {
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}`);
console.log(`Time: ${result.timeOffset / 1e6}s`);
console.log(`\tAdult: ${likelihoods[result.adult]}`);
console.log(`\tSpoof: ${likelihoods[result.spoof]}`);
console.log(`\tMedical: ${likelihoods[result.medical]}`);
console.log(`\tViolent: ${likelihoods[result.violent]}`);
console.log(`\tRacy: ${likelihoods[result.racy]}`);
});
})
.catch((err) => {
Expand Down Expand Up @@ -256,11 +278,11 @@ require(`yargs`) // eslint-disable-line
{},
(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-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`)
.example(`node $0 faces gs://demomaker/volleyball_court.mp4`)
.example(`node $0 shots gs://demomaker/volleyball_court.mp4`)
.example(`node $0 labels-gcs gs://demomaker/volleyball_court.mp4`)
.example(`node $0 labels-file cat.mp4`)
.example(`node $0 safe-search gs://demomaker/volleyball_court.mp4`)
.wrap(120)
.recommendCommands()
.epilogue(`For more information, see https://cloud.google.com/video-intelligence/docs`)
Expand Down
19 changes: 16 additions & 3 deletions video/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,27 @@
},
"dependencies": {
"@google-cloud/videointelligence": "https://storage.googleapis.com/videointelligence-alpha/videointelligence-nodejs.tar.gz",
"googleapis": "19.0.0",
"safe-buffer": "5.0.1",
"yargs": "7.1.0"
},
"devDependencies": {
"@google-cloud/nodejs-repo-tools": "1.4.7",
"ava": "0.19.1"
"@google-cloud/nodejs-repo-tools": "1.4.13",
"ava": "0.19.1",
"proxyquire": "1.7.11"
},
"cloud-repo-tools": {
"requiresKeyFile": true,
"requiresProjectId": true
"requiresProjectId": true,
"product": "video",
"samples": [
{
"id": "video",
"name": "Video Intelligence",
"file": "analyze.js",
"docs_link": "https://cloud.google.com/video-intelligence/docs",
"usage": "node analyze.js --help"
}
]
}
}
30 changes: 22 additions & 8 deletions video/quickstart.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const video = Video.videoIntelligenceServiceClient({
});

// The GCS filepath of the video to analyze
const gcsUri = 'gs://nodejs-docs-samples/videointelligence_quickstart.mp4';
const gcsUri = 'gs://demomaker/volleyball_court.mp4';

// Construct request
const request = {
Expand All @@ -49,26 +49,40 @@ video.annotateVideo(request)
faces.forEach((face, faceIdx) => {
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}`);
console.log(`Face #${faceIdx}, appearance #${segmentIdx}:`);
if (segment.startTimeOffset === -1 && segment.endTimeOffset === -1) {
console.log(`\tEntire video`);
} else {
console.log(`\tStart: ${segment.startTimeOffset / 1e6}s`);
console.log(`\tEnd: ${segment.endTimeOffset / 1e6}s`);
}
});
});

// Gets labels for video from its annotations
const labels = annotations.labelAnnotations;
labels.forEach((label) => {
console.log('Label description:', label.description);
console.log('Locations:');
console.log(`Label ${label.description} occurs at:`);
label.locations.forEach((location) => {
console.log(`\tFrames ${location.segment.startTimeOffset} to ${location.segment.endTimeOffset}`);
if (location.segment.startTimeOffset === -1 && location.segment.endTimeOffset === -1) {
console.log(`\tEntire video`);
} else {
console.log(`\tStart: ${location.segment.startTimeOffset / 1e6}s`);
console.log(`\tEnd: ${location.segment.endTimeOffset / 1e6}s`);
}
});
});

// Gets shot changes for video from its annotations
const shotChanges = annotations.shotAnnotations;
shotChanges.forEach((shot, shotIdx) => {
console.log(`Scene ${shotIdx}:`);
console.log(`\tStart: ${shot.startTimeOffset}`);
console.log(`\tEnd: ${shot.endTimeOffset}`);
console.log(`Scene ${shotIdx} occurs from:`);
if (shot.startTimeOffset === -1 && shot.endTimeOffset === -1) {
console.log(`\tEntire video`);
} else {
console.log(`\tStart: ${shot.startTimeOffset / 1e6}s`);
console.log(`\tEnd: ${shot.endTimeOffset / 1e6}s`);
}
});
})
.catch((err) => {
Expand Down
6 changes: 3 additions & 3 deletions video/system-test/analyze.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ test(`should analyze labels in a local file`, async (t) => {

// analyze_shots
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);
const output = await tools.runAsync(`${cmd} shots gs://nodejs-docs-samples/video/google_gmail.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+/);
t.regex(output, /Time: \d\.\d+s/);
t.regex(output, /Spoof:/);
});
6 changes: 2 additions & 4 deletions video/system-test/quickstart.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ const cwd = path.join(__dirname, `..`);

test(`should analyze a hardcoded video`, async (t) => {
const output = await tools.runAsync(cmd, cwd);
t.regex(output, /Label description:/);
t.regex(output, /Frames \d+ to \d+/);
t.regex(output, /Track \d+ of face \d+: frames \d+ to \d+/);
t.regex(output, /Scene \d+/);
t.regex(output, /Label Property occurs at:/);
t.regex(output, /Scene \d+ occurs from:/);
});