Представьте ситуацию: вы только что вернулись из (заслуженного) отпуска с друзьями и, конечно, сделали множество фотографий. Вы хотите отправить друзьям фото с ними. Но как сделать это эффективно? Можно просмотреть фотографии вручную и отметить каждого друга отдельно. Но вы только вернулись из отпуска, ваш электронный почтовый ящик переполнен, и на просмотр фото совсем нет времени. Как же быть?

Обзор

В этом подразделе мы создадим небольшое приложение, которое автоматически тегирует людей на изображениях. Пользователи предоставляют три элемента входных данных:

  • путь к каталогу, содержащему фотографии, к которым нужно добавить теги;

  • путь к каталогу, содержащему фото людей, которых нужно найти;

  • путь к каталогу, куда будут сохраняться размеченные фотографии.

Для простоты мы будем использовать в качестве тегов имена файлов. Мы предполагаем, что фотографии с человеком, которого нужно найти, названы по имени этого человека. Например, в нашем каталоге с фотографиями людей, которых нужно найти, есть изображения с именами Joe.png и Jane.png. Чтобы тегировать нужное изображение, мы просто изменим имя файла, добавив в его начало имена людей, которые запечатлены на нем.

Предположим, у нас есть изображение beach.png, на котором есть и Джо, и Джейн. Тогда в выходном каталоге мы создадим два файла с именами Joebeach.png и Janebeach.png, показывая, что обе они есть на фотографии пляжа. Если нам нужно отправить все фотографии, на которых изображен один и тот же человек, например Джо, то можно найти все файлы, имя которых удовлетворяет регулярному выражению Joe*.png (где * обозначает произвольные строковые значения).

Сначала на этапе внутренней обработки требуется загрузить, во-первых, изображения людей, которых нужно найти, и, во-вторых, фото, которые нужно тегировать. Для каждого человека, которого мы ищем, и для каждой фотографии, которую нужно тегировать, будем перебирать все возможные пары. Например, если мы ищем пять человек и у нас есть десять фото для тегирования, то придется рассмотреть 50 пар. Для каждой из этих пар мы используем языковую модель GPT-4o, чтобы решить, запечатлен ли соответствующий человек на тегируемой фотографии.

Для этого понадобятся мультимодальные промпты, содержащие текст и два изображения. Первое — фото человека, которого нужно найти, а второе — фото, на котором его нужно отметить. С помощью текстового промпта можно дать языковой модели указание сравнить изображения и определить, есть ли этот человек на фотографии. При каждом совпадении — то есть сочетании человека и фото, на котором он есть, — мы будем копировать соответствующую фотографию в выходную папку, добавляя перед ее названием имя человека.

На рис. 6.2 показан пример промпта. Слева — фотография Джейн, одной из тех, кого мы ищем. Справа — фото для тегирования. В текстовой инструкции содержится указание для языковой модели сравнить две фотографии и дать ответ «Да», если на них изображен один и тот же человек (или «Нет», если люди разные). В данном случае на фотографиях изображены разные люди и правильный ответ должен быть «Нет».

Рис. 6.2. Мультимодальный промпт, содержащий два изображения и текст: языковая модель получает указание проверить, изображен ли на обеих фотографиях один и тот же человек (ожидаемый ответ: «Да») или нет (ожидаемый ответ: «Нет»)
Рис. 6.2. Мультимодальный промпт, содержащий два изображения и текст: языковая модель получает указание проверить, изображен ли на обеих фотографиях один и тот же человек (ожидаемый ответ: «Да») или нет (ожидаемый ответ: «Нет»)

Кодирование локально хранящихся изображений

В разделе 6.2 мы с помощью языковой модели GPT-4o анализировали интернет-изображения. Теперь речь идет о наших личных отпускных фотографиях. Допустим, нам не хочется выкладывать их в сеть и делать общедоступными. Каким образом передать их только GPT-4o?

Скорее всего, придется преобразовать изображения в формат, подходящий для GPT-4o. Языковая модель GPT-4o поддерживает широкий спектр форматов изображений, таких как PNG, JPEG, WEBP и GIF. Для любого формата размер файла изображения в данный момент ограничен 20 Мбайт. Чтобы загрузить изображения поддерживаемых типов в GPT-4o, сначала нужно закодировать их с помощью кодировки base64.

Что такое кодирование base64

