Here are some simple scripts to manage photos from command line. Using them is not as comfortable as having a GUI, but they work better than digiKam, Geeqie, F-Spot, or Shotwell for me. The scripts are file-centric: photos are reasonably ordered files in directories as albums, with embedded XMP metadata.
|
Contents
|
The best choices so far: ImageMagick for batch conversion. Gimp for graphical image editing. ExifTool for XMP tag (metadata) management. Eye of Gnome for image viewing. Emacs for copying, renaming, batch processing, and everything else.
For metadata there are two standards worth mentioning: Exif and XMP. Exif is a specification for the image metadata format used by most digital cameras. The specification uses the existing JPEG and TIFF file formats, with the addition of specific metadata tags. Its specification is here. The standard is very limited in scope and features, for example it uses ASCII for the Artist field. XMP is a broader standard for processing and storing standardized information relating to the contents of a file. The specification is here. The specification text is nonfree, but this can be fixed by writing a new, compatible one when needed. The specification itself is well thought out.
I use only the following fields in XMP: dc:creator, dc:date, dc:title, dc:coverage, dc:subject, and dc:description.
exiftran -ai *.jpg
It also modifies the EXIF tag.
If you want to use XMP for all your metadata needs, but your camera stores only EXIF metadata into JPEGs, you need to convert EXIF metadata to XMP. Here is a snippet converting the time when the photo was taken.
#!/bin/bash # # photos-exif2xmp # for f in *.jpg do dcdate=`exiftool -s -s -s -DateTimeOriginal -d "%Y-%m-%dT%H:%M:%S" $f` exiftool -xmp-dc:date=$dcdate $f exiftool -xmp-dc:date $f done
This script is useful when creating new album. It sorts all photographs by the content of the XMP dc:date field. In existing albums, the order is usually only almost following the creation date. Some photographs needs to be swapped, because it is thematically better. So this script should not be used when adding new photographs to an existing album, which was already sorted by creation date and then the order was manually tweaked.
#!/usr/bin/ruby
#
# photos-sort-by-creation-date
#
# Get all the files sorted by their XMP DublinCore (creation) dates.
files = []
Dir.glob("*.jpg*").sort().each do |file|
files += [[file, `exiftool -s -s -s -xmp-dc:date #{file}`]]
puts "Reading #{file}"
end
files = files.sort_by { |file| file[1] }
# Get the number of digits needed to express all new filenames.
place_count = 0
max_number = files.length - 1
while max_number > 0
max_number /= 10
place_count += 1
end
# If the newname file already exists, move it away.
# Write the new name into files if the file is there.
def backup(filename, files)
if File.exists?(filename)
backupname = "#{filename}.old"
backup(backupname, files) if File.exists?(backupname)
puts "Backuping #{filename} to #{backupname}"
File.rename(filename, backupname)
findex = files.index { |f| f[0] == backupname }
files[findex][0] = backupname unless findex.nil?
end
end
# Rename the files to follow the creation date order.
index = 0
while not files.empty?
file = files.first
newname = format("%0#{place_count}d.jpg", index)
index += 1
next if file[0] == newname
backup(newname, files)
puts "Renaming #{file[0]} to #{newname}: #{file[1]}"
File.rename(file[0], newname)
files.delete_at(0)
end
This script is useful to stack a sequence of photographs which contains "holes" after deleting some of them. For example when photographs are from 00.jpg to 10.jpg with 04.jpg, 06.jpg and 07.jpg missing, and you want them to be from 00.jpg to 07.jpg. The script renames 05.jpg to 04.jpg, 08.jpg to 05.jpg, 09.jpg to 06.jpg, and 10.jpg to 07.jpg.
#!/usr/bin/ruby
#
# photos-stack
#
files = Dir.glob("*.jpg*").sort()
# Get the number of digits needed to express all new filenames.
place_count = 0
max_number = files.length - 1
while max_number > 0
max_number /= 10
place_count += 1
end
# If the newname file already exists, move it away.
# Write the new name into files if the file is there.
def backup(filename, files)
if File.exists?(filename)
backupname = "#{filename}.old"
backup(backupname, files) if File.exists?(backupname)
puts "Backuping #{filename} to #{backupname}"
File.rename(filename, backupname)
findex = files.index { |f| f == backupname }
files[findex] = backupname unless findex.nil?
end
end
# Rename the files to follow the creation date order.
index = 0
while not files.empty?
file = files.first
newname = format("%0#{place_count}d.jpg", index)
index += 1
next if file == newname
backup(newname, files)
puts "Renaming #{file} to #{newname}"
File.rename(file, newname)
files.delete_at(0)
end