Опять XMP тэги лиц. Все плохо, но это поправимо

от автора

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

Оригинальные статьи: раз и два.

Все там написано хорошо и правильно, но счастья все равно нет.

Картинка для привлечения внимания:

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

Лица отмечать буду в пикасе, потому как привязка к гугловским контактам, кроссплатформенность, авто определение, хранение тегов внутри файла (с нюансами). А вот смотреть и использовать эти теги хочу везде в gallery3 потому что она это умеет, в lightroom потому что именно им пользуюсь как каталогизатором, в microsoft explorer потому как он у меня есть и в Microsoft Live Photo Gallery, просто потому что это второй популярный формат и почему бы и его тоже не использовать.

Кто тоже хочет заморочиться — добро пожаловать под кат.

Проблема номер 1 где пикаса хранит информацию о лицах?
Вариантов три:

  • Либо это связка из двух файлов: contacts.xml в профиле пользователя и picasa.ini в папке с фотографиями. Этот вариант верен если вы тегировали лица в пикасе версии меньше чем 3.9 и в последней пикасе, о с выключенной галкой «Store name tags in photo».
  • Второй вариант хранение лиц в XMP-mwg-rs если вы использовали только последнюю пикасу и включили галку «Store name tags in photo» до того как начали отмечать лица, надо помнить, что по умолчанию она включена.
  • И наконец третий вариант, самый распространенный: лица хранятся и там и там и что с этим делать вообще непонятно.

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

picface
почти работает, недавно обновлялась, но у меня многократно вылетала не доделав начатое до конца, а поскольку архив у меня примерно 60К фотографий, все это происходит долго и муторно.

самый популярный avpicfacexmptagger
Умеет делать почти все, что надо, но достаточно однобоко, давно не обновлялся, придумали свою собственную схему для xmp, можно подумать существующих мало. Но в принципе для приведения архива к состоянию два использовать можно.

Я использовал рекомендацию от самого гугла, вам нужна только пикаса версии 3.9 с включенной галкой «Store name tags in photo». Для гарантированной записи всех лиц в xmp надо пройтись по всем людям и переименовать во что-то, а потом переименовать обратно. Быстро, просто, работает. На вкладке people два раза кликаете по первому имени и переименовываете его в ‘x’ потом два раза кликаете по ‘x’ и переименовываете его обратно. Все у меня оттегировано полторы сотни людей и это заняло всего пару минут. Пикасу после этого лучше сразу не закрывать, а дать ей какое-то время записать данные на диск, потому как прогресса никакого она не показывает.

Следующим шагом мы хотим извлечь имена записаные пикасой в xmp-mwg-rs в теге RegionName и записать его в теги PersonInImage и RegionPersonDisplayName после этого мы сможем использовать эти теги при поиске во всех каталогизаторах и даже microsoft explorer будет нам показывать в информации имена людей на фотографии. Сделать это проще всего при помощи exiftool который можно скачать здесь.

exiftool -RegionName>PersonInImage photo.jpg exiftool -RegionName>RegionPersonDisplayName photo.jpg 

после этого мы видим информацию о людях во множестве сторонних програм

Так же можно конвертировать информацию о положении лиц на кадре из стандарта пикасы в стандарт от микрософта, отличаются они не только именами, но даже методом как считаются квадраты, одни задают длинну и высоту от верхнего левого угла квадрата, а другие от центра. Для конвертации нам нужен конфиг для exiftool.

ExifTool_config_convert_regions

%Image::ExifTool::UserDefined = (     'Image::ExifTool::Composite' => {         MyRegion => {             Require => {                 0 => 'RegionInfoMP',                 1 => 'ImageWidth',                 2 => 'ImageHeight',             },             ValueConv => q{                 my ($rgn, @newRgns);                 foreach $rgn (@{$val[0]{Regions}}) {                     my @rect = split /\s*,\s*/, $$rgn{Rectangle};                     my %newRgn = (                         Area => {                             X => $rect[0] + $rect[2]/2,                             Y => $rect[1] + $rect[3]/2,                             W => $rect[2],                             H => $rect[3],                             Unit => 'normalized',                         },                         Name => $$rgn{PersonDisplayName},                         Type => 'Face',                     );                     push @newRgns, \%newRgn;                 }                 return {                     AppliedToDimensions => { W => $val[1], H => $val[2], Unit => 'pixel' },                     RegionList => \@newRgns,                 };             },         },         MyRegionMP => {             Require => 'RegionInfo',             ValueConv => q{                 my ($rgn, @newRgns);                 foreach $rgn (@{$val[0]{RegionList}}) {                     my @rect = @{$$rgn{Area}}{'X','Y','W','H'};                     $rect[0] -= $rect[2]/2;                     $rect[1] -= $rect[3]/2;                     push @newRgns, {                         PersonDisplayName => $$rgn{Name},                         Rectangle => join(', ', @rect),                     };                 }                 return { Regions => \@newRgns };             },         },     }, ); 1;  #end   

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

exiftool -config ExifTool_config_convert_regions "-regioninfomp\<MyRegionMP"' photo.jpg 

после этого лицо правильно отображается в Microsoft Live Photo Gallery и другом софте который придерживается той же схемы

Уже практически профит, но еще бы автоматизировать это дело для всех необходимых фотографий, а кроме того прописать имена в ключевые слова: теги Subject и HierarchialSuject да, здесь тоже не обошлось без двойных форматов. Для этих целей я написал плагинчик для лайтрума, это дает возможность запустить его только на нужных фотографиях, и добавлять ключевые слова, не опасаясь их задвоить или стереть уже существующие, ну и просто потому что я пользуюсь именно лайтрумом как каталогизатором всего архива.