Кодирование base64 — это способ представления двоичных данных в виде текстовой строки, состоящей из печатаемых символов. Как следует из названия base64, используемый для строки алфавит основан на 64 символах. Это означает, что каждый символ можно представить с помощью шести бит (поскольку они позволяют представить 26 = 64 возможных символа). Учитывая, что хранение данных в компьютерах осуществляется на уровне байтов (по восемь бит), кодирование целесообразно производить группами из трех байт (24 бита). Используя кодировку base64, три байта можно использовать для представления четырех символов (поскольку 24 / 6 = 4).

В Python кодирование двоичных данных в формат base64 выполняется с помощью модуля base64. Приведенный ниже код открывает файл изображения, путь к которому указан в переменной image_path, и кодирует его с использованием формата base64:

with open(image_path, 'rb') as image_file:
    encoded = base64.b64encode(image_file.read())

Мы преобразовали двоичные данные изображения в строку в формате base64. Прежде чем отправлять такие изображения языковой модели GPT-4o, необходимо выполнить еще одно (последнее) преобразование: представить строку в кодировке UTF-8.

Что такое кодировка UTF-8

UTF-8 — это способ представления строковых данных. Он чрезвычайно популярен и используется примерно на 98 % сайтов. UTF-8 поддерживает более миллиона символов из самых разных языков. Все эти символы можно представить с помощью фиксированного количества байтов: четыре байта для представления каждого символа. Однако это неэффективно, ведь не учитывается тот факт, что некоторые символы встречаются гораздо чаще других. Если закодировать распространенные символы меньшим количеством байтов, оставив представления с большим количеством байтов для более редких, то можно представить тот же текст, используя меньше байтов. Именно это и делает UTF-8, а поскольку для представления разных символов может потребоваться разное количество байтов, то UTF-8 также называют стандартом переменной длины. При этом он был разработан так, чтобы сохранять обратную совместимость со старым стандартом ASCII: первые 128 символов кодируются в нем точно так же.

Преобразовать нашу строку base64, кодирующую изображение, в формат UTF-8 в Python можно с помощью функции decode. При условии, что изображение все еще закодировано в переменной encoded, можно сделать это с помощью следующего кода:

image = encoded.decode('utf-8')

Полученное изображение, закодированное как текстовая строка UTF-8, подходит в качестве входных данных для языковой модели GPT-4o. Далее мы рассмотрим, как загрузить изображения в этом формате на платформу OpenAI. После их загрузки ссылки на эти изображения можно будет добавить в наши запросы. Изображения обычно указываются как компоненты запроса:

{'type':'image_url', 'image_url':{'url':image_url}}

Здесь image_url обозначает URL, ведущий к изображению, которое необходимо проанализировать. Ранее мы использовали для этого общедоступные URL. Теперь мы анализируем личные изображения, которые отправим компании OpenAI, чтобы использовать их только для обработки конкретных запросов. С учетом того, что элемент image по-прежнему представляет изображение, которое закодировано как строка, URL изображения можно задать так:

image_url = {'url':f'data:image/png;base64,{image}'}

В этом коде предполагается, что изображение хранится в формате PNG (если нет, то замените значение png на соответствующий идентификатор формата, например jpeg). URL объединяет метаданные об изображении (такие как тип изображения и кодировка) с суффиксом строки, представляющим саму фотографию.

Передача изображений из локального хранилища в OpenAI

Воспользуемся этим проектом как возможностью увидеть альтернативный способ взаимодействия с моделями GPT. Такой подход даст представление о внутренних механизмах работы библиотеки Python от OpenAI. До сих пор мы использовали обертки Python, которые в фоновом режиме отправляли запросы на платформу OpenAI. Для отправки своих локальных изображений языковой модели GPT-4o мы сами создадим такие запросы.

Мы используем библиотеку requests Python для создания HTTP-запросов, отправляем промпты (с текстом и изображениями) в GPT-4o и получаем ответ. Точнее говоря, мы будем отправлять HTTP-запросы методом POST. Это тип запроса, принимаемый платформой OpenAI. Такие запросы можно отправить с помощью метода requests.post.

Запросы будут содержать всю информацию, которая нужна GPT-4o для решения интересующей нас задачи (в данном случае проверки, изображен ли на двух фотографиях один и тот же человек). Прежде всего в запрос необходимо добавить заголовки (headers). Мы будем использовать следующие заголовки:

