Jak mogę zmienić nazwę zdjęć, biorąc pod uwagę dane EXIF?

14

Powiedzmy, że mam kilka zdjęć, wszystkie z poprawnymi informacjami EXIF, a zdjęcia są losowo nazywane (z powodu mojego problemu). Mam mały program o nazwie, jheadktóry daje mi następujące wyjście:

$ 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

Teraz muszę zmienić nazwę wszystkich zdjęć w folderze w następnym formacie:

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

Gdzie mniejsza liczba będzie starszym obrazem, a maksymalna nowszym.

Nie jestem zbyt dobry w pisaniu skryptów, więc proszę o pomoc.

Myślę, że wystarczy skrypt bash, ale jeśli czujesz się bardziej komfortowo, możesz napisać skrypt w języku Python.

Pomyślałem w czymś takim jak:

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

ale nie wiem, jak to zrobić dla wszystkich plików jednocześnie.

Tomas
źródło
Nie rozumiem zdania podrzędnego / maksymalnego i poprzedniego przykładu. Czy możesz to wyjaśnić?
maxschlepzig
Te niewielkie / maksymalne nazwy plików są o jeden krok w górę. Ponieważ kiedy mam nazwy plików w sposób, który reprezentują, które są starsze i nowsze, mogę łatwo zmienić nazwę na 001.jpg, 002.jpg itp. Za pomocą innego programu, chociaż lepiej byłoby zrobić to za pomocą innego skryptu.
Tomas
„Tabela zmiany nazwy” ls *.JPG | wc > renameto I musiałbym użyć zmiany nazwy skryptu na XXX.JPG
Tomas
Sory, nie wc, zapomniałem tego, który zamówił po imieniu.
Tomas
Polecenie jest sortowane.
Tomas

Odpowiedzi:

10

Możesz to zrobić dla wszystkich plików za pomocą pętli for (w powłoce / w skrypcie powłoki):

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

To tylko bardzo podstawowy zarys. Usuń echopo sprawdzeniu, czy wszystko działa zgodnie z oczekiwaniami.

maxschlepzig
źródło
Dzięki. A teraz daj mi grep File date : 2011:02:03 20:25:09. Jak mogę filtrować tylko drugą kolumnę?
Tomas
Więc zmieniam nazwę pliku tylko na 20:25:09
Tomas
@Tomas, zaktualizowałem odpowiedź - jeśli naprawdę chcesz tylko czasu (co wtedy z kolizjami?), Musisz oczywiście zmodyfikować wyrażenie regularne.
maxschlepzig
j=`jhead "$i" | grep date | sed 's/.* //'`.jpgzamiast?
frabjous
@maxshlepzig: Dzięki, przegapiłeś 'przed `` .jpg``
Tomas
28

Właśnie dowiedziałem się , że głupek może zrobić to wszystko za Ciebie! :)

jhead -autorot -nf%Y-%m-%d_%H-%M-%S *.jpg
Kevin
źródło
7
Tak! Ale uważaj na ten konkretny ciąg formatu, ponieważ nada on dwóm plikom w tej samej sekundzie tę samą nazwę. %i(lub %03ikonkretnie) poda numer porządkowy zgodnie z pierwotnym pytaniem. Łączenie obu może nie być złym pomysłem.
mattdm
Aby odpowiedzieć na komentarz mattdm - instrukcja „jhead” sugeruje, że automatycznie doda dodatkową liczbę lub literę (1,2,3 lub a, b, c itd.) Do duplikatów (identyczny znacznik czasu).
John U
0

Jeśli ktoś potrzebuje bardziej złożonej zmiany nazwy, na przykład w celu włączenia kluczowych wartości ekspozycji do nazwy pliku, oto mój mały skrypt. To zmienia nazwy plików JPEG na coś takiego: 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
źródło
0

Podobał mi się kod opublikowany przez maxschlepzig, ale mimo to miałem problemy z wyjściem.

Problem polegał na spacji w wynikowej nazwie pliku (między ciągiem daty a ciągiem czasu). Choć jest banalny dla każdego, kto korzysta z GUI, sprawia, że ​​obsługa plików w wierszu poleceń jest nieco trudniejsza.

