diff --git a/video/cloud-client/analyze/beta_snippets.py b/video/cloud-client/analyze/beta_snippets.py index 58bf6faf8a20..356b9ec114d0 100644 --- a/video/cloud-client/analyze/beta_snippets.py +++ b/video/cloud-client/analyze/beta_snippets.py @@ -20,9 +20,22 @@ Usage Examples: python beta_snippets.py transcription \ gs://python-docs-samples-tests/video/googlework_short.mp4 + python beta_snippets.py video-text-gcs \ gs://python-docs-samples-tests/video/googlework_short.mp4 - python beta_snippets.py track-objects /resources/cat.mp4 + + python beta_snippets.py track-objects resources/cat.mp4 + + python beta_snippets.py streaming-labels resources/cat.mp4 + + python beta_snippets.py streaming-shot-change resources/cat.mp4 + + python beta_snippets.py streaming-objects resources/cat.mp4 + + python beta_snippets.py streaming-explicit-content resources/cat.mp4 + + python beta_snippets.py streaming-annotation-storage resources/cat.mp4 \ + gs://mybucket/myfolder """ import argparse @@ -274,6 +287,316 @@ def track_objects(path): return object_annotations +def detect_labels_streaming(path): + # [START video_streaming_label_detection_beta] + from google.cloud import videointelligence_v1p3beta1 as videointelligence + + # path = 'path_to_file' + + client = videointelligence.StreamingVideoIntelligenceServiceClient() + + # Set streaming config. + config = videointelligence.types.StreamingVideoConfig( + feature=(videointelligence.enums. + StreamingFeature.STREAMING_LABEL_DETECTION)) + + # config_request should be the first in the stream of requests. + config_request = videointelligence.types.StreamingAnnotateVideoRequest( + video_config=config) + + # Set the chunk size to 5MB (recommended less than 10MB). + chunk_size = 5 * 1024 * 1024 + + # Load file content. + stream = [] + with io.open(path, 'rb') as video_file: + while True: + data = video_file.read(chunk_size) + if not data: + break + stream.append(data) + + def stream_generator(): + yield config_request + for chunk in stream: + yield videointelligence.types.StreamingAnnotateVideoRequest( + input_content=chunk) + + requests = stream_generator() + + # streaming_annotate_video returns a generator. + responses = client.streaming_annotate_video(requests) + + # Each response corresponds to about 1 second of video. + for response in responses: + # Check for errors. + if response.error.message: + print(response.error.message) + break + + # Get the time offset of the response. + frame = response.annotation_results.label_annotations[0].frames[0] + time_offset = frame.time_offset.seconds + frame.time_offset.nanos / 1e9 + print('{}s:'.format(time_offset)) + + for annotation in response.annotation_results.label_annotations: + description = annotation.entity.description + # Every annotation has only one frame + confidence = annotation.frames[0].confidence + print('\t{} (confidence: {})'.format(description, confidence)) + # [END video_streaming_label_detection_beta] + + +def detect_shot_change_streaming(path): + # [START video_streaming_shot_change_detection_beta] + from google.cloud import videointelligence_v1p3beta1 as videointelligence + + # path = 'path_to_file' + + client = videointelligence.StreamingVideoIntelligenceServiceClient() + + # Set streaming config. + config = videointelligence.types.StreamingVideoConfig( + feature=(videointelligence.enums.StreamingFeature. + STREAMING_SHOT_CHANGE_DETECTION)) + + # config_request should be the first in the stream of requests. + config_request = videointelligence.types.StreamingAnnotateVideoRequest( + video_config=config) + + # Set the chunk size to 5MB (recommended less than 10MB). + chunk_size = 5 * 1024 * 1024 + + # Load file content. + stream = [] + with io.open(path, 'rb') as video_file: + while True: + data = video_file.read(chunk_size) + if not data: + break + stream.append(data) + + def stream_generator(): + yield config_request + for chunk in stream: + yield videointelligence.types.StreamingAnnotateVideoRequest( + input_content=chunk) + + requests = stream_generator() + + # streaming_annotate_video returns a generator. + responses = client.streaming_annotate_video(requests) + + # Each response corresponds to about 1 second of video. + for response in responses: + # Check for errors. + if response.error.message: + print(response.error.message) + break + + for annotation in response.annotation_results.shot_annotations: + start = (annotation.start_time_offset.seconds + + annotation.start_time_offset.nanos / 1e9) + end = (annotation.end_time_offset.seconds + + annotation.end_time_offset.nanos / 1e9) + + print('Shot: {}s to {}s'.format(start, end)) + # [END video_streaming_shot_change_detection_beta] + + +def track_objects_streaming(path): + # [START video_streaming_object_tracking_beta] + from google.cloud import videointelligence_v1p3beta1 as videointelligence + + # path = 'path_to_file' + + client = videointelligence.StreamingVideoIntelligenceServiceClient() + + # Set streaming config. + config = videointelligence.types.StreamingVideoConfig( + feature=(videointelligence.enums. + StreamingFeature.STREAMING_OBJECT_TRACKING)) + + # config_request should be the first in the stream of requests. + config_request = videointelligence.types.StreamingAnnotateVideoRequest( + video_config=config) + + # Set the chunk size to 5MB (recommended less than 10MB). + chunk_size = 5 * 1024 * 1024 + + # Load file content. + stream = [] + with io.open(path, 'rb') as video_file: + while True: + data = video_file.read(chunk_size) + if not data: + break + stream.append(data) + + def stream_generator(): + yield config_request + for chunk in stream: + yield videointelligence.types.StreamingAnnotateVideoRequest( + input_content=chunk) + + requests = stream_generator() + + # streaming_annotate_video returns a generator. + responses = client.streaming_annotate_video(requests) + + # Each response corresponds to about 1 second of video. + for response in responses: + # Check for errors. + if response.error.message: + print(response.error.message) + break + + # Get the time offset of the response. + frame = response.annotation_results.object_annotations[0].frames[0] + time_offset = frame.time_offset.seconds + frame.time_offset.nanos / 1e9 + print('{}s:'.format(time_offset)) + + for annotation in response.annotation_results.object_annotations: + description = annotation.entity.description + confidence = annotation.confidence + + # track_id tracks the same object in the video. + track_id = annotation.track_id + + print('\tEntity description: {}'.format(description)) + print('\tTrack Id: {}'.format(track_id)) + if annotation.entity.entity_id: + print('\tEntity id: {}'.format(annotation.entity.entity_id)) + + print('\tConfidence: {}'.format(confidence)) + + # Every annotation has only one frame + frame = annotation.frames[0] + box = frame.normalized_bounding_box + print('\tBounding box position:') + print('\tleft : {}'.format(box.left)) + print('\ttop : {}'.format(box.top)) + print('\tright : {}'.format(box.right)) + print('\tbottom: {}\n'.format(box.bottom)) + # [END video_streaming_object_tracking_beta] + + +def detect_explicit_content_streaming(path): + # [START video_streaming_explicit_content_detection_beta] + from google.cloud import videointelligence_v1p3beta1 as videointelligence + + # path = 'path_to_file' + + client = videointelligence.StreamingVideoIntelligenceServiceClient() + + # Set streaming config. + config = videointelligence.types.StreamingVideoConfig( + feature=(videointelligence.enums.StreamingFeature. + STREAMING_EXPLICIT_CONTENT_DETECTION)) + + # config_request should be the first in the stream of requests. + config_request = videointelligence.types.StreamingAnnotateVideoRequest( + video_config=config) + + # Set the chunk size to 5MB (recommended less than 10MB). + chunk_size = 5 * 1024 * 1024 + + # Load file content. + stream = [] + with io.open(path, 'rb') as video_file: + while True: + data = video_file.read(chunk_size) + if not data: + break + stream.append(data) + + def stream_generator(): + yield config_request + for chunk in stream: + yield videointelligence.types.StreamingAnnotateVideoRequest( + input_content=chunk) + + requests = stream_generator() + + # streaming_annotate_video returns a generator. + responses = client.streaming_annotate_video(requests) + + # Each response corresponds to about 1 second of video. + for response in responses: + # Check for errors. + if response.error.message: + print(response.error.message) + break + + for frame in response.annotation_results.explicit_annotation.frames: + time_offset = (frame.time_offset.seconds + + frame.time_offset.nanos / 1e9) + pornography_likelihood = videointelligence.enums.Likelihood( + frame.pornography_likelihood) + + print('Time: {}s'.format(time_offset)) + print('\tpornogaphy: {}'.format(pornography_likelihood.name)) + # [END video_streaming_explicit_content_detection_beta] + + +def annotation_to_storage_streaming(path, output_uri): + # [START video_streaming_annotation_to_storage_beta] + from google.cloud import videointelligence_v1p3beta1 as videointelligence + + # path = 'path_to_file' + # output_uri = 'gs://path_to_output' + + client = videointelligence.StreamingVideoIntelligenceServiceClient() + + # Set streaming config specifying the output_uri. + # The output_uri is the prefix of the actual output files. + storage_config = videointelligence.types.StreamingStorageConfig( + enable_storage_annotation_result=True, + annotation_result_storage_directory=output_uri) + # Here we use label detection as an example. + # All features support output to GCS. + config = videointelligence.types.StreamingVideoConfig( + feature=(videointelligence.enums. + StreamingFeature.STREAMING_LABEL_DETECTION), + storage_config=storage_config) + + # config_request should be the first in the stream of requests. + config_request = videointelligence.types.StreamingAnnotateVideoRequest( + video_config=config) + + # Set the chunk size to 5MB (recommended less than 10MB). + chunk_size = 5 * 1024 * 1024 + + # Load file content. + stream = [] + with io.open(path, 'rb') as video_file: + while True: + data = video_file.read(chunk_size) + if not data: + break + stream.append(data) + + def stream_generator(): + yield config_request + for chunk in stream: + yield videointelligence.types.StreamingAnnotateVideoRequest( + input_content=chunk) + + requests = stream_generator() + + # streaming_annotate_video returns a generator. + responses = client.streaming_annotate_video(requests) + + for response in responses: + # Check for errors. + if response.error.message: + print(response.error.message) + break + + print('Storage URI: {}'.format(response.annotation_results_uri)) + # [END video_streaming_annotation_to_storage_beta] + + if __name__ == '__main__': parser = argparse.ArgumentParser( description=__doc__, @@ -300,6 +623,29 @@ def track_objects(path): 'track-objects', help=track_objects.__doc__) video_object_tracking_parser.add_argument('path') + video_streaming_labels_parser = subparsers.add_parser( + 'streaming-labels', help=detect_labels_streaming.__doc__) + video_streaming_labels_parser.add_argument('path') + + video_streaming_shot_change_parser = subparsers.add_parser( + 'streaming-shot-change', help=detect_shot_change_streaming.__doc__) + video_streaming_shot_change_parser.add_argument('path') + + video_streaming_objects_parser = subparsers.add_parser( + 'streaming-objects', help=track_objects_streaming.__doc__) + video_streaming_objects_parser.add_argument('path') + + video_streaming_explicit_content_parser = subparsers.add_parser( + 'streaming-explicit-content', + help=detect_explicit_content_streaming.__doc__) + video_streaming_explicit_content_parser.add_argument('path') + + video_streaming_annotation_to_storage_parser = subparsers.add_parser( + 'streaming-annotation-storage', + help=annotation_to_storage_streaming.__doc__) + video_streaming_annotation_to_storage_parser.add_argument('path') + video_streaming_annotation_to_storage_parser.add_argument('output_uri') + args = parser.parse_args() if args.command == 'transcription': @@ -312,3 +658,13 @@ def track_objects(path): track_objects_gcs(args.gcs_uri) elif args.command == 'track-objects': track_objects(args.path) + elif args.command == 'streaming-labels': + detect_labels_streaming(args.path) + elif args.command == 'streaming-shot-change': + detect_shot_change_streaming(args.path) + elif args.command == 'streaming-objects': + track_objects_streaming(args.path) + elif args.command == 'streaming-explicit-content': + detect_explicit_content_streaming(args.path) + elif args.command == 'streaming-annotation-storage': + annotation_to_storage_streaming(args.path, args.output_uri) diff --git a/video/cloud-client/analyze/beta_snippets_test.py b/video/cloud-client/analyze/beta_snippets_test.py index 7dcec8e64814..b6b61cdb81cd 100644 --- a/video/cloud-client/analyze/beta_snippets_test.py +++ b/video/cloud-client/analyze/beta_snippets_test.py @@ -14,14 +14,43 @@ # See the License for the specific language governing permissions and # limitations under the License. -import pytest +import time +import urllib2 +import uuid import beta_snippets +from google.cloud import storage +import pytest + POSSIBLE_TEXTS = ['Google', 'SUR', 'SUR', 'ROTO', 'Vice President', '58oo9', 'LONDRES', 'OMAR', 'PARIS', 'METRO', 'RUE', 'CARLO'] +@pytest.fixture(scope='session') +def video_path(tmpdir_factory): + file = urllib2.urlopen( + 'http://storage.googleapis.com/cloud-samples-data/video/cat.mp4') + path = tmpdir_factory.mktemp('video').join('file.mp4') + with open(str(path), 'wb') as f: + f.write(file.read()) + + return str(path) + + +@pytest.fixture(scope='function') +def bucket(): + # Create a temporaty bucket to store annotation output. + bucket_name = str(uuid.uuid1()) + storage_client = storage.Client() + bucket = storage_client.create_bucket(bucket_name) + + yield bucket + + # Teardown. + bucket.delete(force=True) + + @pytest.mark.slow def test_speech_transcription(capsys): beta_snippets.speech_transcription( @@ -30,6 +59,55 @@ def test_speech_transcription(capsys): assert 'cultural' in out +@pytest.mark.slow +def test_detect_labels_streaming(capsys, video_path): + beta_snippets.detect_labels_streaming(video_path) + + out, _ = capsys.readouterr() + assert 'cat' in out + + +@pytest.mark.slow +def test_detect_shot_change_streaming(capsys, video_path): + beta_snippets.detect_shot_change_streaming(video_path) + + out, _ = capsys.readouterr() + assert 'Shot' in out + + +@pytest.mark.slow +def test_track_objects_streaming(capsys, video_path): + beta_snippets.track_objects_streaming(video_path) + + out, _ = capsys.readouterr() + assert 'cat' in out + + +@pytest.mark.slow +def test_detect_explicit_content_streaming(capsys, video_path): + beta_snippets.detect_explicit_content_streaming(video_path) + + out, _ = capsys.readouterr() + assert 'Time' in out + + +@pytest.mark.slow +def test_annotation_to_storage_streaming(capsys, video_path, bucket): + output_uri = 'gs://{}'.format(bucket.name) + beta_snippets.annotation_to_storage_streaming(video_path, output_uri) + + out, _ = capsys.readouterr() + assert 'Storage' in out + + # It takes a few seconds before the results show up on GCS. + time.sleep(3) + + # Confirm that one output blob had been written to GCS. + blobs_iterator = bucket.list_blobs() + blobs = [blob for blob in blobs_iterator] + assert len(blobs) == 1 + + @pytest.mark.slow def test_detect_text(): in_file = './resources/googlework_short.mp4' diff --git a/video/cloud-client/analyze/requirements.txt b/video/cloud-client/analyze/requirements.txt index 034977ce4cf1..ba28944b6523 100644 --- a/video/cloud-client/analyze/requirements.txt +++ b/video/cloud-client/analyze/requirements.txt @@ -1 +1,2 @@ -google-cloud-videointelligence==1.7.0 +google-cloud-videointelligence==1.8.0 +google-cloud-storage==1.14.0