headers = {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer ...'
}

Как видите, они заданы как словарь Python. Для нашего сценария использования требуется сохранить только два свойства: тип данных (мы планируем отправлять контент в формате JSON) и учетные данные доступа (три точки обозначают наш ключ доступа OpenAI).

Далее необходимо указать полезную нагрузку, то есть содержимое, которое следует отправить с помощью запроса в первую очередь:

payload = {
    'model': 'gpt-4o',                    1 Указание модели
    'messages': [
        {'role': 'user', 'content': ...}  2 Первое сообщение
        ],
    'max_tokens':1                        3 Длина вывода
}

Обратите внимание: полезная нагрузка содержит именно те поля, которые мы обычно указывали в вызовах метода completions.create. И это не случайно, поскольку второй метод внутри себя создает запросы с аналогичной полезной нагрузкой. Сначала полезная нагрузка указывает модель 1: gpt-4o (чтобы иметь возможность обрабатывать мультимодальные входящие промпты). Мы указываем список сообщений, в котором всего одна запись 2. Сообщение помечено как исходящее от пользователя ('role':'user'), а его содержимое, обозначенное тремя точками, будет содержать текстовые инструкции и изображения. Наконец, мы ограничиваем длину ответа одним токеном ('max_tokens':1) 3. Это логично, ведь нам нужен бинарный результат: один и тот же человек либо есть на нескольких входных изображениях (ожидаемый ответ: «Да»), либо нет (ожидаемый ответ: «Нет»).

Сформировав заголовки и полезную нагрузку, можно вызвать модель GPT-4o с помощью такого кода:

response = requests.post(
    'https://api.openai.com/v1/chat/completions',
    headers=headers, json=payload)

Первым параметром в вызове метода requests.post указан URL, на который нужно отправить запрос. В данном случае адрес https://api.openai.com/v1/chat/completions показывает, что требуется выполнить задачу типа Completion, используя одну из чат-моделей OpenAI (к ним относится и GPT-4o). Мы используем созданные ранее заголовки и полезную нагрузку.

В ответе содержится объект с результатом работы GPT-4o. Получить доступ к ответу (в котором говорится, изображен ли на двух фотографиях один и тот же человек) можно с помощью такого фрагмента кода:

response.json()['choices'][0]['message']['content']

Полный рабочий код для реализации

Теперь мы готовы обсудить полную реализацию от начала до конца! В листинге 6.2 представлен код, позволяющий отмечать людей на фотографиях.

Листинг 6.2. Тегирование людей на фотографиях из локального хранилища

import argparse
import base64
import os
import requests
import shutil

def load_images(in_dir):                 1 Загружает изображения с диска
    """Загружает изображения из каталога.
    
    Аргументы:
        in_dir: путь к входному каталогу.

    Выводит:
        словарь сопоставлений имен файлов с PNG-изображениями.
    """
    name_to_image = {}
    file_names = os.listdir(in_dir)
    for file_name in file_names:
        if file_name.endswith('.png'):
            image_path = os.path.join(in_dir, file_name)
            with open(image_path, 'rb') as image_file:
                encoded = base64.b64encode(image_file.read())
                image = encoded.decode('utf-8')
                name_to_image[file_name] = image

    return name_to_image

def create_prompt(  2 Создает мультимодальный промпт
    person_image, image_to_label):
    """ Создать промпт для сравнения изображений.

    Аргументы:
        person_image: изображение человека.
        image_to_label: изображение для назначения метки.

    Выводит:
        Промпт для проверки, изображен ли один и тот же человек
        на обеих иллюстрациях.
    """
    task = {'type':'text',
            'text':'На этих фотографиях один и тот же человек ("Да"/"Нет")?'}
    prompt = [task]
    for image in [person_image, image_to_label]:
        image_url = {'url':f'data:image/png;base64,{image}'}
        image_msg = {'type':'image_url', 'image_url':image_url}
        prompt += [image_msg]

    return prompt

def call_llm(ai_key, prompt):             3 Генерирует ответ на промпт
    """Вызвать языковую модель для обработки промпта с локальными изображениями.

    Аргументы:
        ai_key: ключ для доступа к OpenAI.
        prompt: промпт, объединяющий текст и локальные изображения.

    Выводит:
        Ответ языковой модели.
    """
    headers = {
        'Content-Type': 'application/json',
        'Authorization': f'Bearer {ai_key}'
    }
    payload = {
        'model': 'gpt-4o',
        'messages': [
            {'role': 'user', 'content': prompt}
            ],
        'max_tokens':1
        }
    response = requests.post(
        'https://api.openai.com/v1/chat/completions',
        headers=headers, json=payload)
    return response.json()['choices'][0]['message']['content']

