Добро пожаловать в наш еженедельный блог с советами и рекомендациями FiftyOne, где мы резюмируем интересные вопросы и ответы, недавно появившиеся на Slack, GitHub, Stack Overflow и Reddit.

Подожди, а что такое FiftyOne?

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

Хорошо, давайте погрузимся в советы и рекомендации этой недели!

Добавление метаданных в набор данных FiftyOne

Член сообщества Slack Иммануэль Вебер спросил:

«Всем привет! Прежде всего позвольте мне сказать, что я действительно люблю FiftyOne! Я хочу добавить метаданные в свои образцы, и с помощью set_values() я смог добавить метаданные, но при таком подходе поля не отображаются в приложении FiftyOne. Является ли мой подход допустимым способом добавления новых метаданных? Есть ли способ заставить приложение FiftyOne отображать эти новые поля? Спасибо!"

Отличный вопрос, Иммануил. В FiftyOne поля, видимые в приложении FiftyOne, определяются вашим набором данных. В приложении отображаются только те поля, которые являются частью схемы. Вы можете распечатать dataset.get_field_schema(), чтобы увидеть схему вашего поля.

Начиная с FiftyOne 0.19.0, когда вы используете set_values() для добавления поля к образцам в вашем наборе данных, у него есть аргумент dynamic, который вы можете использовать для управления добавлением поля в вашу схему. По умолчанию dynamic=False, поэтому поле недобавляется. Если вы передадите dynamic=True, то он будет добавлен в схему и появится в приложении FiftyOne.

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

Вы по-прежнему можете создавать более сложные вложенные объекты в новых EmbeddedDocumentField полях и отображать их в приложении FiftyOne. Например, если вы хотите создать новое поле custom_metadata со встроенным полем, которое хранит уникальность образца, вы можете сделать это следующим образом:

import fiftyone as fo
import fiftyone.brain as fob
import fiftyone.zoo as foz
import fiftyone.core.odm as foo

# load dataset
dataset = foz.load_zoo_dataset("quickstart")

# compute uniqueness
fob.compute_uniqueness(dataset)

# Add a generic embedded document field to which you can add any fields you want
dataset.add_sample_field(
    "custom_metadata",
    fo.EmbeddedDocumentField,
    embedded_doc_type=foo.DynamicEmbeddedDocument,
)

# set values and add to schema
dataset.set_values(
    "custom_metadata.uniqueness",
    dataset.values("uniqueness"),
    dynamic=True,
)

# visualize
session = fo.launch_app(dataset)

Узнайте больше о встроенных документах и динамических атрибутах в FiftyOne Docs.

Изменение тегов при загрузке аннотаций CVAT в FiftyOne

Даниэль Фортунато, член сообщества Slack, спросил:

«Привет всем! Я хочу загрузить образцы из прогона аннотации CVAT с тегом «to_annotate», а затем изменить их на «being_annotated» в FiftyOne, чтобы я мог отслеживать, какие образцы еще нужно загрузить. Я попытался использовать load_annotation_view() для загрузки представления из определенного прогона аннотаций, но, похоже, это не работает с изменением тегов. Как бы вы порекомендовали мне это сделать?'

Эй, Даниэль! Когда вы меняете теги, причина, по которой load_annotation_view() больше не работает, заключается в том, что внутри метод использует этап просмотра MatchTags, который определяется путем поиска всех образцов, имеющих определенные теги. Если вы создадите представление, передав ' to_annotate', а затем измените теги своих образцов на 'being_annotated', эти образцы больше не будут соответствовать условию.

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

import fiftyone as fo
import fiftyone.zoo as foz

# load dataset
dataset = foz.load_zoo_dataset("quickstart")

# match tags
view = dataset.match_tags("to_annotate")

# redefine the view by sample ID
view = dataset.select(view)

# change tags
view.untag_samples("to_annotate")
view.tag_samples("being_annotated")

Узнайте больше об стадиях просмотра, FiftyOne Annotation API и нашей интеграции CVAT в FiftyOne Docs.

Предварительный просмотр видеокадров в FiftyOne

Член сообщества Slack Триша Рамкумар спросила:

"У меня есть набор видеоданных с тысячей кадров. Как сэмплировать изображения через равные промежутки времени и предварительно просматривать их в приложении FiftyOne?»

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

