Python으로 Video Intelligence API 사용

1. 개요

cfaa6ffa7bc5ca70.png

Video Intelligence API를 사용하면 Google 동영상 분석 기술을 애플리케이션의 일부로 사용할 수 있습니다.

이 실습에서는 Python과 함께 Video Intelligence API를 사용하는 방법을 중점적으로 살펴봅니다.

학습할 내용

  • 환경을 설정하는 방법
  • Python 설정 방법
  • 장면 변화를 감지하는 방법
  • 라벨 감지 방법
  • 선정적인 콘텐츠를 감지하는 방법
  • 음성 스크립트 작성 방법
  • 텍스트 감지 및 추적 방법
  • 객체 감지 및 추적 방법
  • 로고 감지 및 추적 방법

필요한 항목

  • Google Cloud 프로젝트
  • 브라우저(Chrome 또는 Firefox 등)
  • Python 사용에 관한 지식

설문조사

이 튜토리얼을 어떻게 사용하실 계획인가요?

읽기만 할 계획입니다 읽은 다음 연습 활동을 완료할 계획입니다

귀하의 Python 사용 경험이 어떤지 평가해 주세요.

초급 중급 고급

귀하의 Google Cloud 서비스 경험을 평가해 주세요.

<ph type="x-smartling-placeholder"></ph> 초보자 중급 숙련도

2. 설정 및 요건

자습형 환경 설정

  1. Google Cloud Console에 로그인하여 새 프로젝트를 만들거나 기존 프로젝트를 재사용합니다. 아직 Gmail이나 Google Workspace 계정이 없는 경우 계정을 만들어야 합니다.

b35bf95b8bf3d5d8.png

a99b7ace416376c4.png

bd84a6d3004737c5.png

  • 프로젝트 이름은 이 프로젝트 참가자의 표시 이름입니다. 이는 Google API에서 사용하지 않는 문자열이며 언제든지 업데이트할 수 있습니다.
  • 프로젝트 ID는 모든 Google Cloud 프로젝트에서 고유하며, 변경할 수 없습니다(설정된 후에는 변경할 수 없음). Cloud 콘솔은 고유한 문자열을 자동으로 생성합니다. 일반적으로는 신경 쓰지 않아도 됩니다. 대부분의 Codelab에서는 프로젝트 ID (일반적으로 PROJECT_ID로 식별됨)를 참조해야 합니다. 생성된 ID가 마음에 들지 않으면 다른 임의 ID를 생성할 수 있습니다. 또는 직접 시도해 보고 사용 가능한지 확인할 수도 있습니다. 이 단계 이후에는 변경할 수 없으며 프로젝트 기간 동안 유지됩니다.
  • 참고로 세 번째 값은 일부 API에서 사용하는 프로젝트 번호입니다. 이 세 가지 값에 대한 자세한 내용은 문서를 참고하세요.
  1. 다음으로 Cloud 리소스/API를 사용하려면 Cloud 콘솔에서 결제를 사용 설정해야 합니다. 이 Codelab 실행에는 많은 비용이 들지 않습니다. 이 튜토리얼이 끝난 후에 요금이 청구되지 않도록 리소스를 종료하려면 만든 리소스 또는 프로젝트를 삭제하면 됩니다. Google Cloud 신규 사용자는 300달러(USD) 상당의 무료 체험판 프로그램에 참여할 수 있습니다.

Cloud Shell 시작

Google Cloud를 노트북에서 원격으로 실행할 수도 있지만 이 Codelab에서는 Cloud에서 실행되는 명령줄 환경인 Cloud Shell을 사용합니다.

Cloud Shell 활성화

  1. Cloud Console에서 Cloud Shell 활성화853e55310c205094.png를 클릭합니다.

55efc1aaa7a4d3ad.png

Cloud Shell을 처음 시작하는 경우에는 무엇이 있는지 설명하는 중간 화면이 표시됩니다. 중간 화면이 표시되면 계속을 클릭합니다.

92662c6a846a5c.png

Cloud Shell을 프로비저닝하고 연결하는 데 몇 분 정도만 걸립니다.

9f0e51b578fecce5.png

가상 머신에는 필요한 개발 도구가 모두 들어 있습니다. 영구적인 5GB 홈 디렉터리를 제공하고 Google Cloud에서 실행되므로 네트워크 성능과 인증이 크게 개선됩니다. 이 Codelab에서 대부분의 작업은 브라우저를 사용하여 수행할 수 있습니다.

Cloud Shell에 연결되면 인증이 완료되었고 프로젝트가 자신의 프로젝트 ID로 설정된 것을 확인할 수 있습니다.

  1. Cloud Shell에서 다음 명령어를 실행하여 인증되었는지 확인합니다.
gcloud auth list

명령어 결과

 Credentialed Accounts
ACTIVE  ACCOUNT
*       <my_account>@<my_domain.com>

To set the active account, run:
    $ gcloud config set account `ACCOUNT`
  1. Cloud Shell에서 다음 명령어를 실행하여 gcloud 명령어가 프로젝트를 알고 있는지 확인합니다.
gcloud config list project

명령어 결과

[core]
project = <PROJECT_ID>

또는 다음 명령어로 설정할 수 있습니다.

gcloud config set project <PROJECT_ID>

명령어 결과

Updated property [core/project].

3. 환경 설정

Video Intelligence API를 사용하려면 먼저 Cloud Shell에서 다음 명령어를 실행하여 API를 사용 설정합니다.

gcloud services enable videointelligence.googleapis.com