if __name__ == '__main__':                4 Отмечает изображения с людьми

    parser = argparse.ArgumentParser()    5 Параметры командной строки
    parser.add_argument('peopledir', type=str, help='Изображения людей')
    parser.add_argument('picsdir', type=str, help='Изображения для тегирования')
    parser.add_argument('outdir', type=str, help='Выходной каталог')
    args = parser.parse_args()

    people_images = load_images(args.peopledir)
    unlabeled_images = load_images(args.picsdir)

    for person_name, person_image in people_images.items():  6 Перебирает людей
        for un_name, un_image in unlabeled_images.items():   7 Перебирает изображения
                     prompt = create_prompt(person_image, un_image)
                     ai_key = os.getenv('OPENAI_API_KEY')
                     response = call_llm(ai_key, prompt)
                     description = f'un_name versus person_name?'
                     print(f'description -> response')

                     if response == 'Да':         8 Копирует изображение в случае совпадения
                         labeled_name = f'person_name[:-4]un_name'
                         source_path = os.path.join(args.picsdir, un_name)
                         target_path = os.path.join(args.outdir, labeled_name)
                         shutil.copy(source_path, target_path)

Сначала рассмотрим основную функцию 4. Как обсуждалось ранее, в качестве параметров командной строки пользователи указывают три каталога 5: с фотографиями, которые требуется тегировать; с изображениями людей, которых нужно отметить на фотографиях; и каталог для вывода.

На первом этапе мы загружаем все изображения для тегирования, а также все изображения людей, которых нужно найти. Для этого используется функция load_images 1. Она получает список всех файлов во входном каталоге, а затем рассматривает те, которые заканчиваются суффиксом .png (то есть все изображения в формате PNG). Как обсуждалось ранее, изображения необходимо закодировать в виде строк (применив кодировку base64), которые в конечном счете представлены с помощью кодировки UTF-8. Результатом работы функции load_images является словарь Python, сопоставляющий имена файлов с соответствующими закодированными изображениями. Этот словарь возвращается как результат функции.

Применив функцию load_images к каждому из двух входных каталогов, мы получим два словаря Python. Один сопоставляет имена файлов, содержащих изображения людей (которые, как мы договорились ранее, являются именами этих людей), с соответствующим закодированным изображением. Другой сопоставляет имена файлов изображений, которые необходимо тегировать, с закодированными изображениями.

Наша цель — сопоставить каждую фотографию, предназначенную для тегирования, со всеми людьми, которые на ней запечатлены. Поскольку мы используем промпты, одномоментно сравнивающие только две фотографии, то придется просмотреть все сочетания людей и изображений, которые требуется тегировать. Именно поэтому мы используем двойной вложенный цикл for: один перебирает людей 6, а другой — размечаемые изображения 7.

Для каждого сочетания изображения и человека с помощью функции create_prompt создается мультимодальный промпт. Эта функция 2 объединяет оба закодированных изображения вместе с текстовыми инструкциями в промпт. Текстовые инструкции («На фотографиях один и тот же человек (“Да”/“Нет”)?») определяют задачу, а также ожидаемый формат вывода («Да» или «Нет»). Каждый промпт отправляется в языковую модель GPT-4o через функцию call_llm. Как обсуждалось ранее, эта функция 3 использует API запросов для отправки фотографий, находящихся в локальном хранилище, в GPT-4o. Если GPT-4o отвечает «Да», значит, рассматриваемый в данный момент человек есть на изображении, которое сейчас размечается.

Если человек есть на фотографии 8, то мы помечаем ее следующим образом. Берем имя человека (имя соответствующего файла изображения без расширения .png) и добавляем в начало имени файла изображения, которое требуется тегировать. Затем файл этого изображения копируем в выходной каталог, уже с новым именем (которое содержит тег).

Проверка в работе