Однако, если вы работаете с очень большими видео, может случиться так, что вы захотите просмотреть только один из каждых 100 или 1000 кадров. Один из способов просмотреть видеокадры в приложении FiftyOne — преобразовать набор видеоданных в набор данных изображений, рассматривая каждый кадр как новый образец. Затем вы можете использовать ViewField FiftyOne и метод match() для фильтрации по номеру кадра в поле frame_number образцов кадров, превращенных в изображения.

Например, если вы работали с Quickstart Video Dataset и хотели сэмплировать кадры со скоростью одно изображение на каждые десять кадров исходного видео, вы могли бы запустить следующее:

import fiftyone as fo
import fiftyone.zoo as foz
from fiftyone import ViewField as F

# load dataset
dataset = foz.load_zoo_dataset("quickstart-video")

# convert from videos to frames
frames = dataset.to_frames(sample_frames=True)

# sample every tenth frame
view = frames.match(F("frame_number") % 10 == 0)

# display, or "preview" the results
session = fo.launch_app(view)

Узнайте больше о работе с видео, просмотрах видео и фреймах в FiftyOne Docs.

Агрегации на уровне кадра

Член сообщества Slack Джой Тиммерманс спросила:

"Как мне рассчитать распределение высоты ограничивающих рамок в моем наборе видеоданных, не зацикливаясь на каждом обнаружении?"

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

В FiftyOne агрегирование и многие другие операции изначально работают с кадрами видео с помощью синтаксиса "." для доступа к атрибутам уровня кадра. Например, следующее генерирует гистограмму значений высоты кадра для всех кадров и выборок в Quickstart Video Dataset.

import fiftyone as fo
import fiftyone.zoo as foz
from fiftyone import ViewField as F

# load dataset
dataset = foz.load_zoo_dataset("quickstart-video")

# compute frame width and height
dataset.compute_metadata()

# compute histogram counts and bin edges
# height is last element of bounding box
count, edges, _ = dataset.histogram_values(
    F('frames.detections.detections.bounding_box')[3]
)

Узнайте больше о histogram_values() и Выражениях в FiftyOne Docs.

Удаление повторяющихся образцов

Участник сообщества Slack Дэн Эрез спросил:

«В моем наборе данных случайно оказалось несколько повторяющихся образцов. Есть ли быстрый способ удалить дубликаты и оставить только по одному?"

Эй, Дэн! Случайное дублирование — распространенная проблема при работе с данными компьютерного зрения. В FiftyOne, например, если вы попытаетесь добавить образец, который уже существует в вашем наборе данных, вместо того, чтобы ничего не делать или перезаписать исходный образец, набор данных создаст новую копию образца и добавит это к набору данных. Например, следующий код добавляет в Quickstart Dataset пятьдесят дубликатов образцов:

import fiftyone as fo
import fiftyone.zoo as foz

# load dataset
dataset = foz.load_zoo_dataset("quickstart")

print(dataset.count())
# 200

# randomly select 50 samples
samples_to_duplicate = dataset.take(50)

# add these as duplicates
dataset.add_samples(samples_to_duplicate)

print(dataset.count())
# 250

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

Предполагая, что каждая выборка в исходном наборе данных имеет уникальный путь к файлу, можно легко «отменить» это дублирование и вернуться к исходному набору данных. Ключом к выявлению дубликатов является определение того, какие пути к файлам встречаются в наборе данных более одного раза, а затем удаление всех образцов, кроме одного, для каждого.

Чтобы найти многократно встречающиеся пути к файлам, вы можете использовать агрегацию FiftyOne count_values():

fp_counts = dataset.count_values("filepath")
dup_fps = [key for key in list(fp_counts.keys()) if fp_counts[key] > 1]

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

## IDs of first sample in dataset for each fp
sids = [dataset[dup_fp].id for dup_fp in dup_fps]
dataset.delete_samples(sids)

Если у вас более одного дубликата на путь к файлу, ознакомьтесь с нашим рецептом дедупликации изображений.

Узнайте больше об агрегациях и дедупликации в FiftyOne Docs.

Присоединяйтесь к сообществу FiftyOne!

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

Что дальше?

Первоначально опубликовано на https://voxel51.com 10 марта 2023 г.