Tutaj polecenie „sed” zostało zasadniczo zmienione na cztery oddzielne operacje „sed” na korzyść poprzedniego argumentu monolitycznego. Aby dopasować się do tego, poniżej zmienia również plik na normalne uprawnienia 644.

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
źródło
2
Witamy w Stack Exchange. (1) Rozumiem sedcałkiem dobrze, więc w zasadzie rozumiem, co próbujesz zrobić. Ale naszym celem w Stack Exchange nie jest rozdawanie kanapek z rybami ani pisanie tysięcy jednorazowych rozwiązań na trywialnie różne pytania; naszym celem jest nauczenie ludzi, jak łowić ryby (tj. nauczyć ludzi, w tym obecnego pytającego i przyszłych czytelników, jak rozwiązywać własne problemy). W tym celu Twoja odpowiedź byłaby lepsza, gdybyś wyjaśnił, co próbujesz. (Nie odpowiadaj w komentarzach; edytuj swoją odpowiedź, aby była jaśniejsza.)… (Ciąg dalszy)
G-Man mówi „Przywróć Monikę”
(Ciąg dalszy)… (2) Twoje polecenie kończy się niepowodzeniem, ponieważ cytaty nie są zrównoważone. Nie publikuj nieprzetestowanego kodu.
G-Man mówi „Przywróć Monikę”
0

Ponieważ łatwiej jest sobie z tym poradzić (imho), napisałem sobie skrypt 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
}

Co to robi?

  1. Iteruje po wszystkich plikach przekazanych jako parametry (np *.JPG.).

    Sprawdziłem, czy poprawnie obsługuje pliki RAW i filmy. Powinien działać ze wszystkim, z czym exiftoolmożna sobie poradzić.

  2. Nic nie robi, jeśli plik

    • nie istnieje,
    • jest katalogiem,
    • nie ma daty EXIF, lub
    • exiftool zgłasza błąd lub
    • plik z docelową nazwą pliku już istnieje.

To czyni go dość solidnym. W szczególności żadne pliki nie mogą zniknąć (po cichu), jak ma to miejsce w przypadku niektórych innych odpowiedzi.

Raphael
źródło
0

exiv2 byłaby alternatywą dla manipulacji, która pozwala na bardzo prostą składnię:

exiv2 to program do odczytu i zapisu metadanych Exif, IPTC, XMP i komentarzy graficznych oraz może odczytywać wiele tagów makernote dostawców. Program opcjonalnie konwertuje między tagami Exif, właściwościami XMP i zestawami danych IPTC

Więc zmieniłoby to nazwę wszystkich plików JPEG w bieżącym folderze:

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

Jeśli chcesz również dodać informacje geograficzne, możesz użyć exivtool:

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

Podoba mi się rozwiązanie @ Kevin, ponieważ chciałem również zachować oryginalną nazwę (unikając problemu ze zdjęciami wykonanymi w tej samej sekundzie), oto moje rozwiązanie:

for i in *.JPG; do jhead -nf%Y%m%d_%H%M%S_"$(basename "$i" .JPG)" "$i" ; done
Giorgio Borgonovo
źródło
0

Pierwszy post przez początkującego ... Pierwszy skrypt bash ... Podobało mi się powyższe rozwiązanie Libor / HalosGhost, ponieważ zawierało więcej szczegółów w zmianie nazwy. Ale po przetestowaniu zduplikowane nazwy plików doprowadziłyby do utraty plików. Dodałem więc znacznik licznika na końcu nazwy pliku, aby ułatwić korzystanie z niego i zapobiegać kolizjom. Jestem pewien, że ktoś tutaj może to poprawić, ale pomyślał, że to może pomóc.

Przepraszam, że opublikowałem kod. Mam problemy z interfejsem, ale jeśli ktoś mógłby wskazać mi poprawny sposób robienia rzeczy, byłoby świetnie.

#!/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
źródło
Dziękuję Stephen za edycję. Próbuję się nauczyć
jgroezin,