Пробуем. Если у вас есть настоящие фотографии из отпуска, на которых можно кого-то отметить, то можете использовать их. Если нет, то подходящие тестовые данные доступны по ссылке Tagging, размещенной на сайте книги в разделе дополнительных материалов к главе. Скачайте заархивированный файл и распакуйте. После распаковки в полученной папке должно быть три подкаталога:

  • people — фотографии людей (в данном случае фото актеров из серии фильмов «Мстители»). Имена файлов содержат имена соответствующих актеров;

  • pics — еще один набор фотографий (в данном случае дополнительные фото тех же актеров, что и в папке people), который нужно тегировать именами актеров;

  • processed — пустая папка, которую можно использовать в качестве выходного каталога.

Предположим, что путь к месту хранения распакованной папки — /tagging (так, путь /tagging/people будет вести к подкаталогу с фотографиями людей, которых нужно найти). Выполните код, запустив из терминала такую команду:

python listing2.py /tagging/people /tagging/pics /tagging/processed

СОВЕТ При запуске кода на платформе под управлением операционной системы Windows пути придется адаптировать. В частности, необходимо будет заменить символ / на \.

Во время обработки программа выводит информацию о том, есть ли конкретные люди на конкретных фотографиях. В примере данных есть фото двух человек, которых нужно найти, и четыре изображения, которые требуется тегировать. Это означает, что обработка должна занимать не более нескольких минут (обычно менее двух).

После завершения обработки зайдите в выходную папку. Там должны находиться размеченные изображения, к именам которых добавлены префиксы — имена людей, запечатленных на этих фото. Неплохо для нескольких строк кода на Python!

Генерация заголовков для видеороликов

Помимо множества фотографий (которые теперь можно автоматически тегировать благодаря коду, описанному в предыдущем разделе!), в отпуске вы сняли и немало видеороликов. Автоматически присвоенные имена файлов мало информативны. В каком видеоролике показывается, как вы плаваете в океане? Было бы здорово присвоить видеороликам осмысленные названия, и это поможет быстрее находить нужные. Но у кого есть время вручную подписывать видеоролики? И снова для автоматического выполнения этой задачи можно использовать языковые модели.

Обзор

Мы разработаем систему, которая будет автоматически присваивать видеороликам подходящие заголовки. Эта система использует языковую модель GPT-4o в фоновом режиме. Чтобы присвоить заголовки видеороликам, необходимо отправить мультимодальный промпт, содержащий кадры видео (то есть изображения) и текст, в котором указание для языковой модели — придумать заголовок. На рис. 6.3 показан пример промпта.

Промпт состоит из нескольких видеокадров (на рис. 6.3 мы видим только первый и последний кадры; три точки обозначают, что между ними есть еще кадры) и текстовой инструкции «Придумай краткий заголовок для видеоролика». Обратите внимание, что мы платим за каждый видеокадр, который отправляем в языковую модель GPT-4o. Следовательно, обработка видеоданных через GPT-4o быстро станет дорогостоящей!

Рис. 6.3. Мультимодальный промпт для обработки видеоролика: языковая модель получает инструкцию сгенерировать подходящий заголовок на основе подборки видеокадров
Рис. 6.3. Мультимодальный промпт для обработки видеоролика: языковая модель получает инструкцию сгенерировать подходящий заголовок на основе подборки видеокадров

В качестве ответа языковая модель GPT-4o должна отправить логичное название. В примере, показанном на рис. 6.3, это может быть отсылка к автомобилям и, возможно, даже к местоположению (на кадрах указано белым текстом).

Кодирование видеокадров

Сначала необходимо обсудить форматы видео. В предыдущем разделе вы узнали, как прописывать в коде изображения, находящиеся в локальном хранилище. Теперь мы расширим этот подход на работу с видеороликами. Наша цель — извлечь последовательность кадров. Однако видеоролики обычно хранятся не как последовательность кадров, а с использованием более эффективных кодировок. Для нас это означает, что сначала придется извлечь из видеоролика изображения.

Сделаем это с помощью библиотеки OpenCV. Это библиотека компьютерного зрения с открытым исходным кодом. Она предоставляет различные функции для компьютерного зрения, а также для обработки изображений и видеороликов в целом. Разумеется, функцию компьютерного зрения для нас выполнит модель GPT-4o. И все равно OpenCV будет полезна для извлечения кадров из видеороликов. Если вы еще не сделали этого, то самое время установить библиотеку OpenCV, следуя инструкциям в разделе 6.1.