За код просьба не бить, а лучше подсказать как его улучшить, это мой первый плагин для LR и вообще первый раз когда я увидел lua.

В плагине всего два файла

Info.lua

return { 	LrSdkVersion = 3.0, 	LrSdkMinimumVersion = 1.3, -- minimum SDK version required by this plug-in 	LrToolkitIdentifier = 'com.adobe.lightroom.sdk.helloworld', 	LrPluginName = LOC "$$$/PicasaFaceToTag/PluginName=Picasa Faces to Tags",  	-- Add the menu item to the Library menu. 	LrLibraryMenuItems = { 		{   title = "Write Picasa Faces to Tags", file = "PersonInImage.lua"}, 	}, 	VERSION = { major=4, minor=1, revision=0, build=831116, }, }  

PersonInImage.lua

--[[---------------------------------------------------------------------------- ------------------------------------------------------------------------------]] -- Access the Lightroom SDK namespaces. local LrTasks = import 'LrTasks' local LrProgressScope = import 'LrProgressScope' local LrApplication = import 'LrApplication'  local catalog = LrApplication.activeCatalog()  local photos = catalog:getTargetPhotos()  local LrPathUtils = import 'LrPathUtils' local logger = import 'LrLogger'("lr") logger:enable('print')  local function faceToTag() 	--[[Convert faces from picasa xmp tag to microsoft xmp ]] 	exeFile = LrPathUtils.child( _PLUGIN.path, "exiftool.exe" ) 	cfgFile = LrPathUtils.child( _PLUGIN.path, "ExifTool_config_convert_regions" ) 	redirect = LrPathUtils.getStandardFilePath('temp') .. "exiftool.stdout" 	local total = ( # catalog:getTargetPhotos() )  	local exifArgs = {"-b -RegionName \>" .. redirect,  	--'-overwrite_original "-RegionName\>PersonInImage"',  	'-overwrite_original "-RegionName\>RegionPersonDisplayName"',  	'-config  '..cfgFile..' -overwrite_original "-regioninfomp\<MyRegionMP"'}  			local progressScope = LrProgressScope{  				title = "Write Picasa Faces to Tags", 				caption = "Updateting " .. total .. " photos." , 			}			 			progressScope:setCancelable( true )   	local parrent  	catalog:withWriteAccessDo("Create parrent keyword", function ()       	parrent = catalog:createKeyword("names", {}, false, nil, true)  		--logger:debug("parrent keyword created: " .. tostring(parrent))     end)      	for completed, photo in ipairs(photos) do   		progressScope:setPortionComplete(completed, total)   		progressScope:setCaption("Updated " .. tostring(completed) .. " of " .. tostring(total) .. " photos")   		if progressScope:isCanceled() then progressScope:done() break end  	 	local path = photo:getRawMetadata('path') 	 	logger:debug(path) -- write filename to debug log 	 	for i,exifArg in ipairs(exifArgs) do 	 		local exeCmd ='"' .. exeFile.." "..exifArg.." "..path .. '"'  			local status = LrTasks.execute(exeCmd)  			if io.open(redirect):read() == nil then break end --check is there any names in the file  			--logger:debug(exeCmd) 		 	if status ~= 0 		 		then logger:debug("Error "..exeCmd) 		 			progressScope:done() 	 		end 	 	end  	 	for name in  io.lines(redirect) do 	 		if name ~= nil then -- check is there any pleople on photo	 	   			logger:debug(name) 	   			catalog:withWriteAccessDo("Adding name keywords", function ()   	   				local keyword = catalog:createKeyword(name, {}, true, parrent, true) 	   				logger:debug("keyword created: " .. tostring(keyword)) 	   				photo:addKeyword(keyword) 	   				--photo:setRawMetadata('personShown', keyword) --doesn't work 		 			logger:debug("keyword added: " .. name) 		 		end) 	 		end 	 	end 	end 	 progressScope:done() end  LrTasks.startAsyncTask(faceToTag)  

Все именные теги хранятся в иерархической структуре внутри тега «names», программы которые не работают с xmp схемой лайтрума будут видеть их просто плоским списком + тег «names». Для работы плагину в папку надо положить exiftool.exe и его конфиг. Все скопом можно скачать с github

Плагин работает, но у него есть недостатки:

  • Не удается записать PersonInImage, если его писать exiftool он перезаписывается лайтрумом, а записать его непосредственно через SDK не получается по непонятным причинам. photo:setRawMetadata(‘personShown’, keyword) вылетает с ошибкой. Можно разнести это в две разных кнопки и перечитывать метаданные вручную, но это тоже некрасиво.
  • работает медленно примерно 1 секунда на фото, при большем архиве это проблема, возможно если переписать через cookbooks.adobe.com/post_ExifTool___making_it_scream_-19501.html будет быстрее.
  • Поскольку плагин пишет одновременно и через exiftool и через lightroom SDK по окончании работы регулярно возникает конфликт метаданных, и их надо сохранять вручную Ctrl+s. Если кто-то знает как заставить лайтрум писать и читать метаданные програмно — отпишитесь. Я пока придумал вариант только с эмуляцией хотеев lrBind но это не красиво.
  • Это уже проблема не плагина, всей системы, если протегировать рав, а после этого его экспортировать, информация о лицах теряется, но теги сохраняются, а это уже профит.

ЗЫ. Я смог найти два плагина которые делают _почти_ тоже самое, берут лица из пикасы и пишут их в теги, но все они берут лица только из picasa.ini и не работают с лицами записанными в XMP.

ссылка на оригинал статьи http://habrahabr.ru/post/193946/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *