¿Cómo puedo cambiar el nombre de las fotos, dados los datos EXIF?

14

Digamos que tengo un montón de fotos, todas con información EXIF ​​correcta, y las fotos se nombran al azar (debido a un problema que tuve). Tengo un pequeño programa llamado jheadque me da el siguiente resultado:

$ 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

Ahora necesito cambiar el nombre de todas las fotos en la carpeta en el siguiente formato:

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

Donde el número menor sería la imagen más antigua y el máximo el más nuevo.

No soy tan bueno escribiendo guiones, así que estoy pidiendo ayuda.

Creo que un script bash es suficiente, pero si te sientes más cómodo, puedes escribir un script python.

Pensé en algo como:

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

pero no sé cómo hacer eso para todos los archivos a la vez.

Tomás
fuente
No entiendo la oración menor / máxima y el ejemplo anterior. ¿Podrías aclarar esto?
maxschlepzig
Ese nombre de archivo menor / máximo es un paso adelante. Porque, una vez que tengo los archivos nombrados de una manera que representan los que son más antiguos y más nuevos, puedo cambiar fácilmente el nombre a 001.jpg, 002.jpg, etc. con otro programa, aunque sería mejor hacerlo con otro script.
Tomás
La "tabla de cambio de nombre" sería ls *.JPG | wc > renameY luego tendría que usar un cambio de nombre de script a XXX.JPG
Tomas
Sory, no lo es wc, olvidé el que ordenar por nombre.
Tomás
El comando es ordenar.
Tomás

Respuestas:

10

Puede hacerlo para todos los archivos usando un bucle for (en el shell / en un 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 es solo un esquema muy básico. Elimine echocuando haya verificado que todo funciona como se esperaba.

maxschlepzig
fuente
Gracias. Ahora, grep me daría File date : 2011:02:03 20:25:09. ¿Cómo puedo filtrar solo la segunda columna?
Tomás
Así que solo renombro el archivo como 20:25:09
Tomás
@Tomas, actualizó la respuesta: si realmente solo desea el tiempo (¿qué pasa con las colisiones, entonces?), Tiene que modificar la expresión regular, por supuesto.
maxschlepzig
j=`jhead "$i" | grep date | sed 's/.* //'`.jpg¿en lugar?
frabjous
@maxshlepzig: Gracias, te perdiste un 'antes `` .jpg`
Tomas
28

Acaba de enterarse de aquí que jhead puede hacerlo todo para usted! :)

jhead -autorot -nf%Y-%m-%d_%H-%M-%S *.jpg
Kevin
fuente
77
¡Si! Pero tenga cuidado con esa cadena de formato en particular, porque le dará el mismo nombre a dos archivos tomados en el mismo segundo. %i(o %03i, específicamente) dará un número de secuencia, según lo solicitado en la pregunta original. Combinar ambos podría no ser una mala idea.
mattdm
Solo para responder al comentario de mattdm: el manual '' jhead '' sugiere que agregará un número o letra adicional (1,2,3 o a, b, c, etc.) a los duplicados (marca de tiempo idéntica) automáticamente.
John U
0

Si alguien necesita un cambio de nombre más complejo, por ejemplo, para incluir valores de exposición clave en el nombre del archivo, aquí está mi pequeño script. Se cambia el nombre de los archivos JPEG a algo como esto: 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
Libor
fuente
0

Me gustó el código publicado por maxschlepzig pero no obstante tuve dificultades con la salida.

El problema era el espacio en el nombre de archivo resultante (entre la cadena de fecha y la cadena de tiempo). Si bien es trivial para cualquiera que use una GUI, dificulta el manejo de archivos en la línea de comandos.

Aquí el comando 'sed' se ha cambiado sustancialmente a cuatro operaciones 'sed' separadas a favor del argumento monolítico anterior. Para adaptarme a mí mismo, lo siguiente también cambia el archivo a los permisos 644 normales.

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
endoterma
fuente
2
Bienvenido a Stack Exchange. (1) Entiendo sedbastante bien, así que básicamente entiendo lo que estás tratando de hacer. Pero nuestro objetivo en Stack Exchange no es entregar sándwiches de pescado o escribir miles de soluciones únicas para preguntas trivialmente distintas; Nuestro objetivo es enseñar a las personas cómo pescar (es decir, enseñarles a las personas, incluidos el interrogador actual y los futuros lectores, cómo resolver sus propios problemas). Con ese fin, su respuesta sería mejor si explicara lo que está haciendo. (Por favor, no responde en los comentarios; editar su respuesta para que sea más claro.) ... (Continuación)
G-hombre dice 'Restablecer Mónica'
(Cont.) ... (2) Su comando falla porque las comillas no están equilibradas. Por favor, no publique código no probado.
G-Man dice 'reinstalar a Mónica'
0

Debido a que es más fácil tratar (en mi humilde opinión), escribí un script de 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
}

¿Qué hace esto?

  1. Itera sobre todos los archivos pasados ​​como parámetros (por ejemplo *.JPG).

    Verifiqué que maneja archivos RAW y videos correctamente. Debería funcionar con todo lo que exiftoolpueda manejar.

  2. No hace nada si un archivo

    • no existe,
    • es un directorio,
    • no tiene una fecha EXIF, o
    • exiftool informa un error, o
    • ya existe un archivo con el nombre del archivo de destino.

Eso lo hace bastante robusto. En particular, ningún archivo puede desaparecer (en silencio) como es el caso con algunas de las otras respuestas.

Rafael
fuente
0

exiv2 sería una alternativa para manipular, que permite una sintaxis muy simple:

exiv2 es un programa para leer y escribir comentarios de imagen y metadatos Exif, IPTC, XMP y puede leer muchas etiquetas de fabricante de notas. El programa opcionalmente convierte entre etiquetas Exif, propiedades XMP y conjuntos de datos IPTC

Entonces, esto cambiaría el nombre de todos los archivos JPEG en la carpeta actual:

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

Si también desea agregar información geográfica, puede usar exivtool:

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

Me gusta la solución de @ Kevin, ya que también quería mantener el nombre original (evitando problemas con las imágenes tomadas en el mismo segundo), aquí está mi solución:

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

Primera publicación por novato ... Primer script bash ... Me gustó la solución Libor / HalosGhost anterior, ya que incluía más detalles en el cambio de nombre. Pero después de la prueba, los nombres de archivos duplicados terminarían perdiendo archivos. Así que agregué una etiqueta de contador al final del nombre de archivo para una fácil referencia y prevención de colisiones. Estoy seguro de que alguien aquí puede mejorar esto, pero pensé que podría ayudar.

Mis disculpas por publicar el código. Tengo dificultades con la interfaz, pero si alguien pudiera indicarme cómo corregir las cosas, sería genial.

#!/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
jgroezin
fuente
Gracias Stephen por la edición. Tratando de aprender
jgroezin