Допустим, установка прошла успешно, и можно получить доступ к OpenCV из кода Python. Соответствующая библиотека Python называется cv2 (это название будет часто встречаться в виде префикса в приведенных ниже фрагментах кода).

Для работы с видеороликами, находящимися в локальном хранилище, сначала нужно открыть соответствующий файл. Запустите этот код, чтобы открыть видео, сохраненное по пути video_path:

video = cv2.VideoCapture(video_path)

Используя переменную video, теперь можно прочитать содержимое видеоролика, применив метод read:

success, frame = video.read()

Результат состоит из кортежей с двумя компонентами: индикатором успешности выполнения (success) и видеокадром (frame). Индикатор указывает, удалось ли нам прочитать еще один кадр. Он перестает работать при достижении конца видеоролика. В этом случае мы не получаем действительный кадр, и значение индикатора становится False.

Предположим, что нам удалось прочитать еще один кадр. В этом случае мы превратим кадр в изображение, которое можно отправить языковой модели GPT-4o. Библиотека OpenCV предоставляет соответствующую функциональность:

_, buffer = cv2.imencode('.jpg', frame)

Функция imencode превращает видеокадр в изображение соответствующего типа. Здесь мы преобразуем кадр в изображение в формате JPEG. В полученном кортеже нам интересен второй компонент (buffer). Он содержит двоичное представление соответствующего изображения.

Эта ситуация знакома нам по предыдущему разделу: есть двоичное представление изображения и его нужно превратить в формат, подходящий для языковой модели GPT-4o. И снова мы сначала кодируем изображение как строку с помощью кодировки base64, а затем представляем эту строку через UTF-8:

encoded = base64.b64encode(buffer)
frame = encoded.decode('utf-8')

Полученный кадр корректно закодирован для добавления в промпт для языковой модели GPT-4o. Завершив обработку видеоролика, закройте соответствующий объект захвата видео, используя такой код:

video.release()

Далее мы соберем фрагменты кода и генерируем заголовки для произвольных видеороликов.

Полный код реализации

В коде листинга 6.3 создаются названия для видеороликов, находящихся в локальном хранилище. Единственный входной параметр — путь к видеоролику. Получив его, программа извлекает несколько видеокадров 4, а затем генерирует промпт, дающий модели GPT-4o указание придумать заголовок для видеоролика на основе выборки кадров. После отправки этого промпта языковой модели ее ответ будет содержать предлагаемый заголовок видеоролика.

Листинг 6.3. Генерация заголовка видеоролика с помощью языковых моделей

import argparse
import cv2
import base64
import openai
import time

client = openai.OpenAI()

def extract_frames(video_path):                1 Извлекает видеокадры
    """ Извлекает кадры из видеоролика.

    Аргументы:
        video_path: путь к видеофайлу.

    Выводит:
        Список первых десяти видеокадров.
    """
    video = cv2.VideoCapture(video_path)
    frames = []
    while video.isOpened() and len(frames) <= 10:
        success, frame = video.read()
        if not success:
            break

        _, buffer = cv2.imencode('.jpg', frame)
        encoded = base64.b64encode(buffer)
        frame = encoded.decode('utf-8')
        frames += [frame]

    video.release()
    return frames

def create_prompt(frames):                     2 Создает мультимодальный промпт
    """ Составить промпт для генерации заголовка к видеоролику.

    Аргументы:
        frames: кадры видеоролика.

    Выводит:
        промпт, содержащий мультимодальные данные (список).
    """
    prompt = ['Придумай точное название для видеоролика.']
    for frame in frames[:10]:
        element = {'image':frame, 'resize':768}
        prompt += [element]
    return prompt

def call_llm(prompt):                          3 Отправляет запрос в языковую модель
    """ Отправить запрос большой языковой модели и вывести ответ.

    Аргументы:
        prompt: ввести запрос к языковой модели.

    Выводит:
        Ответ языковой модели.
    """
    for nr_retries in range(1, 4):
        try:
            response = client.chat.completions.create(
                model='gpt-4o',
                messages=[
                    {'role':'user', 'content':prompt}
                    ]
                )
            return response.choices[0].message.content
        except:
            time.sleep(nr_retries * 2)
    raise Exception('Не удается выполнить запрос к модели OpenAI!')

