惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
Microsoft Security Blog
Microsoft Security Blog
宝玉的分享
宝玉的分享
小众软件
小众软件
雷峰网
雷峰网
F
Full Disclosure
B
Blog
Hugging Face - Blog
Hugging Face - Blog
GbyAI
GbyAI
月光博客
月光博客
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
TaoSecurity Blog
TaoSecurity Blog
博客园 - 聂微东
P
Palo Alto Networks Blog
N
Netflix TechBlog - Medium
S
Secure Thoughts
Google Online Security Blog
Google Online Security Blog
P
Privacy & Cybersecurity Law Blog
U
Unit 42
Cloudbric
Cloudbric
Know Your Adversary
Know Your Adversary
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
Vercel News
Vercel News
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
N
News and Events Feed by Topic
T
Tailwind CSS Blog
S
Schneier on Security
IT之家
IT之家
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tenable Blog
Google DeepMind News
Google DeepMind News
The GitHub Blog
The GitHub Blog
T
Troy Hunt's Blog
V2EX - 技术
V2EX - 技术
Cyberwarzone
Cyberwarzone
P
Privacy International News Feed
I
InfoQ
MongoDB | Blog
MongoDB | Blog
Project Zero
Project Zero
B
Blog RSS Feed
Help Net Security
Help Net Security
H
Heimdal Security Blog
有赞技术团队
有赞技术团队
The Register - Security
The Register - Security
V
V2EX
C
CXSECURITY Database RSS Feed - CXSecurity.com
J
Java Code Geeks

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Тегирование людей на изображениях и Генерация заголовков для видеороликов
ph_piter · 2026-05-31 · via Все публикации подряд на Хабре

Тегирование людей на изображениях и Генерация заголовков для видеороликов

17 мин

8.7K

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

Обзор

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

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

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

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

Для простоты мы будем использовать в качестве тегов имена файлов. Мы предполагаем, что фотографии с человеком, которого нужно найти, названы по имени этого человека. Например, в нашем каталоге с фотографиями людей, которых нужно найти, есть изображения с именами 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. Напоминаем, что у нас на сайте проходит сезонная распродажа.