다음과 같은 결과를 확인할 수 있습니다.

Operation "operations/..." finished successfully.

이제 Video Intelligence API를 사용할 수 있습니다.

홈 디렉터리로 이동합니다.

cd ~

Python 가상 환경을 만들어 종속 항목을 격리합니다.

virtualenv venv-videointel

가상 환경을 활성화합니다.

source venv-videointel/bin/activate

IPython 및 Video Intelligence API 클라이언트 라이브러리를 설치합니다.

pip install ipython google-cloud-videointelligence

다음과 같은 결과를 확인할 수 있습니다.

...
Installing collected packages: ..., ipython, google-cloud-videointelligence
Successfully installed ... google-cloud-videointelligence-2.11.0 ...

이제 Video Intelligence API 클라이언트 라이브러리를 사용할 준비가 되었습니다.

다음 단계에서는 이전 단계에서 설치한 대화형 Python 인터프리터인 IPython을 사용합니다. Cloud Shell에서 ipython를 실행하여 세션을 시작합니다.

ipython

다음과 같은 결과를 확인할 수 있습니다.

Python 3.9.2 (default, Feb 28 2021, 17:03:44)
Type 'copyright', 'credits' or 'license' for more information
IPython 8.12.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]:

4. 샘플 동영상

Video Intelligence API를 사용하여 Cloud Storage에 저장되었거나 데이터 바이트로 제공되는 동영상에 주석을 달 수 있습니다.

다음 단계에서는 Cloud Storage에 저장된 샘플 동영상을 사용합니다. 브라우저에서 동영상을 시청할 수 있습니다.

afe058b29c480d42.png

준비, 땅!

5. 장면 변화 감지

Video Intelligence API를 사용하여 동영상의 장면 변화를 감지할 수 있습니다. 샷이란 시각적 연속성을 지닌 일련의 프레임으로 구성된 동영상의 한 부분입니다.

다음 코드를 IPython 세션에 복사합니다.

from typing import cast

from google.cloud import videointelligence_v1 as vi


def detect_shot_changes(video_uri: str) -> vi.VideoAnnotationResults:
    video_client = vi.VideoIntelligenceServiceClient()
    features = [vi.Feature.SHOT_CHANGE_DETECTION]
    request = vi.AnnotateVideoRequest(input_uri=video_uri, features=features)

    print(f'Processing video: "{video_uri}"...')
    operation = video_client.annotate_video(request)

    # Wait for operation to complete
    response = cast(vi.AnnotateVideoResponse, operation.result())
    # A single video is processed
    results = response.annotation_results[0]

    return results
    

잠시 코드를 살펴보고 SHOT_CHANGE_DETECTION 매개변수와 함께 annotate_video 클라이언트 라이브러리 메서드를 사용하여 동영상을 분석하고 장면 변화를 감지하는 방법을 살펴보세요.

동영상을 분석하는 함수를 호출합니다.

video_uri = "gs://cloud-samples-data/video/JaneGoodall.mp4"

results = detect_shot_changes(video_uri)

동영상이 처리될 때까지 기다립니다.

Processing video: "gs://cloud-samples-data/video/JaneGoodall.mp4"...

이 함수를 추가하여 동영상 장면을 출력합니다.

def print_video_shots(results: vi.VideoAnnotationResults):
    shots = results.shot_annotations
    print(f" Video shots: {len(shots)} ".center(40, "-"))
    for i, shot in enumerate(shots):
        t1 = shot.start_time_offset.total_seconds()
        t2 = shot.end_time_offset.total_seconds()
        print(f"{i+1:>3} | {t1:7.3f} | {t2:7.3f}")
        

함수 호출

print_video_shots(results)

다음과 같은 결과를 확인할 수 있습니다.

----------- Video shots: 34 ------------
  1 |   0.000 |  12.880
  2 |  12.920 |  21.680
  3 |  21.720 |  27.880
...
 32 | 135.160 | 138.320
 33 | 138.360 | 146.200
 34 | 146.240 | 162.520

각 장면의 중간 프레임을 추출하여 프레임이 있는 벽에 배치하면 동영상에 대한 시각적 요약을 생성할 수 있습니다.

25bbffa59f7ed71d.png

요약

이 단계에서는 Video Intelligence API를 사용하여 동영상에서 장면 변화 감지를 수행할 수 있었습니다. 장면 변화 감지에 대해 자세히 알아보세요.

6. 라벨 감지

Video Intelligence API를 사용하여 동영상의 라벨을 감지할 수 있습니다. 라벨은 시각적 콘텐츠를 기반으로 동영상을 설명합니다.

다음 코드를 IPython 세션에 복사합니다.

from datetime import timedelta
from typing import Optional, Sequence, cast

from google.cloud import videointelligence_v1 as vi


def detect_labels(
    video_uri: str,
    mode: vi.LabelDetectionMode,
    segments: Optional[Sequence[vi.VideoSegment]] = None,
) -> vi.VideoAnnotationResults:
    video_client = vi.VideoIntelligenceServiceClient()
    features = [vi.Feature.LABEL_DETECTION]
    config = vi.LabelDetectionConfig(label_detection_mode=mode)
    context = vi.VideoContext(segments=segments, label_detection_config=config)
    request = vi.AnnotateVideoRequest(
        input_uri=video_uri,
        features=features,
        video_context=context,
    )

    print(f'Processing video "{video_uri}"...')
    operation = video_client.annotate_video(request)

    # Wait for operation to complete
    response = cast(vi.AnnotateVideoResponse, operation.result())
    # A single video is processed
    results = response.annotation_results[0]

    return results
    

