Como posso renomear fotos, dados os dados EXIF?


14

Digamos que tenho um monte de fotos, todas com informações EXIF ​​corretas, e as fotos são nomeadas aleatoriamente (por causa de um problema que tive). Eu tenho um pequeno programa chamado jheadque me dá a saída abaixo:

$ jhead IMG_9563.JPG

File name    : IMG_9563.JPG
File size    : 638908 bytes
File date    : 2011:02:03 20:25:09
Camera make  : Canon
Camera model : Canon PowerShot SX210 IS
Date/Time    : 2011:02:03 20:20:24
Resolution   : 1500 x 2000
Flash used   : Yes (manual)
Focal length :  5.0mm  (35mm equivalent: 29mm)
CCD width    : 6.17mm
Exposure time: 0.0080 s  (1/125)
Aperture     : f/3.1
Focus dist.  : 0.29m
ISO equiv.   : 125
Exposure bias: -1.67
Whitebalance : Manual
Light Source : Daylight
Metering Mode: pattern
Exposure Mode: Manual

Agora preciso renomear todas as fotos na pasta no próximo formato:

001.JPG
002.JPG
003.JPG
...

Onde o número menor seria a imagem mais antiga e o máximo a mais nova.

Eu não sou tão bom em scripts, por isso estou pedindo ajuda.

Eu acho que um script bash é suficiente, mas se você se sentir mais confortável, poderá escrever um script python.

Eu pensei em algo como:

$ mv IMG_9563.JPG `jhead IMG_9563.JPG | grep date`

mas não sei como fazer isso para todos os arquivos de uma só vez.


Não entendo a sentença menor / máxima e o exemplo anterior. Você poderia esclarecer isso?
maxschlepzig

Esse nome de arquivo secundário / máximo é um passo à frente. Porque, depois que os arquivos são nomeados de uma maneira que representam os mais antigos e os mais recentes, posso renomear facilmente para 001.jpg, 002.jpg etc. com outro programa, embora seja melhor fazer isso com outro script.
Tomas

A "tabela de renomeação" seria ls *.JPG | wc > renameE então eu teria que usar um script renomeado para XXX.JPG
Tomas

Sory, não é wc, eu esqueci o que pedir pelo nome.
Tomas

O comando é ordenado.
Tomas

Respostas:


10

Você pode fazer isso para todos os arquivos usando um loop for (no shell / em um script de shell):

for i in *.JPG; do
  j=`jhead "$i" | grep date | sed 's/^File date[^:]\+: \(.\+\)$/\1/'`.jpg
  echo mv -i "$i" "$j"
done

Este é apenas um esboço muito básico. Exclua echoquando tiver verificado que tudo funciona conforme o esperado.


Obrigado. Agora, grep não me dará File date : 2011:02:03 20:25:09. Como posso filtrar apenas a segunda coluna?
Tomas

Então, eu apenas renomeio o arquivo como 20:25:09
Tomas

@ Tomas, atualizou a resposta - se você realmente quer apenas o tempo (e as colisões, então?), É necessário modificar a expressão regular, é claro.
maxschlepzig

j=`jhead "$i" | grep date | sed 's/.* //'`.jpgem vez de?
Frabjous

@maxshlepzig: Obrigado, você perdeu um 'antes de `` .jpg` '
Tomas

28

Acabei de descobrir aqui que o jhead pode fazer tudo por você! :)

jhead -autorot -nf%Y-%m-%d_%H-%M-%S *.jpg

7
Sim! Mas tenha cuidado com essa sequência de formato específica, porque ela dará a dois arquivos tirados no mesmo segundo o mesmo nome. %i(ou %03i, especificamente) fornecerá um número de sequência, conforme solicitado na pergunta original. Combinar ambos pode não ser uma má ideia.
precisa saber é o seguinte

Apenas para responder ao comentário de mattdm - o manual '' jhead '' sugere que ele acrescente um número ou letra extra (1,2,3 ou a, b, c etc.) às duplicatas (registro de data e hora idêntico) automaticamente.
John L

0

Se alguém precisar renomear mais complexo, por exemplo, para incluir os principais valores de exposição no nome do arquivo, aqui está meu pequeno script. Ele renomeia os arquivos JPEG para algo como isto: NightSky_2014.08.27_22.30.05_NX20_F2.8_f20.0mm_20s_ISO800.jpg.

#!/bin/bash

for s in *.jpg *.JPG
do
 echo $s
 x=`jhead "$s" | \
 awk 'BEGIN { cmt=""; }
/Camera model/   { c=$4$5$6;} 
/Exposure time:/ { e=$3; 
                   if (e==int(e)) e=int(e); 
                   if (e<1) {e=int(0.5+1/e); e="1T" e "s";} else { e=e "s"; } 
                 }
/ISO equiv./     { iso="ISO" $4; } 
/Focal length/   { f="f" $4; } 
/Date.Time /     { d=$3 "_" $4; gsub(":",".",d); }
/Aperture /      { ap=$3; gsub("f/","F",ap); } 
/Comment  /      { cmt=$3 "_"; }
END { print cmt d "_" c "_" ap "_" f "_" e "_" iso ".jpg"; }'`

 echo mv $s $x
 mv $s $x
 echo =====
done

0

Gostei do código postado por maxschlepzig, mas ainda assim tive dificuldades com a saída.

O problema foi o espaço no nome do arquivo resultante (entre a sequência de datas e a sequência de horas). Embora seja trivial para quem usa uma GUI, torna o tratamento de arquivos na linha de comando um pouco mais difícil.

