Detecting shot changes in videos

Shot change analysis detects shot changes in a video.

This section demonstrates a few ways to analyze a video for shot changes.

Here is an example of performing video analysis for shot changes on a file located in Google Cloud Storage.

Looking for something more in-depth? Check out our detailed Python tutorial.

REST API

The following shows how to send a POST request to the videos:annotate method. The example uses the access token for a service account set up for the project using the Google Cloud Platform Cloud SDK. For instructions on installing the Cloud SDK, setting up a project with a service account, and obtaining an access token, see the Video Intelligence API Quickstart.

curl -X POST \
     -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
     -H "Content-Type: application/json; charset=utf-8" \
     --data '{
      "inputUri": "gs://demomaker/gbikes_dinosaur.mp4",
      "features": ["SHOT_CHANGE_DETECTION"]
    }' "https://videointelligence.googleapis.com/v1/videos:annotate"

If the request is successful, the Cloud Video Intelligence API returns the name for your operation. The following shows an example of such a response, where project-name is the name of your project and operation-id is the ID of the long running operation created for the request.

{
  "name": "projects/project-name/locations/us-west1/operations/operation-id"
}

To retrieve the result of the operation, make a GET request, using the operation name returned from the call to videos:annotate, as shown in the following example.

curl -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
  https://videointelligence.googleapis.com/v1/operation-name

Shot detection annotations are returned as a shotAnnotations list.

    [
      {
        "inputUri": "/demomaker/gbikes_dinosaur.mp4",
        "shotAnnotations": [
          {
            "startTimeOffset": "0s",
            "endTimeOffset": "5.167104s"
          },
          {
            "startTimeOffset": "5.236736s",
            "endTimeOffset": "10.072064s"
          },
          {
            "startTimeOffset": "10.106880s",
            "endTimeOffset": "28.139520s"
          },
          {
            "startTimeOffset": "28.174336s",
            "endTimeOffset": "42.768384s"
          }
        ]
      }
    ]

C#

public static object AnalyzeShotsGcs(string uri)
{
    var client = VideoIntelligenceServiceClient.Create();
    var request = new AnnotateVideoRequest()
    {
        InputUri = uri,
        Features = { Feature.ShotChangeDetection }
    };
    var op = client.AnnotateVideo(request).PollUntilCompleted();
    foreach (var result in op.Result.AnnotationResults)
    {
        foreach (var annotation in result.ShotAnnotations)
        {
            Console.Out.WriteLine("Start Time Offset: {0}\tEnd Time Offset: {1}",
                annotation.StartTimeOffset, annotation.EndTimeOffset);
        }
    }
    return 0;
}

Go


func shotChangeURI(w io.Writer, file string) error {
	ctx := context.Background()
	client, err := video.NewClient(ctx)
	if err != nil {
		return err
	}

	op, err := client.AnnotateVideo(ctx, &videopb.AnnotateVideoRequest{
		Features: []videopb.Feature{
			videopb.Feature_SHOT_CHANGE_DETECTION,
		},
		InputUri: file,
	})
	if err != nil {
		return err
	}
	resp, err := op.Wait(ctx)
	if err != nil {
		return err
	}

	// A single video was processed. Get the first result.
	result := resp.AnnotationResults[0].ShotAnnotations

	for _, shot := range result {
		start, _ := ptypes.Duration(shot.StartTimeOffset)
		end, _ := ptypes.Duration(shot.EndTimeOffset)

		fmt.Fprintf(w, "Shot: %s to %s\n", start, end)
	}

	return nil
}

Java

// Instantiate a com.google.cloud.videointelligence.v1.VideoIntelligenceServiceClient
try (VideoIntelligenceServiceClient client = VideoIntelligenceServiceClient.create()) {
  // Provide path to file hosted on GCS as "gs://bucket-name/..."
  AnnotateVideoRequest request = AnnotateVideoRequest.newBuilder()
      .setInputUri(gcsUri)
      .addFeatures(Feature.SHOT_CHANGE_DETECTION)
      .build();

  // Create an operation that will contain the response when the operation completes.
  OperationFuture<AnnotateVideoResponse, AnnotateVideoProgress> response =
      client.annotateVideoAsync(request);

  System.out.println("Waiting for operation to complete...");
  // Print detected shot changes and their location ranges in the analyzed video.
  for (VideoAnnotationResults result : response.get().getAnnotationResultsList()) {
    if (result.getShotAnnotationsCount() > 0) {
      System.out.println("Shots: ");
      for (VideoSegment segment : result.getShotAnnotationsList()) {
        double startTime = segment.getStartTimeOffset().getSeconds()
            + segment.getStartTimeOffset().getNanos() / 1e9;
        double endTime = segment.getEndTimeOffset().getSeconds()
            + segment.getEndTimeOffset().getNanos() / 1e9;
        System.out.printf("Location: %.3f:%.3f\n", startTime, endTime);
      }
    } else {
      System.out.println("No shot changes detected in " + gcsUri);
    }
  }
}

Node.js

// Imports the Google Cloud Video Intelligence library
const video = require('@google-cloud/video-intelligence').v1;

