Classifique fotos por dados do EXIF ​​+ PHP

Quero compartilhar minha experiência na classificação de fotos usando um script em PHP
Chega um momento em que não há muitas fotografias, mas catastroficamente muitas.

Antecedentes


Um dia, decidi classificar todo o meu arquivo de fotos digitais, acumulado por 20 anos, e percebi que, durante todo esse tempo, havia acumulado 112.000 fotos em 435 gigabytes.

Além disso, alguns deles são mais ou menos classificados, por exemplo, fotos de uma câmera SLR, por pastas com nomes e datas, enquanto a outra parte das fotos que foram importadas do iphone / android não são nomeadas e classificadas, geralmente é apenas uma pasta gigante 10 gigabytes, com alguns milhares de arquivos dentro, e é uma pena excluí-los e resolvê-los.

Comecei a procurar ferramentas de classificação automática e percebi que todos os bons serviços, como o Picasa, já haviam sido comprados e fechados pelo Google, é claro que você pode fazer o upload de tudo para eles no Google. Fotos, no entanto, existem problemas, nem todos são pesquisados ​​e geralmente metade das funções estão ausentes. para estar no Picasa e se você ainda estiver preocupado com o reconhecimento e o uso de suas fotos, o upload para a Web não é o seu caminho.

Como resultado, foi decidido escrever um pequeno script que classificaria tudo, a princípio pensei em um script de shell, mas, percebendo que seria necessário, o EXIF ​​decidiu retornar ao bom e velho PHP.

Tarefa nº 1 - Expandir todos os arquivos por datas


Primeiro, segui o caminho mais simples, pegue todos os arquivos, veja a data de criação e espalhe pelos caminhos aninhados:

$file_list = $files->getDirContents($config['photos.unsorted']); foreach ($file_list as $key => $value) { moveImageFile($value); } function moveImageFile($filename) { $dt= new DateTime(); $dt->setTimestamp(filectime($filename)); $start_path = $this->config['photos']; $year = $start_path."\Year".$dt->format('Y'); if (!is_dir($year)) mkdir($year); $month = $year."\\".$dt->format('Ym-F'); if (!is_dir($month)) mkdir($month); $path = $month."\\".$dt->format('Ym-d'); if (!is_dir($path)) mkdir($path); } $full_path = getUniqueFilename($filename, $path, $dt, 0); copy($filename, $full_path); 

Houve vários problemas:

  • Alguns arquivos tiveram uma data de criação incorreta
  • Se você copiar, um novo arquivo será criado na data atual.
  • Os arquivos podem ter duplicados com o mesmo horário de criação

Problema número 2 - obtenha a data do Exif


Foi decidido pegar a data do EXIF, renomear e tocar nos arquivos para definir a data do exif e também verificar se há duplicatas nos arquivos usando o md5.

Em princípio, o PHP já possui uma extensão exif no conjunto de bibliotecas, então nada de sobrenatural estava previsto

  $dt = DateTime::createFromFormat('Y:m:d H:i:s', $exif['DateTime']); $start_path = $this->config['photos.exif']; $is_exif = true; if (md5_file($filename) == md5_file($full_path)) return false; rename($filename, $full_path); touch($full_path, $dt->getTimestamp()); 

Tudo ficaria bem, 500 gigabytes de fotos foram classificadas e removidas das duplicatas em algumas horas, mas então lembrei das pastas antigas que continham o nome da região onde a sessão de fotos era realizada e pensei: por que não obter os nomes das cidades dos dados geográficos?

Tarefa №3 - Países, cidades e regiões de dados geográficos EXIF


As coordenadas são fáceis de encontrar nos arquivos, elas estão no Exif em GPSLongitude e GPSLatitude, mas não devemos esquecer que elas são armazenadas em graus, minutos e segundos; portanto, você precisa usar as funções para converter coordenadas em decimais.

 function getGps($exifCoord, $hemi) { $degrees = count($exifCoord) > 0 ? $this->gps2Num($exifCoord[0]) : 0; $minutes = count($exifCoord) > 1 ? $this->gps2Num($exifCoord[1]) : 0; $seconds = count($exifCoord) > 2 ? $this->gps2Num($exifCoord[2]) : 0; $flip = ($hemi == 'W' or $hemi == 'S') ? -1 : 1; return $flip * ($degrees + $minutes / 60 + $seconds / 3600); } 

A segunda pergunta, o que fazer com as coordenadas, como obter o nome da cidade?
O geocoder da Yandex vem em socorro, mas tenha cuidado com os limites e termos de uso.

 $url = "https://geocode-maps.yandex.ru/1.x/"; $apikey = require('../config/apikey.php'); $json = array( 'geocode' => $lon.",".$lat, 'kind' => 'locality', 'apikey' => $apikey, 'results' =>'1', 'skip' => '0', 'format' => 'json' ); $response = file_get_contents($url."?".http_build_query($json)); 

Para não matar o Yandex com milhões de consultas, armazenamos em cache os dados no MySql, arredondando as coordenadas para 3 casas decimais, isto é, 43.161 - 19.182 é suficiente para determinar a cidade e, para 110.000 fotos, obtive apenas 1.500 geometrias.

A aparência das pastas é algo como isto:

  • D: \ photos \ photos_exif \ Ano2019 \ 2019-09-setembro \ 2019-09-23-Bósnia e Herzegovina, República Srpska, Foca \
  • D: \ photos \ photos_exif \ Ano2019 \ 2019-08-agosto \ 2019-08-25-Albânia, região de Durres, Kruja \
  • D: \ photos \ photos_exif \ Year2018 \ 2018-10-October \ 2018-10-06-Rússia, região de Moscou, Balashikha \

Em vez de uma conclusão


De fato, se você usar este produto, poderá fazê-lo por meses. Levei um dia para escrever um script e otimizar o armazenamento de fotos.

Dos planos: adicionar geotags às fotos existentes, reorganizar o arquivo atual de fotos, encontrar duplicatas entre as imagens cortadas.

Todos os arquivos do projeto estão disponíveis no GitHub

Não me bata com força, este é o meu primeiro projeto de código aberto completamente, se algo for postado ou gravado incorretamente, diga-me, e sim, agora tudo está preso no tempo de execução do Windows com a codificação 1251.

Source: https://habr.com/ru/post/pt470009/


All Articles