잠시 코드를 살펴보고 LABEL_DETECTION 매개변수와 함께 annotate_video 클라이언트 라이브러리 메서드를 사용하여 동영상을 분석하고 라벨을 감지하는 방법을 알아보세요.

동영상의 처음 37초를 분석하는 함수를 호출합니다.

video_uri = "gs://cloud-samples-data/video/JaneGoodall.mp4"
mode = vi.LabelDetectionMode.SHOT_MODE
segment = vi.VideoSegment(
    start_time_offset=timedelta(seconds=0),
    end_time_offset=timedelta(seconds=37),
)

results = detect_labels(video_uri, mode, [segment])

동영상이 처리될 때까지 기다립니다.

Processing video: "gs://cloud-samples-data/video/JaneGoodall.mp4"...

이 함수를 추가하여 동영상 수준에서 라벨을 출력합니다.

def print_video_labels(results: vi.VideoAnnotationResults):
    labels = sorted_by_first_segment_confidence(results.segment_label_annotations)

    print(f" Video labels: {len(labels)} ".center(80, "-"))
    for label in labels:
        categories = category_entities_to_str(label.category_entities)
        for segment in label.segments:
            confidence = segment.confidence
            t1 = segment.segment.start_time_offset.total_seconds()
            t2 = segment.segment.end_time_offset.total_seconds()
            print(
                f"{confidence:4.0%}",
                f"{t1:7.3f}",
                f"{t2:7.3f}",
                f"{label.entity.description}{categories}",
                sep=" | ",
            )


def sorted_by_first_segment_confidence(
    labels: Sequence[vi.LabelAnnotation],
) -> Sequence[vi.LabelAnnotation]:
    def first_segment_confidence(label: vi.LabelAnnotation) -> float:
        return label.segments[0].confidence

    return sorted(labels, key=first_segment_confidence, reverse=True)


def category_entities_to_str(category_entities: Sequence[vi.Entity]) -> str:
    if not category_entities:
        return ""
    entities = ", ".join([e.description for e in category_entities])
    return f" ({entities})"
    

함수 호출

print_video_labels(results)

다음과 같은 결과를 확인할 수 있습니다.

------------------------------- Video labels: 10 -------------------------------
 96% |   0.000 |  36.960 | nature
 74% |   0.000 |  36.960 | vegetation
 59% |   0.000 |  36.960 | tree (plant)
 56% |   0.000 |  36.960 | forest (geographical feature)
 49% |   0.000 |  36.960 | leaf (plant)
 43% |   0.000 |  36.960 | flora (plant)
 38% |   0.000 |  36.960 | nature reserve (geographical feature)
 38% |   0.000 |  36.960 | woodland (forest)
 35% |   0.000 |  36.960 | water resources (water)
 32% |   0.000 |  36.960 | sunlight (light)

이러한 동영상 수준의 라벨 덕분에 동영상의 시작 부분이 주로 자연과 초목으로 이루어진다는 것을 알 수 있습니다.

이 함수를 추가하여 샷 수준에서 라벨을 출력합니다.

def print_shot_labels(results: vi.VideoAnnotationResults):
    labels = sorted_by_first_segment_start_and_confidence(
        results.shot_label_annotations
    )

    print(f" Shot labels: {len(labels)} ".center(80, "-"))
    for label in labels:
        categories = category_entities_to_str(label.category_entities)
        print(f"{label.entity.description}{categories}")
        for segment in label.segments:
            confidence = segment.confidence
            t1 = segment.segment.start_time_offset.total_seconds()
            t2 = segment.segment.end_time_offset.total_seconds()
            print(f"{confidence:4.0%} | {t1:7.3f} | {t2:7.3f}")


def sorted_by_first_segment_start_and_confidence(
    labels: Sequence[vi.LabelAnnotation],
) -> Sequence[vi.LabelAnnotation]:
    def first_segment_start_and_confidence(label: vi.LabelAnnotation):
        first_segment = label.segments[0]
        ms = first_segment.segment.start_time_offset.total_seconds()
        return (ms, -first_segment.confidence)

    return sorted(labels, key=first_segment_start_and_confidence)
    

함수 호출

print_shot_labels(results)

다음과 같은 결과를 확인할 수 있습니다.

------------------------------- Shot labels: 29 --------------------------------
planet (astronomical object)
 83% |   0.000 |  12.880
earth (planet)
 53% |   0.000 |  12.880
water resources (water)
 43% |   0.000 |  12.880
aerial photography (photography)
 43% |   0.000 |  12.880
vegetation
 32% |   0.000 |  12.880
 92% |  12.920 |  21.680
 83% |  21.720 |  27.880
 77% |  27.920 |  31.800
 76% |  31.840 |  34.720
...
butterfly (insect, animal)
 84% |  34.760 |  36.960
...

이러한 장면 수준의 라벨 덕분에 동영상이 지구와 같은 행성의 장면으로 시작하며 34.760-36.960s 장면에 나비가 있다는 것을 알 수 있습니다.

요약

이 단계에서는 Video Intelligence API를 사용하여 동영상에 대한 라벨 인식을 수행할 수 있었습니다. 라벨 감지에 대해 자세히 알아보세요.

7. 유해성 콘텐츠 감지

Video Intelligence API를 사용하여 동영상의 유해성 콘텐츠를 감지할 수 있습니다. 음란물이란 일반적으로 18세 미만의 청소년에게 부적절한 성인용 콘텐츠로서 과도한 노출, 성행위, 포르노 등을 포함하되 이에 국한되지 않습니다. 감지는 프레임별 시각적 신호만을 기준으로 수행됩니다 (오디오는 사용되지 않음). 응답에는 VERY_UNLIKELY~VERY_LIKELY 범위의 가능성 값이 포함됩니다.

다음 코드를 IPython 세션에 복사합니다.

from datetime import timedelta
from typing import Optional, Sequence, cast

from google.cloud import videointelligence_v1 as vi


def detect_explicit_content(
    video_uri: str,
    segments: Optional[Sequence[vi.VideoSegment]] = None,
) -> vi.VideoAnnotationResults:
    video_client = vi.VideoIntelligenceServiceClient()
    features = [vi.Feature.EXPLICIT_CONTENT_DETECTION]
    context = vi.VideoContext(segments=segments)
    request = vi.AnnotateVideoRequest(
        input_uri=video_uri,
        features=features,
        video_context=context,
    )

    print(f'Processing video "{video_uri}"...')
    operation = video_client.annotate_video(request)

    # Wait for operation to complete
    response = cast(vi.AnnotateVideoResponse, operation.result())
    # A single video is processed
    results = response.annotation_results[0]

    return results
    

잠시 코드를 살펴보고 EXPLICIT_CONTENT_DETECTION 매개변수와 함께 annotate_video 클라이언트 라이브러리 메서드를 사용하여 동영상을 분석하고 선정적인 콘텐츠를 감지하는 방법을 살펴보세요.

동영상의 처음 10초를 분석하는 함수를 호출합니다.

video_uri = "gs://cloud-samples-data/video/JaneGoodall.mp4"
segment = vi.VideoSegment(
    start_time_offset=timedelta(seconds=0),
    end_time_offset=timedelta(seconds=10),
)

results = detect_explicit_content(video_uri, [segment])

동영상이 처리될 때까지 기다립니다.

Processing video: "gs://cloud-samples-data/video/JaneGoodall.mp4"...

이 함수를 추가하여 다양한 가능성 수를 출력합니다.

def print_explicit_content(results: vi.VideoAnnotationResults):
    from collections import Counter

    frames = results.explicit_annotation.frames
    likelihood_counts = Counter([f.pornography_likelihood for f in frames])

    print(f" Explicit content frames: {len(frames)} ".center(40, "-"))
    for likelihood in vi.Likelihood:
        print(f"{likelihood.name:<22}: {likelihood_counts[likelihood]:>3}")
        

함수 호출

print_explicit_content(results)

다음과 같은 결과를 확인할 수 있습니다.

----- Explicit content frames: 10 ------
LIKELIHOOD_UNSPECIFIED:   0
VERY_UNLIKELY         :  10
UNLIKELY              :   0
POSSIBLE              :   0
LIKELY                :   0
VERY_LIKELY           :   0

이 함수를 추가하여 프레임 세부정보를 출력합니다.

def print_frames(results: vi.VideoAnnotationResults, likelihood: vi.Likelihood):
    frames = results.explicit_annotation.frames
    frames = [f for f in frames if f.pornography_likelihood == likelihood]

    print(f" {likelihood.name} frames: {len(frames)} ".center(40, "-"))
    for frame in frames:
        print(frame.time_offset)
        

함수 호출

print_frames(results, vi.Likelihood.VERY_UNLIKELY)

다음과 같은 결과를 확인할 수 있습니다.

------- VERY_UNLIKELY frames: 10 -------
0:00:00.365992
0:00:01.279206
0:00:02.268336
0:00:03.289253
0:00:04.400163
0:00:05.291547
0:00:06.449558
0:00:07.452751
0:00:08.577405
0:00:09.554514

요약

이 단계에서는 Video Intelligence API를 사용하여 동영상에서 유해성 콘텐츠 감지를 수행할 수 있었습니다. 선정적인 콘텐츠 감지에 대해 자세히 알아보세요.

8. 음성 스크립트 작성

Video Intelligence API를 사용하여 동영상 음성을 텍스트로 변환할 수 있습니다.

다음 코드를 IPython 세션에 복사합니다.

from datetime import timedelta
from typing import Optional, Sequence, cast

from google.cloud import videointelligence_v1 as vi


def transcribe_speech(
    video_uri: str,
    language_code: str,
    segments: Optional[Sequence[vi.VideoSegment]] = None,
) -> vi.VideoAnnotationResults:
    video_client = vi.VideoIntelligenceServiceClient()
    features = [vi.Feature.SPEECH_TRANSCRIPTION]
    config = vi.SpeechTranscriptionConfig(
        language_code=language_code,
        enable_automatic_punctuation=True,
    )
    context = vi.VideoContext(
        segments=segments,
        speech_transcription_config=config,
    )
    request = vi.AnnotateVideoRequest(
        input_uri=video_uri,
        features=features,
        video_context=context,
    )

    print(f'Processing video "{video_uri}"...')
    operation = video_client.annotate_video(request)

    # Wait for operation to complete
    response = cast(vi.AnnotateVideoResponse, operation.result())
    # A single video is processed
    results = response.annotation_results[0]

    return results
    

잠시 코드를 살펴보고 annotate_video 클라이언트 라이브러리 메서드와 SPEECH_TRANSCRIPTION 매개변수를 사용하여 동영상을 분석하고 음성을 텍스트로 변환하는 방법을 알아보세요.

55~80초에서 동영상을 분석하는 함수를 호출합니다.

video_uri = "gs://cloud-samples-data/video/JaneGoodall.mp4"
language_code = "en-GB"
segment = vi.VideoSegment(
    start_time_offset=timedelta(seconds=55),
    end_time_offset=timedelta(seconds=80),
)

results = transcribe_speech(video_uri, language_code, [segment])

동영상이 처리될 때까지 기다립니다.

Processing video: "gs://cloud-samples-data/video/JaneGoodall.mp4"...

이 함수를 추가하여 스크립트가 작성된 음성을 출력합니다.

def print_video_speech(results: vi.VideoAnnotationResults, min_confidence: float = 0.8):
    def keep_transcription(transcription: vi.SpeechTranscription) -> bool:
        return min_confidence <= transcription.alternatives[0].confidence

    transcriptions = results.speech_transcriptions
    transcriptions = [t for t in transcriptions if keep_transcription(t)]

    print(f" Speech transcriptions: {len(transcriptions)} ".center(80, "-"))
    for transcription in transcriptions:
        first_alternative = transcription.alternatives[0]
        confidence = first_alternative.confidence
        transcript = first_alternative.transcript
        print(f" {confidence:4.0%} | {transcript.strip()}")
        

함수 호출

print_video_speech(results)

다음과 같은 결과를 확인할 수 있습니다.

--------------------------- Speech transcriptions: 2 ---------------------------
  91% | I was keenly aware of secret movements in the trees.
  92% | I looked into his large and lustrous eyes. They seem somehow to express his entire personality.

이 함수를 추가하여 감지된 단어와 타임스탬프의 목록을 출력합니다.

def print_word_timestamps(
    results: vi.VideoAnnotationResults,
    min_confidence: float = 0.8,
):
    def keep_transcription(transcription: vi.SpeechTranscription) -> bool:
        return min_confidence <= transcription.alternatives[0].confidence

    transcriptions = results.speech_transcriptions
    transcriptions = [t for t in transcriptions if keep_transcription(t)]

    print(" Word timestamps ".center(80, "-"))
    for transcription in transcriptions:
        first_alternative = transcription.alternatives[0]
        confidence = first_alternative.confidence
        for word in first_alternative.words:
            t1 = word.start_time.total_seconds()
            t2 = word.end_time.total_seconds()
            word = word.word
            print(f"{confidence:4.0%} | {t1:7.3f} | {t2:7.3f} | {word}")
            

함수 호출

print_word_timestamps(results)

다음과 같은 결과를 확인할 수 있습니다.

------------------------------- Word timestamps --------------------------------
 93% |  55.000 |  55.700 | I
 93% |  55.700 |  55.900 | was
 93% |  55.900 |  56.300 | keenly
 93% |  56.300 |  56.700 | aware
 93% |  56.700 |  56.900 | of
...
 94% |  76.900 |  77.400 | express
 94% |  77.400 |  77.600 | his
 94% |  77.600 |  78.200 | entire
 94% |  78.200 |  78.500 | personality.

요약

이 단계에서는 Video Intelligence API를 사용하여 동영상에서 음성 스크립트를 작성할 수 있었습니다. 오디오 스크립트 작성에 대해 자세히 알아보세요.

9. 텍스트 감지 및 추적

Video Intelligence API를 사용하여 동영상의 텍스트를 감지하고 추적할 수 있습니다.

다음 코드를 IPython 세션에 복사합니다.

from datetime import timedelta
from typing import Optional, Sequence, cast

from google.cloud import videointelligence_v1 as vi


def detect_text(
    video_uri: str,
    language_hints: Optional[Sequence[str]] = None,
    segments: Optional[Sequence[vi.VideoSegment]] = None,
) -> vi.VideoAnnotationResults:
    video_client = vi.VideoIntelligenceServiceClient()
    features = [vi.Feature.TEXT_DETECTION]
    config = vi.TextDetectionConfig(
        language_hints=language_hints,
    )
    context = vi.VideoContext(
        segments=segments,
        text_detection_config=config,
    )
    request = vi.AnnotateVideoRequest(
        input_uri=video_uri,
        features=features,
        video_context=context,
    )

    print(f'Processing video "{video_uri}"...')
    operation = video_client.annotate_video(request)

    # Wait for operation to complete
    response = cast(vi.AnnotateVideoResponse, operation.result())
    # A single video is processed
    results = response.annotation_results[0]

    return results
    

잠시 코드를 살펴보고 TEXT_DETECTION 매개변수와 함께 annotate_video 클라이언트 라이브러리 메서드를 사용하여 동영상을 분석하고 텍스트를 감지하는 방법을 알아보세요.

13~27초 사이의 동영상을 분석하는 함수를 호출합니다.

video_uri = "gs://cloud-samples-data/video/JaneGoodall.mp4"
segment = vi.VideoSegment(
    start_time_offset=timedelta(seconds=13),
    end_time_offset=timedelta(seconds=27),
)

results = detect_text(video_uri, segments=[segment])

동영상이 처리될 때까지 기다립니다.

Processing video: "gs://cloud-samples-data/video/JaneGoodall.mp4"...

이 함수를 추가하여 감지된 텍스트를 출력합니다.

def print_video_text(results: vi.VideoAnnotationResults, min_frames: int = 15):
    annotations = sorted_by_first_segment_end(results.text_annotations)

    print(" Detected text ".center(80, "-"))
    for annotation in annotations:
        for text_segment in annotation.segments:
            frames = len(text_segment.frames)
            if frames < min_frames:
                continue
            text = annotation.text
            confidence = text_segment.confidence
            start = text_segment.segment.start_time_offset
            seconds = segment_seconds(text_segment.segment)
            print(text)
            print(f"  {confidence:4.0%} | {start} + {seconds:.1f}s | {frames} fr.")


def sorted_by_first_segment_end(
    annotations: Sequence[vi.TextAnnotation],
) -> Sequence[vi.TextAnnotation]:
    def first_segment_end(annotation: vi.TextAnnotation) -> int:
        return annotation.segments[0].segment.end_time_offset.total_seconds()

    return sorted(annotations, key=first_segment_end)


def segment_seconds(segment: vi.VideoSegment) -> float:
    t1 = segment.start_time_offset.total_seconds()
    t2 = segment.end_time_offset.total_seconds()
    return t2 - t1
    

함수 호출

print_video_text(results)

다음과 같은 결과를 확인할 수 있습니다.

-------------------------------- Detected text ---------------------------------
GOMBE NATIONAL PARK
   99% | 0:00:15.760000 + 1.7s | 15 fr.
TANZANIA
  100% | 0:00:15.760000 + 4.8s | 39 fr.
With words and narration by
  100% | 0:00:23.200000 + 3.6s | 31 fr.
Jane Goodall
   99% | 0:00:23.080000 + 3.8s | 33 fr.

이 함수를 추가하여 감지된 텍스트 프레임 및 경계 상자의 목록을 출력합니다.

def print_text_frames(results: vi.VideoAnnotationResults, contained_text: str):
    # Vertex order: top-left, top-right, bottom-right, bottom-left
    def box_top_left(box: vi.NormalizedBoundingPoly) -> str:
        tl = box.vertices[0]
        return f"({tl.x:.5f}, {tl.y:.5f})"

    def box_bottom_right(box: vi.NormalizedBoundingPoly) -> str:
        br = box.vertices[2]
        return f"({br.x:.5f}, {br.y:.5f})"

    annotations = results.text_annotations
    annotations = [a for a in annotations if contained_text in a.text]
    for annotation in annotations:
        print(f" {annotation.text} ".center(80, "-"))
        for text_segment in annotation.segments:
            for frame in text_segment.frames:
                frame_ms = frame.time_offset.total_seconds()
                box = frame.rotated_bounding_box
                print(
                    f"{frame_ms:>7.3f}",
                    box_top_left(box),
                    box_bottom_right(box),
                    sep=" | ",
                )
                

함수를 호출하여 내레이터 이름을 표시하는 프레임을 확인합니다.

contained_text = "Goodall"
print_text_frames(results, contained_text)

다음과 같은 결과를 확인할 수 있습니다.

--------------------------------- Jane Goodall ---------------------------------
 23.080 | (0.39922, 0.49861) | (0.62752, 0.55888)
 23.200 | (0.38750, 0.49028) | (0.62692, 0.56306)
...
 26.800 | (0.36016, 0.49583) | (0.61094, 0.56048)
 26.920 | (0.45859, 0.49583) | (0.60365, 0.56174)

상응하는 프레임 위에 경계 상자를 그리면 다음과 같은 결과가 나타납니다.

7e530d3d25f2f40e.gif

요약

이 단계에서는 Video Intelligence API를 사용하여 동영상에서 텍스트 감지 및 추적을 수행할 수 있었습니다. 텍스트 감지 및 추적에 대해 자세히 알아보세요.

10. 객체 감지 및 추적

Video Intelligence API를 사용하여 동영상의 객체를 감지하고 추적할 수 있습니다.

다음 코드를 IPython 세션에 복사합니다.

from datetime import timedelta
from typing import Optional, Sequence, cast

from google.cloud import videointelligence_v1 as vi


def track_objects(
    video_uri: str, segments: Optional[Sequence[vi.VideoSegment]] = None
) -> vi.VideoAnnotationResults:
    video_client = vi.VideoIntelligenceServiceClient()
    features = [vi.Feature.OBJECT_TRACKING]
    context = vi.VideoContext(segments=segments)
    request = vi.AnnotateVideoRequest(
        input_uri=video_uri,
        features=features,
        video_context=context,
    )

    print(f'Processing video "{video_uri}"...')
    operation = video_client.annotate_video(request)

    # Wait for operation to complete
    response = cast(vi.AnnotateVideoResponse, operation.result())
    # A single video is processed
    results = response.annotation_results[0]

    return results
    

잠시 코드를 살펴보고 annotate_video 클라이언트 라이브러리 메서드와 OBJECT_TRACKING 매개변수를 사용하여 동영상을 분석하고 객체를 감지하는 방법을 알아보세요.

98~112초 사이의 동영상을 분석하는 함수를 호출합니다.

video_uri = "gs://cloud-samples-data/video/JaneGoodall.mp4"
segment = vi.VideoSegment(
    start_time_offset=timedelta(seconds=98),
    end_time_offset=timedelta(seconds=112),
)

results = track_objects(video_uri, [segment])

동영상이 처리될 때까지 기다립니다.

Processing video: "gs://cloud-samples-data/video/JaneGoodall.mp4"...

이 함수를 추가하여 감지된 객체의 목록을 출력합니다.

def print_detected_objects(
    results: vi.VideoAnnotationResults,
    min_confidence: float = 0.7,
):
    annotations = results.object_annotations
    annotations = [a for a in annotations if min_confidence <= a.confidence]

    print(
        f" Detected objects: {len(annotations)}"
        f" ({min_confidence:.0%} <= confidence) ".center(80, "-")
    )
    for annotation in annotations:
        entity = annotation.entity
        description = entity.description
        entity_id = entity.entity_id
        confidence = annotation.confidence
        t1 = annotation.segment.start_time_offset.total_seconds()
        t2 = annotation.segment.end_time_offset.total_seconds()
        frames = len(annotation.frames)
        print(
            f"{description:<22}",
            f"{entity_id:<10}",
            f"{confidence:4.0%}",
            f"{t1:>7.3f}",
            f"{t2:>7.3f}",
            f"{frames:>2} fr.",
            sep=" | ",
        )
        

함수 호출

print_detected_objects(results)

다음과 같은 결과를 확인할 수 있습니다.

------------------- Detected objects: 3 (70% <= confidence) --------------------
insect                 | /m/03vt0   |  87% |  98.840 | 101.720 | 25 fr.
insect                 | /m/03vt0   |  71% | 108.440 | 111.080 | 23 fr.
butterfly              | /m/0cyf8   |  91% | 111.200 | 111.920 |  7 fr.

이 함수를 추가하여 감지된 객체 프레임 및 경계 상자의 목록을 출력합니다.

def print_object_frames(
    results: vi.VideoAnnotationResults,
    entity_id: str,
    min_confidence: float = 0.7,
):
    def keep_annotation(annotation: vi.ObjectTrackingAnnotation) -> bool:
        return (
            annotation.entity.entity_id == entity_id
            and min_confidence <= annotation.confidence
        )

    annotations = results.object_annotations
    annotations = [a for a in annotations if keep_annotation(a)]
    for annotation in annotations:
        description = annotation.entity.description
        confidence = annotation.confidence
        print(
            f" {description},"
            f" confidence: {confidence:.0%},"
            f" frames: {len(annotation.frames)} ".center(80, "-")
        )
        for frame in annotation.frames:
            t = frame.time_offset.total_seconds()
            box = frame.normalized_bounding_box
            print(
                f"{t:>7.3f}",
                f"({box.left:.5f}, {box.top:.5f})",
                f"({box.right:.5f}, {box.bottom:.5f})",
                sep=" | ",
            )
            

곤충의 항목 ID로 함수를 호출합니다.

insect_entity_id = "/m/03vt0"
print_object_frames(results, insect_entity_id)

다음과 같은 결과를 확인할 수 있습니다.

--------------------- insect, confidence: 87%, frames: 25 ----------------------
 98.840 | (0.49327, 0.19617) | (0.69905, 0.69633)
 98.960 | (0.49559, 0.19308) | (0.70631, 0.69671)
...
101.600 | (0.46668, 0.19776) | (0.76619, 0.69371)
101.720 | (0.46805, 0.20053) | (0.76447, 0.68703)
--------------------- insect, confidence: 71%, frames: 23 ----------------------
108.440 | (0.47343, 0.10694) | (0.63821, 0.98332)
108.560 | (0.46960, 0.10206) | (0.63033, 0.98285)
...
110.960 | (0.49466, 0.05102) | (0.65941, 0.99357)
111.080 | (0.49572, 0.04728) | (0.65762, 0.99868)

상응하는 프레임 위에 경계 상자를 그리면 다음과 같은 결과가 나타납니다.

8f5796f6e73d1a46.gif

c195a2dca4573f95.gif

요약

이 단계에서는 Video Intelligence API를 사용하여 동영상에서 객체 감지 및 추적을 수행할 수 있었습니다. 객체 감지 및 추적에 대해 자세히 알아보세요.

11. 로고 감지 및 추적

Video Intelligence API를 사용하여 동영상의 로고를 감지하고 추적할 수 있습니다. 100,000개가 넘는 브랜드와 로고를 감지할 수 있습니다.

다음 코드를 IPython 세션에 복사합니다.

from datetime import timedelta
from typing import Optional, Sequence, cast

from google.cloud import videointelligence_v1 as vi


def detect_logos(
    video_uri: str, segments: Optional[Sequence[vi.VideoSegment]] = None
) -> vi.VideoAnnotationResults:
    video_client = vi.VideoIntelligenceServiceClient()
    features = [vi.Feature.LOGO_RECOGNITION]
    context = vi.VideoContext(segments=segments)
    request = vi.AnnotateVideoRequest(
        input_uri=video_uri,
        features=features,
        video_context=context,
    )

    print(f'Processing video "{video_uri}"...')
    operation = video_client.annotate_video(request)

    # Wait for operation to complete
    response = cast(vi.AnnotateVideoResponse, operation.result())
    # A single video is processed
    results = response.annotation_results[0]

    return results
    

잠시 코드를 살펴보고 annotate_video 클라이언트 라이브러리 메서드와 LOGO_RECOGNITION 매개변수를 사용하여 동영상을 분석하고 로고를 감지하는 방법을 살펴보세요.

동영상의 끝에서 마지막 시퀀스를 분석하는 함수를 호출합니다.

video_uri = "gs://cloud-samples-data/video/JaneGoodall.mp4"
segment = vi.VideoSegment(
    start_time_offset=timedelta(seconds=146),
    end_time_offset=timedelta(seconds=156),
)

results = detect_logos(video_uri, [segment])

동영상이 처리될 때까지 기다립니다.

Processing video: "gs://cloud-samples-data/video/JaneGoodall.mp4"...