Aqui o comando 'sed' foi substancialmente alterado para quatro operações 'sed' separadas em favor do argumento monolítico anterior. Para me adequar, o seguinte também altera o arquivo para permissões 644 normais.

for i in *.JPG ; do
  chmod 644 $i
  j=`jhead "$i" | grep ^Date/Time | sed -e 's/^Date\/Time[ ]\+: //;s/:/-/g;s/ /_/g':/-/g;s/ /_/g'`.jpg
  echo mv -i "$i" "$j"
  # mv -i "$i" "$j"
done

2
Bem-vindo ao Stack Exchange. (1) Eu entendo sedmuito bem, então basicamente entendo o que você está tentando fazer. Mas nosso objetivo no Stack Exchange não é distribuir sanduíches de peixe ou escrever milhares de soluções pontuais para perguntas trivialmente distintas; nosso objetivo é ensinar as pessoas a pescar (ou seja, ensinar as pessoas, incluindo o questionador atual e futuros leitores, a resolver seus próprios problemas). Para esse fim, sua resposta seria melhor se você explicasse o que está se esforçando. (Por favor, não responda nos comentários; edite sua resposta para torná-la mais clara.)… (Continua)
G-Man diz 'Reinstate Monica'

(Continua)… (2) Seu comando falha porque as aspas não são equilibradas. Por favor, não publique código não testado.
G-Man diz 'Reinstate Monica'

0

Como é mais fácil lidar com (imho), escrevi para mim mesmo um script Ruby:

require 'fileutils'

ARGV.each { |file|
  if File.exist?(file) && !File.directory?(file)
    exif = `exiftool "#{file}" | grep -E "(Error|Create Date)"`
    if exif.strip.size > 0
      exif = exif.split("\n")[0].split(/\s+/)
      if exif[0] != "Error"
        # Change the target format here
        filename = exif[3].gsub(":", "-") + " " + 
                   exif[4].gsub(":", ".") + 
                   File.extname(file)
        if filename != file && !File.exist?(filename)
          FileUtils::mv(file, File.dirname(file) + "/" + filename)
        end
      end
    end
  end
}

O que isso faz?

  1. Repete todos os arquivos passados ​​como parâmetros (por exemplo *.JPG).

    Eu verifiquei se ele lida com arquivos e vídeos RAW corretamente. Deve funcionar com tudo o que exiftoolpode lidar.

  2. Não faz nada se um arquivo

    • não existe,
    • é um diretório,
    • não tem uma data EXIF, ou
    • exiftool relata um erro ou
    • um arquivo com o nome do arquivo de destino já existe.

Isso o torna bastante robusto. Em particular, nenhum arquivo pode desaparecer (silenciosamente), como é o caso de algumas das outras respostas.


0

exiv2 seria uma alternativa para manipulação, que permite uma sintaxe muito simples:

O exiv2 é um programa para ler e gravar metadados Exif, IPTC, XMP e comentários de imagens e pode ler muitas tags makernote de fornecedores. O programa opcionalmente converte entre tags Exif, propriedades XMP e conjuntos de dados IPTC

Portanto, isso renomeará todos os jpegs na pasta atual:

for i in *.JPG; do exiv2 -v -r '%Y%m%d.%H%M%S.:basename:' rename "$i"; done

Se você também deseja adicionar informações geográficas, pode usar exivtool:

exiftool '-filename<${gpslatitude;} ${gpslongitude} ${datetimeoriginal}' -d "%Y-%m-%d %H.%M.%S%%-c.%%e" *.JPG

0

Eu gosto da solução do @ Kevin, já que eu também queria manter o nome original (evitando problemas com imagens tiradas no mesmo segundo), eis a minha solução:

for i in *.JPG; do jhead -nf%Y%m%d_%H%M%S_"$(basename "$i" .JPG)" "$i" ; done

0

Primeira postagem do novato ... Primeiro script do bash ... Gostei da solução Libor / HalosGhost acima, pois inclui mais detalhes na renomeação. Mas, após o teste, nomes de arquivos duplicados acabariam perdendo arquivos. Então, adicionei uma tag de contador no final do nome do arquivo para facilitar a referência e a prevenção de colisões. Estou certo de que alguém aqui pode melhorar isso, mas achei que poderia ajudar.

Minhas desculpas por postar o código. Estou tendo dificuldades com a interface, mas se alguém pudesse me indicar uma maneira correta de fazer as coisas, isso seria ótimo.

#!/bin/bash

y=1
for s in *.jpg *.JPG
do
 echo $s
 x=`jhead "$s" | \
 awk 'BEGIN { cmt=""; }
/Camera model/   { c=$4$5$6;}
/Exposure time:/ { e=$3;
                   if (e==int(e)) e=int(e);
                   if (e<1) {e=int(0.5+1/e); e="1T" e "s";} else { e=e "s"; }
                 }
/ISO equiv./     { iso="ISO" $4; }
/Focal length/   { f="f" $4; }
/Date.Time /     { d=$3 "_" $4; gsub(":","",d); }
/Aperture /      { ap=$3; gsub("f/","F",ap); }
/Comment  /      { cmt=$3 "_"; }
END { print cmt d "_" c "_" ap "_" f "_" e "_" iso "_" ; }'`

 echo mv $s "$x${y}.jpg"
 mv $s "$x${y}.jpg"
 y=$((y+1))
 echo =====
done

Obrigado Stephen pela edição. Tentativa de aprender #
21419 jgroezin
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.