В этой статье мы познакомимся с форматом EXIF и узнаем, какую информацию содержат метаданные фотографий, как эту информацию можно получить, и каким образом использовать. В качестве примера будем использовать фототеку, управляемую с помощью Apple Photos.
Согласно статистике приложения Photos, за последние 14 лет владения iPhone я сделал 73 281 фотографию.
Каждое из этих фото не просто содержит само изображение, которое мы видим при прокрутке библиотеки, а целую кучу информации, закодированной в его файле. Эта информация отражает полезные метаданные, такие как место снимка (чтобы в дальнейшем можно было просматривать фото в привязке к карте), его дату и время, использованные линзы, кратность зума, экспозицию, ISO, апертуру объектива и многое другое.
Для просмотра всех этих данных существует масса инструментов. При этом какое бы приложение вы ни использовали для управления фотографиями, сопутствующие метаданные будут отображаться вместе с изображением, как в iOS 16 здесь:
Эти метаданные называются EXIF (Exchangeable Image File Format) и хранятся в самом начале самих файлов фотографий (до данных фактического изображения). Если рассмотреть содержимое JPEG-снимка, экспортированного с iPhone, то мы увидим, что маркер данных EXIF (FFE1
) идёт сразу после маркера начала изображения (FFD8
):
Поспешу вас успокоить — в этой статье мы не будем разбирать каждый байт вышеприведённого изображения. Вместо этого мы рассмотрим содержимое фотографии и различные способы отображения, запроса и взаимодействия с содержащимися в нём данными.
▍ Что содержится в снимке?
На изображении ниже показано, что в одной фотографии содержится огромный объём информации. Можете смело использовать для просмотра данных EXIF представленные примеры из моей личной библиотеки или же выбрать собственное фото.
Имейте в виду, что любые выбранные изображения остаются на стороне клиента, никогда не покидая ваш браузер. Парсинг EXIF на клиентской стороне производится библиотекой ExifReader, написанной Маттиасом Волландером.
В целях безопасности iOS исключает из загружаемых изображений информацию о местоположении, так что для получения полной картины попробуйте проделать это на десктопной системе. Интересно, что iOS добавляет в фото дополнительные метаданные, такие как обнаруженные на изображении лица (включая их положение по осям
x
иy
,width
иheight
, а также углыyaw
иroll
).
Фрагмент интерактивного примера из оригинала статьи, где вы можете загрузить своё фото
Ещё интереснее то, что для каждого обнаруженного лица iOS включает свойство FaceID
, которое представляет собой последовательно возрастающее целое число, связанное с ID лица во вкладке People приложения Photos. Предполагаю, что меньшие значения FaceID
могут указывать на более близкую степень знакомства людей в рамках снимка.
Обратите на них внимание, когда будете просматривать данные, хранящиеся в ваших фотографиях.
Каждое изображение несёт в себе большой клубок информации. В нём содержатся не только данные по освещению, но также ваше точное местоположение и ориентация в момент снимка.
Если у вас есть машина времени, и вы окажетесь свободны 13 августа 2023 года в 13:07:07, то сможете узнать, где меня найти.
▍ Доступ к метаданным
Как мы только что видели, фотографии содержат большой объём информации. Извлечение и запрос сотен полей в фотоархиве, включающем тысячи снимков, может стать непростой задачей. К счастью, существует ряд инструментов, которые с этим помогут.
Эталоном здесь выступает exiftool, и я очень рекомендую поэкспериментировать с ним. В попытках понять значение некоторых полей незаменимой окажется документация и справочник тегов.
Для хранения и запроса фотографий мы будем использовать БД SQLite. Она вполне подходит для наших целей и отличается высоким быстродействием.
Начнём с установки этих инструментов. На Mac это можно сделать с помощью brew
:
brew install sqlite3 exiftool
Если вы используете Apple Photos, то у вас есть два пути: экспорт всей библиотеки фотографий с последующим применением exiftool к выходным изображениям либо непосредственно к сгенерированным пиктограммам, которые Apple Photos использует внутренне.
Если вам просто нужен быстрый способ генерации полного набора данных EXIF для вашей библиотеки Apple Photos, то мне понравилось, как работает следующая команда. Она парсит внутренний каталог derivatives в поиске пиктограмм с низким разрешением (те, которые оканчиваются на _c.jpeg):
exiftool -n -csv -r ~/Pictures/Photos Library.photoslibrary/resources/derivatives -i ~/Pictures/Photos Library.photoslibrary/resources/derivatives/masters -if '$Filename=~/_c.jpeg/i' > exif.csv
Извлечение набора фотографий из Apple Photos может оказаться на удивление непростым процессом. В связи с тем, как организована внутренняя схема и хранилище изображений, оригиналы многих из которых хранятся удалённо на iCloud, для парсинга здесь требуются специальные инструменты. Хорошо подойдёт для этого osxphotos. Вы можете использовать его для экспорта всей библиотеки Apple Photo в любом формате, а флаги вроде --added-in-last "1 month"
позволят выполнить детальную фильтрацию. Например, я успешно извлёк набор данных с помощью следующей команды:
osxphotos export --added-in-last "1 month" --only-photos --not-edited --exif Model iPhone ./export
Когда у нас есть каталог изображений, можно использовать exiftool для извлечения метаданных в CSV.
exiftool -n -m -csv ./images > exif.csv
После чего импортировать полученный CSV в SQLite для последующего запроса его содержимого:
sqlite exif.db
sqlite> .mode csv
sqlite> .import exif.csv photos
sqlite> SELECT count(*) FROM photos;
68182
Теперь у нас есть база доступных для запроса метаданных каждого сделанного когда-либо снимка.
Чуть позже мы позадаём этой базе данных некоторые вопросы.
▍ Интересные поля
В процессе просмотра всех доступных метаданных меня особенно привлекли некоторые поля. Ряд из них являются стандартами EXIF, но многие относятся конкретно к камерам Apple:
Поле обзора (FOV
). Горизонтальное поле обзора, охватываемое камерой при съёмке.
Ориентация / Направление (GPSImgDirection
). Наряду с позицией (долготой и широтой) EXIF также сохраняет информацию о направлении камеры в момент снимка. Совместно с полем обзора эта информация позволяет дополнить наше расположение на карте конусом поля зрения.
Высота (GPSAltitudeRef
). Высота, хоть и бывает зачастую неточной, может использоваться для дополнения двухмерной пары широты/долготы третьим измерением. Это дополнительное измерение пригождается, когда съёмка происходит в многоуровневых зданиях или в воздухе.
Скорость (GPSSpeed
). Как быстро двигалась камера в момент получения снимка.
Угол наклона (CameraElevationAngle
). При достаточной точности, используя пространственную точку x, y, z, обозначенную полями GPSLatitude
, GPSLongitude
и GPSAltitudeRef
, а также угол из поля GPSImgDirection
, можно выстроить в пространстве 3D-вектор в заданный момент времени.
Это единственное свойство из приведённого списка, которое в текущих моделях iPhone не отражается, но которое оказалось бы очень кстати. Если эту статью читает кто-либо из разработчиков Apple или создателей приложения для камеры, то мне бы хотелось узнать, в чём сложность реализовать захват CameraElevationAngle
.
Представьте себе возможность поиска документов путём отфильтровывания изображений до тех, которые были получены при съёмке сверху вниз.
Шум изображения (SignalToNoiseRatio
). Отношение сигнала к шуму, зафиксированное датчиком камеры. Можно использовать для повышения качества изображений.
Время работы камеры (RunTimeValue
). Интересный элемент данных: как долго камера работала с момента последнего включения. В современном мире постоянно включённых смартфонов возникает вопрос относительно актуальности этой информации, но она тем не менее фиксируется.
Точка фокуса (FocusDistance & FocusPosition
). Расстояние от камеры, на которое наведён фокус изображения.
Ускорение камеры (AccelerationVector
). Трёхмерный вектор, демонстрирующий ускорение камеры вперёд/назад, вверх/вниз и из стороны в сторону.
Эти поля позволяют задать нашей фототеке интересные вопросы.
Извиняюсь за использование здесь различных вызовов CAST
. Можно было бы корректно типизировать схему нашей таблицы SQLite, но для этого сгенерированный CSV должен в точности согласовываться с определением схемы, что с учётом большой вариативности тегов EXIF бывает очень редко. Ниже я прикрепил примерную схему и скрипт экспорта.
exiftool -n -m -csv -p '$filename, $AccelerationVector, $AEAverage, $AEStable, $AETarget, $AFConfidence, $AFMeasuredDepth, $Aperture, $BrightnessValue, $CircleOfConfusion, $DateTimeOriginal, $DigitalZoomRatio, $ExposureCompensation, $ExposureTime, $FileSize, $Flash, $FocalLengthIn35mmFormat, $FocusPosition, $FOV, $GPSAltitude, $GPSAltitudeRef, $GPSHPositioningError, $GPSImgDirection, $GPSLatitude, $GPSLongitude, $GPSSpeed, $HDRGain, $HDRHeadroom, $HyperfocalDistance, $ISO, $LightValue, $Megapixels, $MeteringMode, $Orientation, $RegionAppliedToDimensionsH, $RegionAppliedToDimensionsW, "$RegionAreaH", "$RegionAreaW", "$RegionAreaX", "$RegionAreaY", $RegionExtensionsAngleInfoRoll, $RegionExtensionsAngleInfoYaw, $RegionExtensionsConfidenceLevel, $RegionExtensionsFaceID, "$RegionType", $RunTimeSincePowerUp, $ShutterSpeed, $SignalToNoiseRatio, $SubjectArea, $WhiteBalance' ./export > export.csv
sqlite> CREATE TABLE "photos" (
"SourceFile" TEXT,
"AccelerationVector" TEXT,
"AEAverage" INTEGER,
"AEStable" INTEGER,
"AETarget" INTEGER,
"AFConfidence" INTEGER,
"AFMeasuredDepth" INTEGER,
"Aperture" REAL,
"BrightnessValue" REAL,
"CircleOfConfusion" REAL,
"DateTimeOriginal" TEXT,
"DigitalZoomRatio" REAL,
"ExposureCompensation" REAL,
"ExposureMode" INTEGER,
"ExposureTime" REAL,
"FileSize" INTEGER,
"Flash" INTEGER,
"FNumber" REAL,
"FocalLengthIn35mmFormat" REAL,
"FocusDistanceRange" REAL,
"FocusPosition" INTEGER,
"FOV" REAL,
"GPSAltitude" REAL,
"GPSAltitudeRef" INTEGER,
"GPSHPositioningError" REAL,
"GPSImgDirection" REAL,
"GPSLatitude" REAL,
"GPSLongitude" REAL,
"GPSSpeed" REAL,
"HDRGain" REAL,
"HDRHeadroom" REAL,
"HyperfocalDistance" REAL,
"ISO" INTEGER,
"LightValue" REAL,
"Megapixels" REAL,
"MeteringMode" INTEGER,
"Orientation" INTEGER,
"RegionAppliedToDimensionsH" INTEGER,
"RegionAppliedToDimensionsW" INTEGER,
"RegionAreaH" TEXT,
"RegionAreaW" TEXT,
"RegionAreaX" TEXT,
"RegionAreaY" TEXT,
"RegionExtensionsAngleInfoRoll" INTEGER,
"RegionExtensionsAngleInfoYaw" INTEGER,
"RegionExtensionsConfidenceLevel" INTEGER,
"RegionExtensionsFaceID" INTEGER,
"RegionType" TEXT,
"RunTimeSincePowerUp" REAL,
"ShutterSpeed" REAL,
"SignalToNoiseRatio" REAL,
"SubjectArea" TEXT,
"WhiteBalance" INTEGER
)
На каком снимке присутствует больше всего лиц?
SELECT SourceFile
FROM photos
WHERE RegionType IS NOT ''
ORDER BY length(RegionType) DESC
LIMIT 1;
На каком снимке объект съёмки расположен слишком близко для наведения фокуса?
SELECT SourceFile
FROM photos
WHERE CAST(FocusPosition AS INT) IS 255
LIMIT 10;
Какое фото я сделал через минимальный промежуток времени после включения телефона?
SELECT SourceFile
FROM photos
WHERE CAST(RunTimeValue AS INT) > 0
ORDER BY CAST(RunTimeValue as INT) ASC
LIMIT 1;
Сколько снимков я сделал при скорости движения выше 60 км/ч?
SELECT count(*)
FROM photos
WHERE CAST(GPSSpeed AS REAL) > 50;
317
Какое фото я получил при максимальной скорости движения?
SELECT SourceFile
FROM photos
ORDER BY CAST(GPSSpeed AS REAL) DESC
LIMIT 1;
А какое фото я сделал при максимальной скорости передвижения по суше?
SELECT SourceFile
FROM photos
WHERE CAST(GPSAltitude AS REAL) < 10
ORDER BY CAST(GPSSpeed AS REAL) DESC
LIMIT 1;
Покажи мне закат :)
SELECT SourceFile
FROM photos
WHERE CAST(GPSImgDirection AS INT) BETWEEN 225 AND 315
AND time(TimeCreated) BETWEEN time('16:00') AND time('20:00')
AND CAST(FocusPosition AS INT) = 0
ORDER BY CAST(SignalToNoiseRatio as REAL) DESC
LIMIT 10;
Результаты будут отличаться в зависимости от вашего расположения, но в моём случае всё сработало отлично. Этот запрос можно расширить, используя широту/долготу и время года, чтобы сузить временной диапазон конкретно до заката.
Какие фото я получил, уронив телефон?
SELECT SourceFile
FROM photos
ORDER BY CAST(substr(AccelerationVector, 0, instr(AccelerationVector, ' ')) AS REAL) ASC
LIMIT 10;
На каком снимке присутствует максимально удалённое лицо?
SELECT SourceFile
FROM photos
WHERE length(RegionAreaW) IS 20 AND length(RegionAreaH) IS 20
ORDER BY CAST(substr(RegionAreaW,0,20) AS REAL) * CAST(substr(RegionAreaH,0,20) AS REAL) ASC
LIMIT 10;
На каком снимке человек максимально наклонил голову?
SELECT SourceFile
FROM photos
ORDER BY CAST(RegionExtensionsAngleInfoRoll AS REAL) ASC
LIMIT 1;
▍ Визуализация
У нас есть все данные, чтобы ответить на эти вопросы, и были они у нас более десяти лет. Где же инструменты, позволяющие их увидеть?
Большие языковые модели (LLM, Large Language Models), такие как GPT-4, помогли нам приблизиться к миру «абстрактных вычислений». Теперь мы можем примерно запрашивать информацию без точного понимания, что конкретно ищем. Тем не менее для получения ответов при фильтрации огромных датасетов по-прежнему активно используется прямое взаимодействие с обратной связью.
Компьютерные интерфейсы — это эксплоративные механизмы. Текущие инструменты управления фотографиями зачастую раскрывают метаданные EXIF в виде интерфейсов только для чтения, но как бы мог выглядеть эксплоративный инструмент фотографий, работающий с EXIF?
▍ Картирование
Совмещение GPSLatitude
, GPSLongitude
, GPSAltitude
, GPSImgDirection
и FieldOfView
позволило бы не только помещать снимки на карту в виде флажков, но также отображать их парящими в 3D-пространстве с конусом зрения, исходящим из точки съёмки в направлении объекта съёмки:
Представьте себе серию таких снимков на 3D-карте, показывающей местонахождение объекта съёмки в пространстве и времени, или приложение пространственных вычислений для Vision Pro, позволяющее прогуливаться по ландшафту ваших фотографий.
▍ Фильтрация
Как вы находите снимки в фототеке? Я лично первым делом опираюсь на пространственную память. Сначала обычно использую карту в приложении Photo для уточнения места получения снимка и уже затем ориентируюсь по времени его получения.
А что, если бы для каждого из этих измерений EXIF — пространства, времени, скорости, угла съёмки, количества человек — у нас были интерфейсы с фильтрацией, которые бы позволяли совмещать фрагменты из нашей памяти для более быстрого обнаружения нужных снимков?
У нас есть отличные инструменты для поиска по одному из этих измерений, но сильно не хватает такого, в котором бы они совмещались в единый эксплоративный интерфейс.
Для сужения обширного многомерного датасета можно использовать парадигмы эксплоративного взаимодействия, такие как ручное отслеживание или MIDI-интерфейсы.
▍ Следствия
Когда вы размещаете в сети фото, сделанное в квартире друга, то раскрываете не только сам снимок, но также его широту, долготу и высоту. При достаточной точности GPS вполне возможно найти точную квартиру, в которой это фото было сделано.
В действительности, когда вы предоставляете приложению iOS полный доступ к вашей фототеке, то также выдаёте всю эту информацию. Вот используемые в Swift ключи kCGImageProperty
для метаданных EXIF, GPS и IPTC, отражающие информацию, доступную для любого приложения, способного получить доступ к вашим фото через iOS UIImagePickerController
(по крайней мере, в iOS 17). Любое приложение, имеющее доступ к вашей фототеке, при достаточных усилиях может определить ваш адрес; магазин, в который вы ходите, место проживания ваших друзей; где вы проводите выходные; где работаете и когда ложитесь спать. И это всё без просмотра содержимого самих фотографий.
Причём отсутствие в этом процессе ИИ вас может как обрадовать, так и опечалить.
Если в основе такого ИИ лежат большие данные, тогда в вашей фототеке находится реально огромный объём метаданных, которые вы дополняете с каждым новым снимком.
Последние достижения в области распознавания и сегментации изображений (например, в модели компании Meta под названием Segment Anything) позволяют получать из данных изображения ещё больше информации. В Apple Photos с помощью передовых моделей машинного обучения в фотографии добавляются «баллы» — ZOVERALLAESTHETICSCORE
, ZINTERESTINGSUBJECTSCORE
, ZHARMONIOUSCOLORSCORE
, ZWELLCHOSENSUBJECTSCORE
— на основе которых затем выделяются потенциально более интересные для пользователей снимки.
В следующий раз, когда будете рассматривать фото, задумайтесь о его метаданных, которые не видите — ускорение камеры, её температура, сцена за пределами фиксируемого камерой поля обзора.
Когда в очередной раз будете делать фото и вносить эту запись в сюжет вашей жизни, подумайте о тех, с кем вы этим сюжетом делитесь.
Но самое главное — подумайте о том, как вы себя чувствуете, когда используете иммерсивные, многомерные инструменты для передачи этого сюжета близким вам людям.
▍ Полезные ссылки
- Using SQL to find my best photo of a pelican according to Apple Photos. Прекрасная работа Саймона Уиллсона, где он рассказывает, как задействовал свойства используемых в Apple моделей машинного обучения при фильтрации собственной фототеки.
- osxphotos
- exiftool
Автор: Дмитрий Брайт