이 함수를 추가하여 감지된 로고의 목록을 출력합니다.

def print_detected_logos(results: vi.VideoAnnotationResults):
    annotations = results.logo_recognition_annotations

    print(f" Detected logos: {len(annotations)} ".center(80, "-"))
    for annotation in annotations:
        entity = annotation.entity
        entity_id = entity.entity_id
        description = entity.description
        for track in annotation.tracks:
            confidence = track.confidence
            t1 = track.segment.start_time_offset.total_seconds()
            t2 = track.segment.end_time_offset.total_seconds()
            logo_frames = len(track.timestamped_objects)
            print(
                f"{confidence:4.0%}",
                f"{t1:>7.3f}",
                f"{t2:>7.3f}",
                f"{logo_frames:>3} fr.",
                f"{entity_id:<15}",
                f"{description}",
                sep=" | ",
            )
            

함수 호출

print_detected_logos(results)

다음과 같은 결과를 확인할 수 있습니다.

------------------------------ Detected logos: 1 -------------------------------
 92% | 150.680 | 155.720 |  43 fr. | /m/055t58       | Google Maps

이 함수를 추가하여 감지된 로고 프레임 및 경계 상자의 목록을 출력합니다.

def print_logo_frames(results: vi.VideoAnnotationResults, entity_id: str):
    def keep_annotation(annotation: vi.LogoRecognitionAnnotation) -> bool:
        return annotation.entity.entity_id == entity_id

    annotations = results.logo_recognition_annotations
    annotations = [a for a in annotations if keep_annotation(a)]
    for annotation in annotations:
        description = annotation.entity.description
        for track in annotation.tracks:
            confidence = track.confidence
            print(
                f" {description},"
                f" confidence: {confidence:.0%},"
                f" frames: {len(track.timestamped_objects)} ".center(80, "-")
            )
            for timestamped_object in track.timestamped_objects:
                t = timestamped_object.time_offset.total_seconds()
                box = timestamped_object.normalized_bounding_box
                print(
                    f"{t:>7.3f}",
                    f"({box.left:.5f}, {box.top:.5f})",
                    f"({box.right:.5f}, {box.bottom:.5f})",
                    sep=" | ",
                )
                

Google 지도 로고 항목 ID로 함수를 호출합니다.

maps_entity_id = "/m/055t58"
print_logo_frames(results, maps_entity_id)

다음과 같은 결과를 확인할 수 있습니다.

------------------- Google Maps, confidence: 92%, frames: 43 -------------------
150.680 | (0.42024, 0.28633) | (0.58192, 0.64220)
150.800 | (0.41713, 0.27822) | (0.58318, 0.63556)
...
155.600 | (0.41775, 0.27701) | (0.58372, 0.63986)
155.720 | (0.41688, 0.28005) | (0.58335, 0.63954)

상응하는 프레임 위에 경계 상자를 그리면 다음과 같은 결과가 나타납니다.

554743aff6d8824c.gif

요약

이 단계에서는 Video Intelligence API를 사용하여 동영상에서 로고 감지 및 추적을 수행할 수 있었습니다. 로고 감지 및 추적에 대해 자세히 알아보세요.

12. 여러 특성 감지

다음은 모든 통계를 한 번에 얻을 수 있는 요청의 유형입니다.

from google.cloud import videointelligence_v1 as vi

video_client = vi.VideoIntelligenceServiceClient()
video_uri = "gs://..."
features = [
    vi.Feature.SHOT_CHANGE_DETECTION,
    vi.Feature.LABEL_DETECTION,
    vi.Feature.EXPLICIT_CONTENT_DETECTION,
    vi.Feature.SPEECH_TRANSCRIPTION,
    vi.Feature.TEXT_DETECTION,
    vi.Feature.OBJECT_TRACKING,
    vi.Feature.LOGO_RECOGNITION,
    vi.Feature.FACE_DETECTION,  # NEW
    vi.Feature.PERSON_DETECTION,  # NEW
]
context = vi.VideoContext(
    segments=...,
    shot_change_detection_config=...,
    label_detection_config=...,
    explicit_content_detection_config=...,
    speech_transcription_config=...,
    text_detection_config=...,
    object_tracking_config=...,
    face_detection_config=...,  # NEW
    person_detection_config=...,  # NEW
)
request = vi.AnnotateVideoRequest(
    input_uri=video_uri,
    features=features,
    video_context=context,
)

# video_client.annotate_video(request)

13. 축하합니다.

cfaa6ffa7bc5ca70.png

Python을 사용하여 Video Intelligence API를 사용하는 방법을 알아보았습니다.

삭제

Cloud Shell에서 개발 환경을 삭제하려면 다음 안내를 따르세요.

  • 아직 IPython 세션에 있다면 셸로 돌아갑니다. exit
  • Python 가상 환경 사용 중지: deactivate
  • 가상 환경 폴더 삭제: cd ~ ; rm -rf ./venv-videointel

Google Cloud 프로젝트를 삭제하려면 Cloud Shell에서 다음 안내를 따르세요.

  • 현재 프로젝트 ID를 가져옵니다. PROJECT_ID=$(gcloud config get-value core/project)
  • 삭제하려는 프로젝트가 맞는지 확인합니다. echo $PROJECT_ID
  • 프로젝트 삭제: gcloud projects delete $PROJECT_ID

자세히 알아보기

라이선스

이 작업물은 Creative Commons Attribution 2.0 일반 라이선스에 따라 사용이 허가되었습니다.