if __name__ == '__main__':

    parser = argparse.ArgumentParser()
    parser.add_argument('videopath', type=str, help='Путь к видеофайлу')
    args = parser.parse_args()

    frames = extract_frames(args.videopath)    4 Создает название для видеоролика
    prompt = create_prompt(frames)
    title = call_llm(prompt)
    print(title)

Код извлекает видеокадры с помощью функции extract_frames 1. Как обсуждалось ранее, она использует библиотеку OpenCV, чтобы открыть видеоролик для извлечения кадров, а затем последовательно считывает каждый кадр. Для генерации названия видео мы будем использовать не более десяти кадров. Вот почему извлечение заканчивается после максимум десяти кадров (или меньшего количества, если видео очень короткое). Каждый извлеченный кадр кодируется в соответствии с требованиями GPT-4o (то есть изображения в формате JPEG, закодированные как строки). Результатом функции является список закодированных кадров.

В процессе генерации промпта 2 мы объединяем соответствующие текстовые инструкции («Придумай краткий заголовок для видео») с первыми десятью кадрами из видеоролика. Для отправки этих изображений вместе с инструкциями в модель GPT-4o снова воспользуемся оберткой Python 3. Как вариант, можно самостоятельно составлять запросы (как мы делали в предыдущем проекте). Ответ языковой модели должен содержать подходящий заголовок для нашего видеоролика.

Разумеется, мы отправляем только первые несколько кадров видеоролика. Если его содержание после этих нескольких кадров кардинально изменится, то заголовок может оказаться неоптимальным. Причина, по которой мы отправляем только десять кадров, — стоимость вычислений. Помните, что вы платите за каждое отправленное в промпт изображение! Отправка всех кадров длинных видеороликов, как правило, обходится непомерно дорого. Вот почему мы довольствуемся отправкой только небольшого подмножества видеокадров.

Проверка в работе

Протестируем наш генератор заголовков видеороликов! На сайте книги в разделе дополнительных материалов к этой главе есть ссылка Cars, по которой вы можете найти короткий видеоролик с камеры наблюдения, показывающий движение на оживленной дороге. Скачайте его на свой компьютер.

Откройте терминал и перейдите в каталог, содержащий код для этой главы. Предполагается, что видеоролик был загружен в тот же каталог (если нет, замените имя видеоролика — cars.mp4 — на полный путь к нему).

Запустите следующую команду:

python listing3.py cars.mp4

Спустя несколько секунд вычислений должен появиться предлагаемый вариант заголовка видеоролика: например, «Дорожная ситуация на шоссе I-5 у SR 516 и 188-й улицы» (точное название может меняться при разных запусках из-за рандомизации).

Обратите внимание, что название содержит информацию (указание местоположения), которая доступна в видеоролике только в виде текста. Использование GPT-4o для извлечения текста из изображений может быть полезно в различных сценариях: например, для извлечения данных из бланков.

Резюме

  • Языковая модель GPT-4o обрабатывает как изображения, так и текст.

  • В промптах можно объединять фрагменты текста и изображения.

  • Модель GPT-4o поддерживает несколько форматов изображений.

  • Изображения можно указывать через общедоступный URL.

  • Изображения, находящиеся в локальном хранилище, можно загружать в OpenAI.

  • Модель GPT-4o обрабатывает изображения, закодированные в виде строк.

  • Стоимость обработки изображений выше, чем обработки текста, и может зависеть от размера изображения. Обработка изображений с низкой степенью детализации снижает затраты.

  • Модуль base64 может кодировать изображения в строки.

  • Для отправки в языковую модель GPT-4o видео нужно разделять на кадры.

  • Для извлечения кадров из видео можно использовать библиотеку OpenCV.


Оформить предзаказ на книгу «Анализ данных с LLM. Текст, таблицы, изображения и аудио» можно на нашем сайте.

Для Хаброжителей действует скидка 35% по промокоду — Предзаказ

P.S. Напоминаем, что у нас на сайте проходит сезонная распродажа.

Комментарии (1)


  1. KEugene
    31.05.2026 13:41

    То есть модель распознает "без сомнения и упрека"?

    Например, распознавание лиц в Лайтруме работает так, что если человек точно опознан, то ок. Но если есть сомнения, то живой оператор должен подтвердить или опровергнуть распознавание. Это повлияет на дальнейшее распознавание. Тут же ложные срабатывания попросту игнорируются. Если эталон лица одел очки или шапку, то он уже не попадет в выборку. Правильно?