really i wish i would find some time and finish (or actually start) a python based front-end for the command utility exiv2. but unfortunately until now, there is just a bad first version which i have used so far …
film cameras doesn’t store metadata onto negatives (maybe there are some … ?). taking pictures is fun and processing them in darktable gives them the little extra. so i like to scan all my negatives in tiff format for post processing. but the tiff files than do not contain any metadata about camera, lens, settings, timestamps and so on – i like to have those data already in the tiff files! under linux there are several opportunities to store exif data into image files and the command line tool exiv2 is one. but applied to each image file dedicated exif data is not fun, doing it by hand and one-by-one. a kind of automatisation is needed to save me a lot lot time.
the python gui already helped a lot but till now it has a lot of drawbacks and bugs – starting slow and each image needs to be accessed alone – … doesn’t saved me time well! something else was need until an improved version is done.
what do i need is a quick solution to store certain exif data to a couple of image files in a bunch. i shot a film so mostly i need have about 36 images with the camera and lens data. for the moment i do not note camera settings or dates, when and how it was shooting. but i like to save a date and time with an increasing second on each photo to have the order of the shots. in addition i like to fall back on a kind of database where i have different cameras or lens data to be stored and not to think and type in again and again the information i need to have.
starting to write a bash script seamed to me the quickest solution and i already had done some other simple little gui helpers with yad dialogues. at the end i’m actually quite happy with the script i wrote. it contains now each function i liked to have. it even stores different camera and lens data. all the equipment i’m currently using. the usability of the script, i think, is also quite nice. as it is stored as a nautilus script in my system, i just need to select the image files that should obtain metadata and invoke the script from the nautilus menu. the gui opens and data can be selected or changed and entered which should end up in the files. the script is ready to use and contains in it data about camera and lenses – extensible for additional cameras and lenses.
work done – now i’m out and shooting! 8)
… and the script i’d like to share
#!/bin/bash
#-----------------------------------------------------------------------
# title: exiv2gui (micro)
#-----------------------------------------------------------------------
# version: 1.3.4
# author: rth.
# web: www.dev-used.de
# license: General Public License, Version 3 or newer
# http://www.gnu.org/licenses/gpl-3.0.html
#
# note: search for 'EDIT_HERE' if new
# camera/lens should be added
#
########################################################################
declare -A EQUIP_DB
##____CAMERA__DATA____________________________________<_EDIT_HERE_>_____
CAMERA="Ricoh GR1v!Konica Hexar AF!Fuji GW690 III!Nikon F100!Nikon F3/HP!Minolta X-700"
#-----------MAKE----------------------MODEL-------------------SERIAL--------
EQUIP_DB[CAM,0,0]="Ricoh"; EQUIP_DB[CAM,0,1]="GR1v"; EQUIP_DB[CAM,0,2]="521005067"
EQUIP_DB[CAM,1,0]="Konica"; EQUIP_DB[CAM,1,1]="Hexar AF"; EQUIP_DB[CAM,1,2]="0039000"
EQUIP_DB[CAM,2,0]="Fujifilm"; EQUIP_DB[CAM,2,1]="GW690 III"; EQUIP_DB[CAM,2,2]="5010082"
EQUIP_DB[CAM,3,0]="NIKON"; EQUIP_DB[CAM,3,1]="F100"; EQUIP_DB[CAM,3,2]="2086656"
EQUIP_DB[CAM,4,0]="NIKON JP."; EQUIP_DB[CAM,4,1]="F3/HP"; EQUIP_DB[CAM,4,2]="1540870"
EQUIP_DB[CAM,5,0]="Minolta"; EQUIP_DB[CAM,5,1]="X-700"; EQUIP_DB[CAM,5,2]="1195267"
##____LENS__DATA______________________________________<_EDIT_HERE_>_____
LENS="Ricoh GR1v!Konica Hexar AF!Fuji GW690 III"
#-------------MAKE-------------------MODEL----------------------SERIAL------
EQUIP_DB[LENS,0,0]="Ricoh"; EQUIP_DB[LENS,0,1]="GR1v"; EQUIP_DB[LENS,0,2]="521005067";
EQUIP_DB[LENS,1,0]="Konica"; EQUIP_DB[LENS,1,1]="Hexar AF"; EQUIP_DB[LENS,1,2]="0039000";
EQUIP_DB[LENS,2,0]="Fujifilm"; EQUIP_DB[LENS,2,1]="GW690 III"; EQUIP_DB[LENS,2,2]="5010082";
#-------------LENGTH----------------LENGTH35--------------MAXAPERTURE-------
EQUIP_DB[LENS,0,3]="280/10"; EQUIP_DB[LENS,0,4]="28"; EQUIP_DB[LENS,0,5]="F2.8";
EQUIP_DB[LENS,1,3]="350/10"; EQUIP_DB[LENS,1,4]="35"; EQUIP_DB[LENS,1,5]="F2.0";
EQUIP_DB[LENS,2,3]="900/10"; EQUIP_DB[LENS,2,4]="39"; EQUIP_DB[LENS,2,5]="F3.5";
########################################################################
START_PATH="$(pwd -P)"
##_______DATE__AND__TIME________________________________________________
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
NOW="$(date +"%d:%m:%Y %H:%M:%S")"
NOW_YEAR=${NOW:6:4}
NOW_MONTH=${NOW:3:2}
NOW_DAY=${NOW:0:2}
NOW_HOUR=${NOW:11:2}
NOW_MIN=${NOW:14:2}
NOW_SEC=${NOW:17:2}
#__FSTOP_LOOKUP__TABLE__________________________________________________
declare -A fstopTbl=( \
["- empty -"]="" \
["F1"]="10/10" \
["F1.4"]="14/10" \
["F2"]="20/10" \
["F2.8"]="28/10" \
["F4"]="40/10" \
["F5.6"]="56/10" \
["F8"]="80/10" \
["F11"]="110/10" \
["F16"]="160/10" \
["F22"]="220/10" \
["F32"]="320/10" \
["F45"]="450/10" \
)
#__SHUTTER_SPEED__LOOKUP__TABLE_________________________________________
declare -A shutspdTbl=( \
["- empty -"]="" \
["60"]="600/10" \
["30"]="300/10" \
["15"]="150/10" \
["8"]="80/10" \
["4"]="40/10" \
["2"]="20/10" \
["1"]="10/10" \
["1/2"]="10/20" \
["1/4"]="10/40" \
["1/8"]="10/80" \
["1/15"]="10/150" \
["1/30"]="10/300" \
["1/60"]="10/600" \
["1/125"]="10/1250" \
["1/250"]="10/2500" \
["1/5000"]="10/5000" \
["1/1000"]="10/10000" \
["1/2000"]="10/20000" \
["1/4000"]="10/40000" \
["1/8000"]="10/80000" \
)
########################################################################
##_______GUI__SETTINGS________________________________<_EDIT_HERE_>_____
FSTOP="^- empty -!F1!F1.4!F2!F2.8!F4!F5.6!F8!F11!F16!F22!F32!F45"
SHUSPD="^- empty -!60!30!15!8!4!2!1!1/2!1/4!1/8!1/15!1/30!1/60!1/125!1/250!\
1/500!1/1000!1/2000!1/4000!1/8000"
ISO="50!^100!200!400!800!1600!3200!6400"
CPYR="^CC BY-NC-ND 4.0!CC BY-ND 4.0!CC BY 4.0"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
ARTIST="rth."
COMM="thebalanceoflight.com"
DESCRP="\
Shot on AgfaPhoto APX 100!\
Shot on AgfaPhoto APX 400!\
Shot on AgfaPhoto CT precisa 100!\
Shot on AgfaPhoto Vista Plus 200!\
Shot on CineStill 800 Tungsten!\
Shot on CineStill BwXXX double-x!\
Shot on Fujifilm Fujichrome Provia 100F [RDP III]!\
Shot on Fujifilm Fujichrome Velvia 50!\
Shot on Fujifilm Fujicolor C200!\
Shot on Fujifilm Neopan 100 Acros!\
Shot on Fujifilm Superia 200!\
Shot on Fujifilm Superia 1600!\
Shot on Ilford Delta 100!\
Shot on Ilford Delta 400!\
Shot on Ilford Delta 3200!\
Shot on Ilford FP4 plus!\
Shot on Ilford HP5 plus!\
Shot on Ilford PAN F plus!\
Shot on Ilford XP2 Super!\
Shot on Kodak Ektar 100!\
Shot on Kodak Ektachrome E100!\
Shot on Kodak Gold 200!\
Shot on Kodak Gold Max 400!\
Shot on Kodak Ultramax 400!\
Shot on Kodak Portra 400!\
Shot on Kodak Portra 800!\
Shot on Kodak T-MAX 100!\
Shot on Kodak T-MAX 400!\
Shot on Kodak Tri-X 400!\
Shot on Kodak Ultramax 400\
"
########################################################################
##_______USERINTERFACE____GUI___________________________________________
DELIMITER="|"
SEPARATOR="!"
DATA=$(yad --title "exiv2gui (micro)" \
--width=480 --height=532 --on-top --center \
--button=gtk-cancel:1 \
--button="Ok":0 --buttons-layout=center \
--separator=${DELIMITER} --item-separator=${SEPARATOR} \
--text="\n\t<b>EXIF metadata batch processing ...\n</b>" \
--focus-field=0 \
--form --columns=1 \
--date-format="%d:%m:%Y %H:%M:%S" \
--day=$(echo $NOW_DAY | sed 's/^0*//') \
--month=$(echo $NOW_MONTH | sed 's/^0*//') \
--year=$(echo $NOW_YEAR | sed 's/^0*//') \
--field=" Camera: ":CB "$CAMERA" \
--field=" Lens: ":CB "$LENS" \
--field=" F-Stop Value: ":CB "$FSTOP" \
--field=" Shutter Speed: ":CB "$SHUSPD" \
--field=" ISO settings: ":CB "$ISO" \
--field=" Copyright: ":CB "$CPYR" \
--field=" Artist: ":T "$ARTIST" \
--field=" Date Original: ":DT "$NOW" \
--field=" Image Description: ":CBE "$DESCRP" \
--field=" Comment: ":TXT "$COMM" \
--field=" increment seconds on each image ":CHK "TRUE" \
)
## evaluate exit code
EXC=$?
## exit code of yad: [OK: 0] [CANCEL: 1] [TIMEOUT: 70] [ESC: 252]
if [ $EXC -eq 1 ] || [ $EXC -eq 70 ] || [ $EXC -eq 252 ]; then
echo -e "\n> file processing stopped (code: ${EXC})!\n"
exit 0
fi
##_______PREPARE__GUI__SETTINGS_________________________________________
IFS="$DELIMITER" read -ra GUISET <<< $DATA
echo
echo ">____received__gui__settings____"
for idx in ${!GUISET[@]}; do
echo " $idx: '${GUISET[idx]}'"
done
########################################################################
##_______PREPARE__CAMERAS__CONFIG_______________________________________
IFS="$SEPARATOR" read -ra CAMERA <<< $CAMERA
echo
echo ">____available__cameras____"
for idx in ${!CAMERA[@]}; do
echo " $idx: '${CAMERA[idx]}'"
done
#__find__settings__for__CAM___________________________<_EDIT_HERE_>_____
function get_camdata {
echo "> found camera [make:${EQUIP_DB[CAM,$1,0]}]"\
"[model:${EQUIP_DB[CAM,$1,1]}] [s/n:${EQUIP_DB[CAM,$1,2]}]"
CAMAKE="${EQUIP_DB[CAM,$1,0]}"
CAMMOD="${EQUIP_DB[CAM,$1,1]}"
CAMSER="${EQUIP_DB[CAM,$1,2]}"
}
echo "> selected camera: '${GUISET[0]}'"
case ${GUISET[0]} in
"${CAMERA[0]}") get_camdata 0 ;;
"${CAMERA[1]}") get_camdata 1 ;;
"${CAMERA[2]}") get_camdata 2 ;;
"${CAMERA[3]}") get_camdata 3 ;;
"${CAMERA[4]}") get_camdata 4 ;;
"${CAMERA[5]}") get_camdata 5 ;;
*)
yad --title="ERROR" --error --on-top --center \
--kill-parent="SIGTERM" \
--button="Ok":0 --buttons-layout=center \
--text="\n\t<b>ERROR></b> the camera '${GUISET[0]}' is not in list!\t "
exit 1;
;;
esac
##_______PREPARE__LENS__CONFIG__________________________________________
IFS="$SEPARATOR" read -ra LENS <<< $LENS
echo
echo ">____available__lenses____"
for idx in ${!LENS[@]}; do
echo " $idx: '${LENS[idx]}'"
done
#__find__settings__for__LENS__________________________<_EDIT_HERE_>_____
function get_lendata {
echo "> found lens [make:${EQUIP_DB[LENS,$1,0]}] [model:${EQUIP_DB[LENS,$1,1]}]"\
"[s/n:${EQUIP_DB[LENS,$1,2]}] [fl:${EQUIP_DB[LENS,$1,3]}]"\
"[fl35:${EQUIP_DB[LENS,$1,4]}] [maxA:${EQUIP_DB[LENS,$1,5]}]"
LENMAKE="${EQUIP_DB[LENS,$1,0]}"
LENMOD="${EQUIP_DB[LENS,$1,1]}"
LENSER="${EQUIP_DB[LENS,$1,2]}"
LENFL="${EQUIP_DB[LENS,$1,3]}"
LENFL35="${EQUIP_DB[LENS,$1,4]}"
LENMAXA="${EQUIP_DB[LENS,$1,5]}"
}
echo "> selected lens: '${GUISET[1]}'"
case ${GUISET[1]} in
"${LENS[0]}") get_lendata 0 ;;
"${LENS[1]}") get_lendata 1 ;;
"${LENS[2]}") get_lendata 2 ;;
*)
yad --title="ERROR" --error --on-top --center \
--kill-parent="SIGTERM" \
--button="Ok":0 --buttons-layout=center \
--text="\n\t<b>ERROR></b> the lens '${GUISET[1]}' is not in list!\t "
exit 2;
;;
esac
########################################################################
#________COLLECT__COMMAND__OPTIONS______________________________________
#__CAMERA____________
OPT+='-M'"'"'set Exif.Image.Make '${CAMAKE}"' "
OPT+='-M'"'"'set Exif.Image.Model '${CAMMOD}"' "
OPT+='-M'"'"'set Exif.Image.CameraSerialNumber '${CAMSER}"' "
#__LENS______________
OPT+='-M'"'"'set Exif.Photo.LensMake '${LENMAKE}"' "
OPT+='-M'"'"'set Exif.Photo.LensModel '${LENMOD}"' "
OPT+='-M'"'"'set Exif.Photo.LensSerialNumber '${LENSER}"' "
OPT+='-M'"'"'set Exif.Photo.MaxApertureValue '${LENMAXA}"' "
OPT+='-M'"'"'set Exif.Photo.FocalLength '${LENFL}"' "
OPT+='-M'"'"'set Exif.Photo.FocalLengthIn35mmFilm '${LENFL35}"' "
#__PHOTO_____________
OPT+='-M'"'"'set Exif.Photo.ISOSpeedRatings '${GUISET[4]}"' "
if [ -n "${fstopTbl["${GUISET[2]}"]}" ]; then
OPT+='-M'"'"'set Exif.Photo.FNumber '${fstopTbl["${GUISET[2]}"]}"' "; fi
if [ -n "${fstopTbl["${GUISET[2]}"]}" ]; then
OPT+='-M'"'"'set Exif.Image.ApertureValue '${GUISET[2]}"' "; fi
if [ -n "${shutspdTbl["${GUISET[3]}"]}" ]; then
OPT+='-M'"'"'set Exif.Photo.ExposureTime '${shutspdTbl["${GUISET[3]}"]}"' "; fi
#if [ -n "${GUISET[3]}" ]; then
# OPT+='-M'"'"'set Exif.Image.ShutterSpeedValue 'ERROR"' "; fi
#__DATE______________INCREMENTABLE__FORMAT____
DATEINCR="${GUISET[7]:6:4}${GUISET[7]:3:2}${GUISET[7]::2} "
DATEINCR+="${GUISET[7]:11:2}:${GUISET[7]:14:2}:${GUISET[7]:17:2}"
#__USER______________
OPT+='-M'"'"'set Exif.Image.Copyright '${GUISET[5]}"' "
OPT+='-M'"'"'set Xmp.dc.rights '${GUISET[5]}"' "
OPT+='-M'"'"'set Exif.Image.Artist '${GUISET[6]}"' "
OPT+='-M'"'"'set Xmp.photoshop.Credit '${GUISET[6]}"' "
#OPT+='-M'"'"'set Xmp.dc.creator '${GUISET[6]}"' "
OPT+='-M'"'"'set Exif.Photo.UserComment '${GUISET[8]}"' "
OPT+='-M'"'"'set Exif.Image.ImageDescription '${GUISET[9]}"' "
OPT+='-M'"'"'set Xmp.photoshop.Source '${GUISET[9]}"' "
echo
echo ">____CMD__OPTIONS__________"
echo $OPT
echo "> Start date: '$DATEINCR'"
echo
########################################################################
##_______FILE__BATCH__PROCESSING__/__WRITE__EXIF__DATA__________________
CNT=0
(
for img in "$@"; do
echo "#$img\n"
#__increment__seconds__each__image____________
if [ ${GUISET[10]} = "TRUE" ] || [ $CNT -eq 0 ]; then
EDATE=$(TZ=UTC date +"%Y:%m:%d %H:%M:%S" -d "$DATEINCR UTC + $CNT seconds")
fi
EDATES=""
EDATES+='-M'"'"'set Exif.Photo.DateTimeDigitized '${EDATE}"' "
EDATES+='-M'"'"'set Exif.Photo.DateTimeOriginal '${EDATE}"' "
EDATES+='-M'"'"'set Exif.Image.DateTimeOriginal '${EDATE}"' "
EDATES+='-M'"'"'set Exif.Image.DateTime '${EDATE}"' "
#__write__exif__data__________________
eval /usr/bin/exiv2 ${OPT[@]} ${EDATES[@]} \"$img\"
if [ $? -ne 0 ]; then
echo "#\n-- ERROR --\n" \
"exiv2 commandline error!\n\n" \
"File: $img\n\n" \
"auto close after 10 seconds!\n\n"
sleep 10
exit 3;
fi
CNT=$((CNT + 1))
echo $(bc <<< "100*$CNT/$#"); sleep 0.1
done
) | yad --title "saving exiv2 metadata" \
--width=400 --height=100 --on-top --center \
--progress --percentage=0 --auto-close \
--text "\n\t<b>Saving exif data to file ...</b>"
exit 0