// Creates a client
const client = new video.VideoIntelligenceServiceClient();

/**
 * TODO(developer): Uncomment the following line before running the sample.
 */
// const gcsUri = 'GCS URI of file to analyze, e.g. gs://my-bucket/my-video.mp4';

const request = {
  inputUri: gcsUri,
  features: ['SHOT_CHANGE_DETECTION'],
};

// Detects camera shot changes
const [operation] = await client.annotateVideo(request);
console.log('Waiting for operation to complete...');
const [operationResult] = await operation.promise();
// Gets shot changes
const shotChanges = operationResult.annotationResults[0].shotAnnotations;
console.log('Shot changes:');

if (shotChanges.length === 1) {
  console.log(`The entire video is one shot.`);
} else {
  shotChanges.forEach((shot, shotIdx) => {
    console.log(`Scene ${shotIdx} occurs from:`);
    if (shot.startTimeOffset === undefined) {
      shot.startTimeOffset = {};
    }
    if (shot.endTimeOffset === undefined) {
      shot.endTimeOffset = {};
    }
    if (shot.startTimeOffset.seconds === undefined) {
      shot.startTimeOffset.seconds = 0;
    }
    if (shot.startTimeOffset.nanos === undefined) {
      shot.startTimeOffset.nanos = 0;
    }
    if (shot.endTimeOffset.seconds === undefined) {
      shot.endTimeOffset.seconds = 0;
    }
    if (shot.endTimeOffset.nanos === undefined) {
      shot.endTimeOffset.nanos = 0;
    }
    console.log(
      `\tStart: ${shot.startTimeOffset.seconds}` +
        `.${(shot.startTimeOffset.nanos / 1e6).toFixed(0)}s`
    );
    console.log(
      `\tEnd: ${shot.endTimeOffset.seconds}.` +
        `${(shot.endTimeOffset.nanos / 1e6).toFixed(0)}s`
    );
  });
}

Python

For more information on installing and using the Cloud Video Intelligence API Client Library for Python, refer to Cloud Video Intelligence API Client Libraries.
""" Detects camera shot changes. """
video_client = videointelligence.VideoIntelligenceServiceClient()
features = [videointelligence.enums.Feature.SHOT_CHANGE_DETECTION]
operation = video_client.annotate_video(path, features=features)
print('\nProcessing video for shot change annotations:')

result = operation.result(timeout=90)
print('\nFinished processing.')

# first result is retrieved because a single video was processed
for i, shot in enumerate(result.annotation_results[0].shot_annotations):
    start_time = (shot.start_time_offset.seconds +
                  shot.start_time_offset.nanos / 1e9)
    end_time = (shot.end_time_offset.seconds +
                shot.end_time_offset.nanos / 1e9)
    print('\tShot {}: {} to {}'.format(i, start_time, end_time))

PHP

use Google\Cloud\VideoIntelligence\V1\VideoIntelligenceServiceClient;
use Google\Cloud\VideoIntelligence\V1\Feature;

/** Uncomment and populate these variables in your code */
// $uri = 'The cloud storage object to analyze (gs://your-bucket-name/your-object-name)';
// $options = [];

# Instantiate a client.
$video = new VideoIntelligenceServiceClient();

# Execute a request.
$operation = $video->annotateVideo([
    'inputUri' => $uri,
    'features' => [Feature::SHOT_CHANGE_DETECTION]
]);

# Wait for the request to complete.
$operation->pollUntilComplete($options);

# Print the result.
if ($operation->operationSucceeded()) {
    $results = $operation->getResult()->getAnnotationResults()[0];
    foreach ($results->getShotAnnotations() as $shot) {
        $start = $shot->getStartTimeOffset();
        $end = $shot->getEndTimeOffset();
        printf('Shot: %ss to %ss' . PHP_EOL,
            $start->getSeconds() + $start->getNanos()/1000000000.0,
            $end->getSeconds() + $end->getNanos()/1000000000.0);
    }
} else {
    print_r($operation->getError());
}

Ruby

# path = "Path to a video file on Google Cloud Storage: gs://bucket/video.mp4"

require "google/cloud/video_intelligence"

video = Google::Cloud::VideoIntelligence.new

# Register a callback during the method call
operation = video.annotate_video input_uri: path, features: [:SHOT_CHANGE_DETECTION] do |operation|
  raise operation.results.message? if operation.error?
  puts "Finished processing."

  # first result is retrieved because a single video was processed
  annotation_result = operation.results.annotation_results.first
  puts "Scenes:"

  annotation_result.shot_annotations.each do |shot_annotation|
    start_time = (shot_annotation.start_time_offset.seconds +
                   shot_annotation.start_time_offset.nanos / 1e9)
    end_time =   (shot_annotation.end_time_offset.seconds +
                   shot_annotation.end_time_offset.nanos / 1e9)

    puts "#{start_time} to #{end_time}"
  end
end

puts "Processing video for shot change annotations:"
operation.wait_until_done!

Kunde den här sidan hjälpa dig? Berätta:

Skicka feedback om ...

Cloud Video Intelligence API Documentation