Create Canon DSLR CR2 image statistics (exiftool & GNU Plot) and convert ML dual ISO CR2 files painlessly for post-processing
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1220 lines
53 KiB

5 years ago
  1. #!/bin/bash
  2. # Statistics of camera RAW images with GNU Plot & Exiftool
  3. # Copyright (C) 2017 Pekka Helenius
  4. #
  5. # This program is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU General Public License
  7. # as published by the Free Software Foundation; either version 2
  8. # of the License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License
  16. # along with this program; if not, write to the Free Software
  17. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  18. #
  19. ###############################################
  20. # TODO list:
  21. # TODO Add message, either of these:
  22. # If not a geotagged image file:
  23. # A You haven't geotagged selected photos. Are you sure you want to continue?
  24. # B Following photos don't have altitude value: XX XX XX Are you sure you want to continue?
  25. # TODO IF INPUT IS A CSV FILE, DO NOT SUGGEST 'EXPORT ONLY' AS AN OPTION
  26. # TODO if user chooses alternative 'Export CSV' only, do not crash if errtags or errnames encountered
  27. #
  28. # TODO Be more specific in which case errtags may crash the operation. For instance, if user has chosen only Temperature and ISO to be plotted on GNU Plot, do not crash in case where Focal Length is missing. In other words: be more specific in which conditions match the crash requirement, do not crash the process for nothing.
  29. # TODO The script has only been tested on Canon 5D Mark 3 CR2 files. How does the script work with other Canon camera models? And what about Sony, Nikon?
  30. #
  31. # TODO Re-write, parse and simplify some scripts logics!
  32. ###############################################
  33. # Supported & tested camera raw pictures:
  34. # Canon EOS 5D Mark 3
  35. # TOOL REQUIREMENTS
  36. # perl-exiftool
  37. # kdialog (Qt5)
  38. # qt5-tools (qdbus)
  39. # coreutils (md5sum, echo etc..)
  40. # netpbm (pgmhist)
  41. # dcraw
  42. # gawk
  43. # gnuplot (+ Qt interface)
  44. #
  45. #
  46. # OTHER REQUIREMENTS
  47. #
  48. # Recommended: Qt5 Dolphin file manager (dolphin)
  49. #
  50. # This script is meant to be run as a Dolphin File Manager script (Qt5). It gets file arguments straight from Dolphin
  51. # You can run the script in bash (or execute Dolphin via bash) to get detailed script output (CLI messages, debug messages etc.)
  52. #
  53. #BRACKETS AND QUOTATION MARKS ARE USED IN THIS SCRIPT BECAUSE FOLDER AND FILE PATHS MAY CONTAIN SPACES!
  54. #######################################################################
  55. # VARIABLES FOR THE SCRIPT
  56. CSV_FILECOUNT=$(printf '%s\n' "${@}" | rev | cut -f 1 -d '.' | rev | grep -i "csv" | wc -l)
  57. RAW_FILECOUNT=$(printf '%s\n' "${@}" | rev | cut -f 1 -d '.' | rev | grep -i -E "cr2|nef|dng" | wc -l)
  58. COLUMNCOUNT=24 #Count of all columns must be found in CSV file. This number must match with the number found in kdialog processing dialog below.
  59. #######################################################################
  60. # 1ST FILE CHECK
  61. if [[ "${@}" == "" ]]; then
  62. kdialog --error "Not any files selected!";
  63. exit
  64. elif [[ ! $CSV_FILECOUNT == 0 ]] && [[ ! $RAW_FILECOUNT == 0 ]]; then
  65. kdialog --error "Select only RAW files or a single CSV file!";
  66. exit
  67. elif [[ $CSV_FILECOUNT -gt 1 ]]; then
  68. kdialog --error "Select only one CSV file!";
  69. exit
  70. elif [[ $RAW_FILECOUNT == 1 ]]; then
  71. kdialog --error "Please select at least 2 valid RAW files or a CSV file!";
  72. exit
  73. elif [[ $RAW_FILECOUNT == 0 ]] && [[ $CSV_FILECOUNT == 0 ]]; then
  74. kdialog --error "Please select valid RAW files or a CSV file!";
  75. exit
  76. fi
  77. #######################################################################
  78. # KDIALOG CHECK LIST FORMATTED SELECTION WINDOW
  79. if [[ $RAW_FILECOUNT == 0 ]] && [[ $CSV_FILECOUNT == 1 ]]; then
  80. SELECTION=$(kdialog --checklist "Select statistics to display:" \
  81. 1 "" off \
  82. 2 "" off \
  83. 3 "Temperatures & ISOs" on \
  84. 4 "" off \
  85. );
  86. # 1 Apertures, Exposures & ISOs
  87. # 2 Focal Lengths & Lenses
  88. # 3 Temperatures & ISOs
  89. # 4 Shooting & Focus Modes
  90. if [ "$?" = 0 ]; then
  91. if [ $(expr length "$SELECTION") != 0 ]; then
  92. for result in $SELECTION
  93. do
  94. if [ $result = '"1"' ]; then
  95. SEL1=true
  96. fi
  97. if [ $result = '"2"' ]; then
  98. SEL2=true
  99. fi
  100. if [ $result = '"3"' ]; then
  101. SEL3=true
  102. fi
  103. if [ $result = '"4"' ]; then
  104. SEL4=true
  105. fi
  106. done
  107. else
  108. kdialog --sorry "Aborted";
  109. fi
  110. elif [ "$?" = 1 ]; then
  111. exit 0
  112. else
  113. kdialog --error "Unexpected Error";
  114. fi
  115. SEL5=false
  116. fi
  117. if [[ $RAW_FILECOUNT != 0 ]] && [[ $CSV_FILECOUNT == 0 ]]; then
  118. SELECTION=$(kdialog --checklist "Select statistics to display:" \
  119. 1 "" off \
  120. 2 "" off \
  121. 3 "Temperatures & ISOs" on \
  122. 4 "" off \
  123. 5 "Export Only (CSV)" off \
  124. );
  125. # 1 Apertures, Exposures & ISOs
  126. # 2 Focal Lengths & Lenses
  127. # 3 Temperatures & ISOs
  128. # 4 Shooting & Focus Modes
  129. # 5 Export Only (CSV)
  130. if [ "$?" = 0 ]; then
  131. if [ $(expr length "$SELECTION") != 0 ]; then
  132. for result in $SELECTION
  133. do
  134. if [ $result = '"1"' ]; then
  135. SEL1=true
  136. fi
  137. if [ $result = '"2"' ]; then
  138. SEL2=true
  139. fi
  140. if [ $result = '"3"' ]; then
  141. SEL3=true
  142. fi
  143. if [ $result = '"4"' ]; then
  144. SEL4=true
  145. fi
  146. if [ $result = '"5"' ]; then #If checked, we force all other values to be false
  147. SEL5=true
  148. SEL4=false
  149. SEL3=false
  150. SEL2=false
  151. SEL1=false
  152. fi
  153. done
  154. else
  155. kdialog --sorry "Aborted";
  156. fi
  157. elif [ "$?" = 1 ]; then
  158. exit 0
  159. else
  160. kdialog --error "Unexpected Error";
  161. fi
  162. fi
  163. # SEL1 = "Apertures, Exposures & ISOs" true/false
  164. # SEL2 = "Focal Lengths & Lenses" true/false
  165. # SEL3 = "Temperatures & ISOs" true/false
  166. # SEL4 = "Shooting & Focus Modes" true/false
  167. # SEL5 = "Export Only (CSV)" true/false
  168. #######################################################################
  169. #We get the directory just from the first filename. Pwd should be easier, but bugged, so...
  170. INPUT_DIR=$(dirname "${1}")
  171. DIR_BASENAME=$(echo -n "${INPUT_DIR}" | rev | cut -d'/' -f 1 | rev)
  172. #First & Last file names (without suffixes)
  173. for last; do true; done
  174. FIRST=$(basename "${1}" | cut -f 1 -d '.') #Name of the first file passed into the script
  175. LAST=$(basename "${last}" | cut -f 1 -d '.') #Name of the last file passed into the script
  176. #File name is based on the folder where files exist
  177. FILENAME=$(echo "${DIR_BASENAME}-${FIRST}-${LAST}_metadata")
  178. FILE_EXT=.csv
  179. #Sed is here to remove any trailing spaces and crap like blank lines
  180. INPUT_FILESYSTEM=$(df -h "${1}" | awk -F ' ' 'FNR> 1 {print $1}' | grep -i -E "/dev/sd?|/dev/hd?|?rewritefs|/dev/nvme?" | sed '/^\s*$/d' | wc -l)
  181. #######################################################################
  182. # 2ND FILE CHECK
  183. # Check if we are dealing with a CSV file or bunch of RAW files.
  184. if [[ $CSV_FILECOUNT == 1 ]]; then
  185. FILENAME=$(basename "${1}" | sed 's/\.\w*$//') #without a suffix. #We use valid existing CSV file name here
  186. CSVFOLDER="${INPUT_DIR}" #We don't need to redirect this folder path to $HOME because we assume this file already exists
  187. # If the first input file is not CSV but a RAW file, then we check the filesystem of this file and decide whether to use $HOME folder or picture folder for a new CSV file.
  188. # We don't need this check for any CSV files, because we just extract information from them, not any write operations are required.
  189. elif [[ $RAW_FILECOUNT != 0 ]]; then
  190. echo "Multiple RAW files."
  191. if [[ "${INPUT_FILESYSTEM}" -eq 0 ]]; then #if input file (first file printed in bash) filesystem does not start with /dev/sdX
  192. CSVFOLDER="${HOME}"
  193. kdialog --msgbox "Images are in a SD Card. Writing EXIF CSV data file to ${HOME}/"
  194. # TODO If we have exactly same files selected in SD card but we don't have CSV file in home folder, this doesn't work as expected. It assumes the file to be checked is in home folder.
  195. else
  196. CSVFOLDER="${INPUT_DIR}"
  197. fi
  198. fi
  199. RAWDATA_TMP="${CSVFOLDER}/${FILENAME}${FILE_EXT}"
  200. #######################################################################
  201. # 1) CHECK CSV FILE VALIDITY AGAINST THE SCRIPT OUTPUT
  202. # 2) GET VALUE FOR 'INPUT_FILES_MD5SUM' VARIABLE
  203. # NOTE: We don't check MD5Sums, if we use CSV file as an input. Though this file can exist in the same folder with the pictures, we want to keep CSV files as portable as possible in general.
  204. # Thus, we don't do the following check: CSV file list MD5Sums vs actual corresponding files in the folder (if only CSV is selected as input). This can arise other problems such as images
  205. # with equivalent names listed in CSV file but they are actually different files. This causes mismatch between CSV file content and folder content. So, no go.
  206. # 1) Check validity of the selected CSV file for analysis purposes. Not RAW files selected.
  207. if [[ -e "${RAWDATA_TMP}" ]]; then # Referring to existing CSV file here. User input may or may not be a CSV file, so we don't check it.
  208. echo "This is a valid CSV file. Checking columns."
  209. FILE_COLUMNCOUNT=$(echo -n $(awk -F ',' '{print NF}' "${RAWDATA_TMP}" | sort -nu)) #This *must* return only one value (equal to COLUMNCOUNT). If many values are returned CSV file can't be used because, therefore, there are mismatch between column numbers in rows.
  210. FILE_HASMD5COLUMN=$(awk -F ',' '{print $2}' "${RAWDATA_TMP}" | head -n 1)
  211. FILE_MD5_CHARNUM=$(echo -n $(awk -F ',' ' FNR > 1 {print length($2)}' "${RAWDATA_TMP}" | sort -nu)) #This *must* return only one value. Value 32.
  212. # If the input csv file has valid count of columns and the second column includes md5sums.
  213. if [[ $FILE_COLUMNCOUNT -eq $COLUMNCOUNT ]] && [[ $FILE_HASMD5COLUMN == "File MD5Sum" ]] && [[ $FILE_MD5_CHARNUM == 32 ]]; then
  214. COLUMNS_OK=true
  215. echo "Columns OK, continuing."
  216. elif [[ $RAW_FILECOUNT == 0 ]]; then
  217. echo -e "Charnum is:$FILE_MD5_CHARNUM"
  218. echo "Error in columns."
  219. kdialog --error "Error in CSV file columns!";
  220. exit
  221. else
  222. echo "Error in matching file columns. RAW files as input."
  223. COLUMNS_OK=false #This is a case where we have detected a pattern matcing CSV file but it has invalid columns.
  224. fi
  225. fi
  226. # 2) Instead of single CSV file, if multiple RAW files have been selected, then
  227. if [[ $RAW_FILECOUNT != 0 ]]; then
  228. echo "Getting MD5Sums for RAW files..."
  229. #get md5sums for the files and print output
  230. #Syntax: IMG_8217,IMG_8408,IMG_8544 ... (replace these file names just with md5sums and you get the idea)
  231. INPUT_FILES_MD5SUM=$(echo -n $(printf '%s\n' $(md5sum "${@}") | sed '$!N;s/\n/ /' | awk -F ' ' '{print $2,$1}' | sed -e 's/^.*\///' | sort -n | awk -F ' ' '{print $2}' | tr '\n' ',' | sed 's/,*\r*$//'))
  232. echo "Comparing MD5Sums..."
  233. MAINCSV=$(find "${CSVFOLDER}" -maxdepth 1 -iname "${FILENAME}*${FILE_EXT}")
  234. MAINCSV_COUNT=$(find "${CSVFOLDER}" -maxdepth 1 -iname "${FILENAME}*${FILE_EXT}" | wc -l)
  235. OTHER_CSV=$(find "${CSVFOLDER}" -maxdepth 1 -iname "*${FILE_EXT}")
  236. OTHER_CSV_COUNT=$(find "${CSVFOLDER}" -maxdepth 1 -iname "*${FILE_EXT}" | wc -l)
  237. # Main CSV file
  238. if [[ $COLUMNS_OK == true ]]; then
  239. COMPAREFILE_MD5SUM=$(echo -n $(awk -F ',' 'FNR> 1 {print $1,$2}' "${RAWDATA_TMP}" |sort -n | awk -F ' ' '{print $2}' | tr '\n' ',' | sed 's/,*\r*$//'))
  240. #if md5sums match OK, then...
  241. if [[ "$INPUT_FILES_MD5SUM" == "$COMPAREFILE_MD5SUM" ]]; then
  242. echo -e "MD5Sums match OK."
  243. USEMAINCSV=true
  244. else
  245. echo -e "MD5Sums match not OK."
  246. USEMAINCSV=false
  247. fi
  248. fi
  249. # Other CSV files, including variant of the CSV "file template"
  250. if [[ ! -e "${RAWDATA_TMP}" ]] || [[ ! $OTHER_CSV_COUNT == 0 ]] || [[ $USEMAINCSV == false ]]; then
  251. # Check for CSV variants (which match the filename syntax)
  252. if [[ $MAINCSV_COUNT -gt 0 ]]; then
  253. for m in $MAINCSV; do
  254. echo "DEBUG: Do we get here 1?"
  255. COMPAREFILE_MD5SUM=$(echo -n $(awk -F ',' 'FNR> 1 {print $1,$2}' "${m}" |sort -n | awk -F ' ' '{print $2}' | tr '\n' ',' | sed 's/,*\r*$//'))
  256. if [[ "$INPUT_FILES_MD5SUM" == "$COMPAREFILE_MD5SUM" ]]; then
  257. RAWDATA_TMP="${m}"
  258. FILENAME=$(basename "${m}" | cut -f 1 -d '.') #We get the existing file name template and remove extension.
  259. echo "DEBUG: Do we get here 2?"
  260. USEMAINCSV=true
  261. break
  262. else
  263. echo "DEBUG: Do we get here 3?"
  264. USEMAINCSV=false
  265. fi
  266. done
  267. fi
  268. # Check for other CSVs
  269. if [[ $MAINCSV_COUNT -eq 0 ]] || [[ ! $OTHER_CSV_COUNT == 0 ]] && [[ $USEMAINCSV == false ]]; then
  270. for f in $OTHER_CSV; do
  271. echo "DEBUG: Do we get here 4?"
  272. COMPAREFILE_MD5SUM=$(echo -n $(awk -F ',' 'FNR> 1 {print $1,$2}' "${f}" |sort -n | awk -F ' ' '{print $2}' | tr '\n' ',' | sed 's/,*\r*$//'))
  273. if [[ "$INPUT_FILES_MD5SUM" == "$COMPAREFILE_MD5SUM" ]]; then
  274. RAWDATA_TMP="${f}"
  275. FILENAME=$(basename "${f}" | cut -f 1 -d '.') #We get the existing file name template and remove extension.
  276. echo "DEBUG: Do we get here 5?"
  277. USEOTHERCSV=true
  278. break
  279. else
  280. echo "DEBUG: Do we get here 6?"
  281. USEOTHERCSV=false
  282. fi
  283. done
  284. fi
  285. fi
  286. fi
  287. if [[ $USEMAINCSV == false ]] && [[ $USEOTHERCSV == false ]]; then
  288. echo "DEBUG: Do we get here 7?"
  289. x=1
  290. while [[ -e "${CSVFOLDER}/${FILENAME}-${x}${FILE_EXT}" ]]; do
  291. let x++
  292. done
  293. FILENAME="${FILENAME}-${x}"
  294. fi
  295. echo -e "MD5Sums checked.\n"
  296. if [[ $USEMAINCSV == true ]] || [[ $USEOTHERCSV == true ]]; then
  297. echo -e "Found an existing CSV file with MD5Sums.\n"
  298. elif [[ $USEMAINCSV == true ]] && [[ $USEOTHERCSV == false ]]; then
  299. echo -e "Using existing CSV with correct file template.\n"
  300. elif [[ $USEMAINCSV == true ]] && [[ $USEOTHERCSV == false ]]; then
  301. echo -e "Using a custom named CSV file.\n"
  302. elif [[ $USEMAINCSV == false ]] || [[ $USEOTHERCSV == false ]]; then
  303. echo -e "Creating a new CSV file.\n"
  304. fi
  305. #######################################################################
  306. #We need to redefine bash variables to overwrite the old values!
  307. FILENAME2="${FILENAME}-temp"
  308. FILENAME3="${FILENAME}-iso"
  309. RAWDATA_TMP="${CSVFOLDER}/${FILENAME}${FILE_EXT}"
  310. RAWDATA_TMP2="/tmp/${FILENAME2}${FILE_EXT}"
  311. RAWDATA_TMP3="/tmp/${FILENAME3}${FILE_EXT}"
  312. #################
  313. #DEBUGGING
  314. echo "We use file named $RAWDATA_TMP"
  315. if [[ $SEL3 == true ]]; then
  316. echo "We use isofile named $RAWDATA_TMP2"
  317. echo "We use tempfile named $RAWDATA_TMP3"
  318. fi
  319. #echo
  320. #exit
  321. #################
  322. ##############################################################################################################################################
  323. # KDIALOG PROCESSING WINDOW - BEGIN
  324. ####PROGRESSBAR STUFF - BEGIN
  325. LABELTEXT='Exporting statistics...'
  326. numargs=$# # Number of all files
  327. tics=100 # Percentage tics
  328. inc=0 # Current file number
  329. mltp=1000 # Percentage multiplier for bash
  330. if [[ ! -e "${RAWDATA_TMP}" ]]; then #If the file already exists, we don't want overwrite it. Instead, we skip these steps to speed up the process.
  331. dbusRef=$(kdialog --title "Metadata Extraction ($DIR_BASENAME: images $FIRST-$LAST)" --progressbar "$LABELTEXT" $tics)
  332. qdbus $dbusRef showCancelButton true
  333. ####PROGRESSBAR STUFF - END
  334. while [[ $# -gt 0 ]] && [[ $(qdbus $dbusRef wasCancelled) == "false" ]]; do
  335. i="${1}"
  336. ##############################################
  337. # 1 COLUMN
  338. # ENABLE THIS IF STATEMENT ONLY IF FILE NAMES CONTAINING 'IMG_' ARE ONLY ACCEPTED
  339. #if [[ ! $(echo $(basename "${1}" | cut -f 1 -d '.')) == *"IMG_"* ]]; then
  340. # echo $(basename "${1}" | cut -f 1 -d '.')
  341. # ERRFILE=1 #PRINT INVALID INPUT AS THE LAST COLUMN DUE TO DATE/TIME COLUMNS! SEE BELOW!
  342. #else
  343. echo $(basename "${i}" | cut -f 1 -d '.') #| sed -e 's/IMG_//g') #echo ${i##*/} | sed -e 's/.CR2//g' -e 's/.DNG//g')
  344. # ERRFILE=0
  345. #fi
  346. ##############################################
  347. # 2 COLUMN
  348. #Write md5sum of a file for checking purposes!
  349. md5sum "${i}" | awk -F ' ' '{print $1}'
  350. ##############################################
  351. # 3 COLUMN
  352. if [[ $(exiftool "${i}" |grep --max-count=1 "Camera Temperature" | sed -e 's/[^0-9]*//g' | wc -l) -eq 1 ]]; then
  353. exiftool "${i}" |grep --max-count=1 "Camera Temperature" | sed -e 's/[^0-9]*//g'
  354. else
  355. echo "errtag"
  356. fi
  357. ##############################################
  358. # 4 COLUMN
  359. # ISO Speed setting (yeah, we get it from "Recommended Exposure Index" tag.
  360. if [[ $(exiftool "${i}" | grep -v "Sensitivity" | grep --max-count=1 "Recommended Exposure Index" | sed 's/[^0-9]*//g' | wc -l) -eq 1 ]]; then
  361. exiftool "${i}" | grep -v "Sensitivity" | grep --max-count=1 "Recommended Exposure Index" | sed 's/[^0-9]*//g'
  362. else
  363. echo "errtag"
  364. fi
  365. ##############################################
  366. # 5 COLUMN
  367. if [[ $(exiftool "${i}" |grep --max-count=1 "Exposure Time" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g' | wc -l) -eq 1 ]]; then
  368. exiftool "${i}" |grep --max-count=1 "Exposure Time" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g'
  369. else
  370. echo "errtag"
  371. fi
  372. ##############################################
  373. # 6 COLUMN
  374. if [[ $(exiftool "${i}" |grep --max-count=1 "Target Exposure Time" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g' | wc -l) -eq 1 ]]; then
  375. exiftool "${i}" |grep --max-count=1 "Target Exposure Time" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g'
  376. else
  377. echo "errtag"
  378. fi
  379. ##############################################
  380. # 7 COLUMN
  381. if [[ $(exiftool "${i}" |grep --max-count=1 "Exposure Compensation" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g' | wc -l) -eq 1 ]]; then
  382. exiftool "${i}" |grep --max-count=1 "Exposure Compensation" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g'
  383. else
  384. echo "errtag"
  385. fi
  386. ##############################################
  387. # 8 COLUMN
  388. if [[ $(exiftool "${i}" |grep --max-count=1 "Aperture Value" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g' | wc -l) -eq 1 ]]; then
  389. exiftool "${i}" |grep --max-count=1 "Aperture Value" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g'
  390. else
  391. echo "errtag"
  392. fi
  393. ##############################################
  394. # 9 COLUMN
  395. if [[ $(exiftool "${i}" |grep --max-count=1 "Target Aperture" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g' | wc -l) -eq 1 ]]; then
  396. exiftool "${i}" |grep --max-count=1 "Target Aperture" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g'
  397. else
  398. echo "errtag"
  399. fi
  400. ##############################################
  401. # 10 COLUMN
  402. # Average histogram value for image (brightness etc.)
  403. # For documentation, see http://netpbm.sourceforge.net/doc/pgmhist.html
  404. # we need to convert the image into grayscale with dcraw -d option
  405. # dcraw "manual" is found here: http://www.inweb.ch/foto/dcrawhelp.txt
  406. if [[ $(dcraw -d -4 -j -c "${i}" | pgmhist -median | wc -l) -eq 1 ]]; then
  407. dcraw -d -4 -j -c "${i}" | pgmhist -median | sed 's/[^0-9]*//g'
  408. else
  409. echo "errtag"
  410. fi
  411. ##############################################
  412. # 11 COLUMN
  413. if [[ $(exiftool "${i}" |grep --max-count=1 "Focal Length" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g' -e 's/ //g' | wc -l) -eq 1 ]]; then
  414. exiftool "${i}" |grep --max-count=1 "Focal Length" | sed -e 's/[A-Za-z]*//g' -e 's/.*: //g' -e 's/ //g' #sed 's/ mm//g'
  415. else
  416. echo "errtag"
  417. fi
  418. ##############################################
  419. # 12 COLUMN
  420. if [[ $(exiftool "${i}" |grep --max-count=1 "Hyperfocal Distance" | sed -e 's/.*: //g' -e 's/ m//g' | wc -l) -eq 1 ]]; then
  421. exiftool "${i}" |grep --max-count=1 "Hyperfocal Distance" | sed -e 's/.*: //g' -e 's/ m//g'
  422. else
  423. echo "errtag"
  424. fi
  425. ##############################################
  426. # 13 COLUMN
  427. if [[ $(exiftool "${i}" |grep --max-count=1 "Focus Distance Upper" | sed -e 's/.*: //g' -e 's/ m//g' | wc -l) -eq 1 ]]; then
  428. exiftool "${i}" |grep --max-count=1 "Focus Distance Upper" | sed -e 's/.*: //g' -e 's/ m//g'
  429. else
  430. echo "errtag"
  431. fi
  432. ##############################################
  433. # 14 COLUMN
  434. if [[ $(exiftool "${i}" |grep --max-count=1 "Focus Distance Lower" | sed -e 's/.*: //g' -e 's/ m//g' | wc -l) -eq 1 ]]; then
  435. exiftool "${i}" |grep --max-count=1 "Focus Distance Lower" | sed -e 's/.*: //g' -e 's/ m//g'
  436. else
  437. echo "errtag"
  438. fi
  439. ##############################################
  440. # 15 COLUMN
  441. if [[ $(exiftool "${i}" |grep --max-count=1 "Depth Of Field" | sed -e 's/.*: //g' -e 's/ m//g' | wc -l) -eq 1 ]]; then
  442. exiftool "${i}" |grep --max-count=1 "Depth Of Field" | sed -e 's/.*: //g' -e 's/ m//g'
  443. else
  444. echo "errtag"
  445. fi
  446. ##############################################
  447. # 16 COLUMN
  448. if [[ $(exiftool "${i}" |grep --max-count=1 "Camera Model Name" | sed 's/.*: //g' | wc -l) -eq 1 ]]; then
  449. exiftool "${i}" |grep --max-count=1 "Camera Model Name" | sed 's/.*: //g'
  450. else
  451. echo "errtag"
  452. fi
  453. ##############################################
  454. # 17 COLUMN
  455. if [[ $(exiftool "${i}" |grep --max-count=1 "Lens Type" | sed 's/.*: //g' | wc -l) -eq 1 ]]; then
  456. exiftool "${i}" |grep --max-count=1 "Lens Type" | sed 's/.*: //g'
  457. else
  458. echo "errtag"
  459. fi
  460. ##############################################
  461. # 18 COLUMN
  462. if [[ $(exiftool "${i}" |grep --max-count=1 "Focus Mode" | sed 's/.*: //g' | wc -l) -eq 1 ]]; then
  463. exiftool "${i}" |grep --max-count=1 "Focus Mode" | sed 's/.*: //g'
  464. else
  465. echo "errtag"
  466. fi
  467. ##############################################
  468. # 19 COLUMN
  469. if [[ $(exiftool "${i}" |grep --max-count=1 "Shooting Mode" | sed 's/.*: //g' | wc -l) -eq 1 ]]; then
  470. exiftool "${i}" |grep --max-count=1 "Shooting Mode" | sed 's/.*: //g'
  471. else
  472. echo "errtag"
  473. fi
  474. ##############################################
  475. # 20 COLUMN
  476. if [[ $(exiftool "${i}" |grep --max-count=1 "Live View Shooting" | sed 's/.*: //g' | wc -l) -eq 1 ]]; then
  477. exiftool "${i}" |grep --max-count=1 "Live View Shooting" | sed 's/.*: //g'
  478. else
  479. echo "errtag"
  480. fi
  481. ##############################################
  482. # 21 COLUMN
  483. if [[ $(exiftool "${i}" |grep --max-count=1 "Camera Orientation" | sed 's/.*: //g' | wc -l) -eq 1 ]]; then
  484. exiftool "${i}" |grep --max-count=1 "Camera Orientation" | sed 's/.*: //g'
  485. else
  486. echo "errtag"
  487. fi
  488. ##############################################
  489. # 21 COLUMN
  490. # Subject Tags
  491. if [ $(exiftool "${i}" |grep --max-count=1 "Subject" | sed -e 's/.*: //g' | wc -l) == 0 ]; then
  492. # If Subject tag is empty, get input file filetype (CR2 or cr2 // DNG or dng)
  493. if [[ $(echo $(basename "${i}" | cut -f 2 -d '.' | sed '/^\s*$/d')) == "CR2" ]] || [[ $(echo $(basename "${i}" | cut -f 2 -d '.' | sed '/^\s*$/d')) == "cr2" ]]; then
  494. echo "Single ISO CR2"
  495. elif [[ $(echo $(basename "${i}" | cut -f 2 -d '.' | sed '/^\s*$/d')) == "DNG" ]] || [[ $(echo $(basename "${i}" | cut -f 2 -d '.' | sed '/^\s*$/d')) == "dng" ]]; then
  496. echo "Single ISO DNG"
  497. fi
  498. #If we have a real Subject tag, extract info from it
  499. else
  500. exiftool "${i}" |grep --max-count=1 "Subject" | sed -e 's/.*: //g'
  501. fi
  502. ##############################################
  503. # 23 COLUMN
  504. if [[ $(exiftool "${i}" |grep --max-count=1 "Date/Time Original" | sed -e 's/.*: //g' | cut -d' ' -f1 | awk -F ":" '{print $3, $2, $1}' | sed -e 's/ /\//g' | wc -l) -eq 1 ]]; then
  505. exiftool "${i}" |grep --max-count=1 "Date/Time Original" | sed -e 's/.*: //g' | cut -d' ' -f1 | awk -F ":" '{print $3, $2, $1}' | sed -e 's/ /\//g'
  506. else
  507. echo "errtag"
  508. fi
  509. ##############################################
  510. # 24 COLUMN
  511. if [[ $(exiftool "${i}" |grep --max-count=1 "Date/Time Original" | sed -e 's/.*: //g' | cut -d' ' -f2 | wc -l) -eq 1 ]]; then
  512. exiftool "${i}" |grep --max-count=1 "Date/Time Original" | sed -e 's/.*: //g' | cut -d' ' -f2
  513. else
  514. echo "errtag"
  515. fi
  516. ##############################################
  517. # ENABLE THIS FOR CHECKING FILE NAMES. IF ENABLED, ALL FILES MUST CONTAIN 'IMG_' STRING OR OTHERWISE, THEY ARE EXCLUDED FROM THE STATISTICS!
  518. #Write this line/column only, if an invalid file name has been detected
  519. # if [[ "$ERRFILE" -eq 1 ]]; then
  520. # echo "errname"
  521. # fi
  522. ##############################################
  523. echo "newline" #this is written just as a dummy separator for each processed files for further line separation done below.
  524. ##############################################
  525. ####PROGRESSBAR STUFF - BEGIN
  526. # This section is increasing values seen in the kdialog processing window.
  527. let inc++
  528. #Percentage needs to be calculated like this due to bash rounding limitations...
  529. PERCENT_VALUE=$((($mltp*$tics)/(200*$numargs/$inc % 2 + $mltp*$numargs/$inc)))
  530. qdbus $dbusRef Set "" "value" $PERCENT_VALUE;
  531. qdbus $dbusRef setLabelText "$LABELTEXT ($inc/$numargs)";
  532. ####PROGRESSBAR STUFF - END
  533. shift
  534. ##############################################
  535. # Sort output: replace newlines with commas, remove more commas, replace 'newline' with a new line, trim the first and the last commas of any line. Sort lines by Date & Time (23th & 24th column) and write output
  536. done | tr '\n' ',' | sed -e 's/,[^,]*$//' -e 's/newline/\n/g' | sed -e 's/^,//g' -e 's/\(.*\),/\1/' | sort -t ',' -n -k23 -k24 | sed 's/inf/∞/g' > "${RAWDATA_TMP}" #| sed 's/newline/\n/g' | sort -u
  537. ##############################################
  538. #Close processing window if cancelled event has been triggered.
  539. ####PROGRESSBAR STUFF - BEGIN
  540. # If the process was cancelled, remove tmp file and exit the script.
  541. if [[ ! $(qdbus $dbusRef wasCancelled) == "false" ]]; then
  542. rm "${RAWDATA_TMP}" #We can delete the file because its existence has been checked before the processing window has been opened. Thus we don't get here, if the file already exists.
  543. exit
  544. fi
  545. ####PROGRESSBAR STUFF - END
  546. ###########################################
  547. #Add correct titles to the first row in RAWDATA_TMP file:
  548. sed -i '1s/^/Image File,File MD5Sum,Camera Temperature,ISO,Shutter Speed,Target Exposure,Exposure Compensation,Aperture,Target Aperture,Histogram Median,Focal Length,Hyperfocal Distance,Upper Focus Distance,Lower Focus Distance,Depth Of Field,Camera Model,Lens Model,Focus Mode,Exposure Mode,Live View,Camera Orientation,ISO Type,Date,Time\n/' "${RAWDATA_TMP}"
  549. #Close processing window if not cancelled and processing finished.
  550. ####PROGRESSBAR STUFF - BEGIN
  551. qdbus $dbusRef close
  552. ####PROGRESSBAR STUFF - END
  553. # SEL1 = "Apertures, Exposures & ISOs" true/false
  554. # SEL2 = "Focal Lengths & Lenses" true/false
  555. # SEL3 = "Temperatures & ISOs" true/false
  556. # SEL4 = "Shooting & Focus Modes" true/false
  557. # SEL5 = "Export Only (CSV)" true/false
  558. if [[ $SEL5 == true ]]; then
  559. kdialog --msgbox "EXIF data exported successfully";
  560. exit
  561. fi
  562. elif [[ -e "${RAWDATA_TMP}" ]] && [[ $SEL5 == true ]]; then
  563. kdialog --msgbox "EXIF data exported already.\n\nFile:\n\n${RAWDATA_TMP}";
  564. exit
  565. fi
  566. #KDIALOG PROCESSING WINDOW - END
  567. ##############################################
  568. # Check RAWDATA_TMP for bad line outputs
  569. # 1) BADFILES: Open written (or existing) CSV file
  570. # 2) BADFILES: List all lines matching pattern "errname" or "errtag"
  571. # 3) BADFILES: Write output as a single line, using comma mark to separate the written output (file names). Remove the last extra comma.
  572. BADFILES=$(cat "${RAWDATA_TMP}" | sed -ne '/errname/p' -ne '/errtag/p' | sed 's/,.*$//')
  573. BADFILES_COUNT=$(cat "${RAWDATA_TMP}" | sed -ne '/errname/p' -ne '/errtag/p' | sed 's/,.*$//' | wc -l)
  574. #Count lines found in the output of BADFILES. If not zero (e.g. bad strings found), then...
  575. if [[ $BADFILES_COUNT != 0 ]]; then
  576. cat "${RAWDATA_TMP}" | sed -e '/errname/d' -e '/errtag/d' > "/tmp/${FILENAME}-errtags${FILE_EXT}"
  577. RAWDATA_TMP_ERR="/tmp/${FILENAME}-errtags${FILE_EXT}" #We don't want to overwrite the original file.
  578. if [[ $(cat "${RAWDATA_TMP_ERR}" | wc -l) == 1 ]]; then #If not any valid output image files. Minimum count of lines is 2.
  579. kdialog --error "Could not process any input file:\n\n$BADFILES\n\nThis can be due to missing EXIF data such as Temperature, ISO, Date or Time.\n\nPlease check CSV file (${FILENAME}${FILE_EXT}) contents to study the problem.\n\nExiting.";
  580. rm "${RAWDATA_TMP_ERR}"
  581. exit
  582. elif [[ $(cat "${RAWDATA_TMP_ERR}" | wc -l) -le 2 ]]; then #if we have just a single file here. Minimum count of lines is 3.
  583. kdialog --error "Could not process a valid number of input files. Minimum count of valid files is 2.\n\nFiles that could not be processed:\n\n$BADFILES\n\nThis can be due to missing EXIF data such as Temperature, ISO, Date or Time.\n\nPlease check CSV file (${FILENAME}${FILE_EXT}) contents to study the problem.\n\nExiting."
  584. rm "${RAWDATA_TMP_ERR}"
  585. exit
  586. else
  587. mv "/tmp/${FILENAME}-errtags${FILE_EXT}" "${CSVFOLDER}/${FILENAME}-errtags${FILE_EXT}"
  588. RAWDATA_TMP="${CSVFOLDER}/${FILENAME}-errtags${FILE_EXT}"
  589. kdialog --msgbox "Could not process files:\n\n$BADFILES\n\nThis can be due to missing EXIF data such as Temperature, ISO, Date or Time.\n\nPlease exclude these files or check CSV file (${FILENAME}${FILE_EXT}) contents to study the problem.\n\nNew CSV file written as (bad files excluded):\n${RAWDATA_TMP}";
  590. fi
  591. fi
  592. ######################################################################################
  593. FILELIST=$(echo -n $(awk -F ',' 'FNR> 1 {print $1}' "${RAWDATA_TMP}" |sort -n |tr ' ' '\n' | sort -n | tr '\n' ',' | sed 's/,*\r*$//'))
  594. #Total count of accepted pictures, used for further data representation in gnuplot. We reduce it by 1 due to file header (column titles are not counted):
  595. ACCEPTED_TOTAL=$(echo -n $(($(cat "${RAWDATA_TMP}" | wc -l) - 1)))
  596. ##############################################################################################################################################
  597. ##############################################################################################################################################
  598. ##############################################################################################################################################
  599. ####GNUPLOT STUFF - BEGIN
  600. GNUPLOT_MAINWINDOW_TITLE=$(echo "$DIR_BASENAME ($ACCEPTED_TOTAL images, $FILENAME)")
  601. ###########################################################
  602. # GNUPLOT TIME VALUES
  603. #PLOT 1
  604. # Should we use time values in the first plot? If too many images, basically rendered unreadable
  605. # Rotate x labels if there are too many of them
  606. if [[ $ACCEPTED_TOTAL -lt 10 ]]; then #Do not rotate x labels, if we have less than 10 images selected
  607. X_ROTATELABELS=$(echo -n "")
  608. if [[ $ACCEPTED_TOTAL -le 6 ]]; then #Time values are only if max 6 images selected
  609. X2_TIMESTRINGS=$(echo -n "set x2tics offset 0,-0.5")
  610. else
  611. X2_TIMESTRINGS=$(echo -n "unset x2tics")
  612. fi
  613. else
  614. X_ROTATELABELS=$(echo -n "set xtics rotate 90")
  615. fi
  616. ############################################################
  617. #IMAGE COUNT/UNIT SCALES FOR GNUPLOT PLOTS 2 & 3
  618. if [[ $ACCEPTED_TOTAL -le 10 ]]; then
  619. SCALE=1
  620. elif [[ $ACCEPTED_TOTAL -gt 10 ]] && [[ $ACCEPTED_TOTAL -le 20 ]]; then
  621. SCALE=2
  622. elif [[ $ACCEPTED_TOTAL -gt 20 ]] && [[ $ACCEPTED_TOTAL -le 40 ]]; then
  623. SCALE=4
  624. elif [[ $ACCEPTED_TOTAL -gt 40 ]] && [[ $ACCEPTED_TOTAL -le 60 ]]; then
  625. SCALE=6
  626. elif [[ $ACCEPTED_TOTAL -gt 60 ]] && [[ $ACCEPTED_TOTAL -le 80 ]]; then
  627. SCALE=8
  628. elif [[ $ACCEPTED_TOTAL -gt 80 ]] && [[ $ACCEPTED_TOTAL -le 200 ]]; then
  629. SCALE=10
  630. elif [[ $ACCEPTED_TOTAL -gt 200 ]] && [[ $ACCEPTED_TOTAL -le 400 ]]; then
  631. SCALE=20
  632. elif [[ $ACCEPTED_TOTAL -ge 400 ]]; then
  633. SCALE=40
  634. fi
  635. ##############################################################################################################################################
  636. ##############################################################################################################################################
  637. ##############################################################################################################################################
  638. # ISO VALUES
  639. #Do the following stuff only, if we have checked for any ISO related stuff in the kdialog selection.
  640. if [[ $SEL1 == true ]] || [[ $SEL3 == true ]]; then
  641. #ISO min max values
  642. # 1) Use awk to print field 3 from RAWDATA_TMP. Ignore the first row with FNR> 1 option.
  643. # 2) awk prints equivalent numbers as output. Merge them with "|sort -n" pipe. sort prints numbers starting from the smallest (first line) and ending to the greatest (last line)
  644. # 3) Strip the output, either first line (head -1) or the last one (tail -1).
  645. ISO_MIN_VALUE=$(echo -n $(awk -F ',' 'FNR> 1 {print $4}' "${RAWDATA_TMP}" | sort -n | head -1))
  646. ISO_MAX_VALUE=$(echo -n $(awk -F ',' 'FNR> 1 {print $4}' "${RAWDATA_TMP}" | sort -n | tail -1))
  647. ####################################################################################################################
  648. # ISO VALUES GENERATING RAWDATA_TMP2
  649. #Get percentages for ISO values usage
  650. # OUTPUT template for RAWDATA_TMP3 is as follows:
  651. # <count of ISO values>,<ISO value>,<percentage of specific ISO value usage in data>
  652. # Explanation for the following command:
  653. # 1) Use awk to print field 3 from RAWDATA_TMP. Ignore the first row with FNR> 1 option.
  654. # 2) awk prints equivalent numbers as output. Count and merge them with "|sort -n | uniq -c" pipe
  655. # 3) Output results leading white spaces. For each line, delete them with sed.
  656. # 4) use awk as pipe (awk starting with '{b[$2]=$1;sum=sum ...) to calculate percentage for the first column. First column has count number for each ISO value ("how many times ISO XX is used"). ISOs are defined in column 2. Print the output to a new column 3.
  657. # 5) In step 4, the output has too many decimals. As the output of this step is written to column 3, we use another awk pipe to strip too many decimals of the current column 3. To keep two first decimals, we use %.2f option. Print column 1 ($1) and 2 ($2) as they are, respectively. Add % mark and start a new line (\n) after each printf function.
  658. # 6) Replace spaces with commas for the final output, and write the final output to RAWDATA_TMP3.
  659. awk -F ',' 'FNR> 1 {print $4}' "${RAWDATA_TMP}" |sort -n | uniq -c | sed "s/^[ \t]*//" | awk '{b[$2]=$1;sum=sum+$1} END{for (i in b) print b[i],i,(b[i]/sum)*100}' | awk '{printf "%.0f %.0f %.2f'%'\n", $1,$2,$3}' | tr ' ' ',' > "${RAWDATA_TMP2}"
  660. ####################################################################################################################
  661. # ISO VALUES - MINIMUM, MAXIMUM, LEAST USED, MOST USED AND AVERAGE
  662. #What is the maximum number of matches for a single ISO value?
  663. MAX_MATCH_FOR_ISO=$(echo -n $(awk -F ',' '{print $1}' "${RAWDATA_TMP2}" | sort -n | tail -1))
  664. #We store current min/max ISOvalues to a string variables
  665. WHATIS_REAL_MAX_ISO=$(echo -en "Max: $ISO_MAX_VALUE") # Returns column 3 value of RAWDATA_TMP E.G. 3200, based on max column 3 value of RAWDATA_TMP
  666. WHATIS_REAL_MIN_ISO=$(echo -en "Min: $ISO_MIN_VALUE") # Returns column 3 value of RAWDATA_TMP E.G. 200, based on min column 3 value of RAWDATA_TMP
  667. #Format: (1*400)+(1*1600)+(2*3200) ...
  668. ISO_DIVIDEND=$(echo -n $(($(awk -F ',' '{print "("$1,$2")"}' "${RAWDATA_TMP2}" | sort -n | sed -e 's/ /*/g' | tr '\n' '+' | sed 's/+[^+]*$//'))))
  669. #Just a basic average calculation
  670. ISO_AVERAGE=$(echo -e $(awk 'BEGIN {print "'"$ISO_DIVIDEND"'"/"'"$ACCEPTED_TOTAL"'"}' | awk '{printf "%.0f", $1}'))
  671. ##########################################################
  672. # ISO VALUES - CHECK FOR MIN AND MAX VALUES
  673. # 1) Get awk output of file RAWDATA_TMP3, separator for columns is comma mark, get column 3 ($3)
  674. # 2) Sort percentage values from lowest to greatest, starting from the lowest
  675. # 3) Get the match count for each percentage value (column 3 value) with 'uniq -c'
  676. # 4) Trim all leading white spaces for each printed line
  677. # 5) We have now two columns, separated by space. Get the first column with awk pipe, use space as a column separator, and print column 1 ($1)
  678. # 6) Get the first line. Output represents the number of matches for listed percentage. We check if it's not 1 in the following if statement.
  679. # The whole idea is that we can't give a true statement for "What is the least/most used ISO value" if multiple ISO values equal same percentage for usage
  680. MOSTUSED_ISO_CHECK=$(echo -n $(awk -F ',' '{print $3}' "${RAWDATA_TMP2}" | sort -n | uniq -c | sed "s/^[ \t]*//" | awk -F ' ' '{print $1}' | tail -1))
  681. LEASTUSED_ISO_CHECK=$(echo -n $(awk -F ',' '{print $3}' "${RAWDATA_TMP2}" | sort -n | uniq -c | sed "s/^[ \t]*//" | awk -F ' ' '{print $1}' | head -1))
  682. #The following gives a correct value ONLY IF there are unique values for EACH ISOs. Otherwise, the output is not as expected. That's why we need to check the values of MOST/LEASTUSED_ISO_CHECK first.
  683. MOSTUSED_ISO=$(echo -n $(awk -F ',' '{print $3,$2}' "${RAWDATA_TMP2}" | sort -n | awk -F ' ' '{print $2}' | tail -1))
  684. LEASTUSED_ISO=$(echo -n $(awk -F ',' '{print $3,$2}' "${RAWDATA_TMP2}" | sort -n | awk -F ' ' '{print $2}' | head -1))
  685. ##########################################################
  686. #In addition, we consider that minimum of 10 pictures must be accepted as input. Otherwise, user can read this info pretty easily just checking the gnuplot graphs.
  687. if [[ "$LEASTUSED_ISO_CHECK" -ne 1 ]]; then #If more than one, then...
  688. WHATIS_LEASTUSED_ISO=$(echo -n "") #Output string, nothing to say.
  689. elif [[ "$LEASTUSED_ISO_CHECK" -eq 1 ]]; then #Else if it's one, then...
  690. if [[ "$ACCEPTED_TOTAL" -gt 10 ]]; then #...we check the number of pictures. If it's greater than 10, then print the following string.
  691. WHATIS_LEASTUSED_ISO=$(echo -n ", Least used: $LEASTUSED_ISO") #Returns column 2 value of RAWDATA_TMP3 E.G. 400, based on max column 3 value of RAWDATA_TMP3
  692. elif [[ "$ACCEPTED_TOTAL" -le 10 ]]; then #...we check the number of pictures. If it's equal or less than 10, we print nothing.
  693. WHATIS_LEASTUSED_ISO=$(echo -n "") #Output string, nothing to say
  694. fi
  695. fi
  696. if [[ "$MOSTUSED_ISO_CHECK" -ne 1 ]]; then #If more than one, then...
  697. WHATIS_MOSTUSED_ISO=$(echo -n "") #Output string, nothing to say.
  698. elif [[ "$MOSTUSED_ISO_CHECK" -eq 1 ]]; then #Else if it's one, then...
  699. if [[ "$ACCEPTED_TOTAL" -gt 10 ]]; then #...we check the number of pictures. If it's greater than 10, then print the following string.
  700. WHATIS_MOSTUSED_ISO=$(echo -n ", Most used: $MOSTUSED_ISO") #Returns column 2 value of RAWDATA_TMP3 E.G. 400, based on max column 3 value of RAWDATA_TMP3
  701. elif [[ "$ACCEPTED_TOTAL" -le 10 ]]; then #...we check the number of pictures. If it's equal or less than 10, we print nothing.
  702. WHATIS_MOSTUSED_ISO=$(echo -n "") #Output string, nothing to say
  703. fi
  704. fi
  705. # Max ISO string: $WHATIS_REAL_MAX_ISO
  706. # Min ISO string: $WHATIS_REAL_MIN_ISO
  707. # Least used ISO string: $WHATIS_LEASTUSED_ISO
  708. # Most used ISO string: $WHATIS_MOSTUSED_ISO
  709. ###########################################################
  710. # ISO VALUES - SHIFT ISO RANGE VALUES FOR GNUPLOT
  711. # We shift down minimum ISO values to get a proper scale for gnuplot. Use "Less than" integer comparison because there can be ISO values such as 160, 250...
  712. #DO NOT CHANGE THE CHECK (ELIF EXECUTION) ORDER!!
  713. if [[ "$ISO_MIN_VALUE" -le 100 ]]; then #Less or equal than...
  714. ISO_MIN_VALUE_GNU=0 #Just scaling down, not a true ISO value
  715. elif [[ "$ISO_MIN_VALUE" -le 200 ]]; then
  716. ISO_MIN_VALUE_GNU=100
  717. elif [[ "$ISO_MIN_VALUE" -le 400 ]]; then
  718. ISO_MIN_VALUE_GNU=200
  719. elif [[ "$ISO_MIN_VALUE" -le 800 ]]; then
  720. ISO_MIN_VALUE_GNU=400
  721. elif [[ "$ISO_MIN_VALUE" -le 1600 ]]; then
  722. ISO_MIN_VALUE_GNU=800
  723. elif [[ "$ISO_MIN_VALUE" -le 3200 ]]; then
  724. ISO_MIN_VALUE_GNU=1600
  725. elif [[ "$ISO_MIN_VALUE" -le 6400 ]]; then
  726. ISO_MIN_VALUE_GNU=3200
  727. elif [[ "$ISO_MIN_VALUE" -le 8000 ]]; then
  728. ISO_MIN_VALUE_GNU=6400
  729. fi
  730. if [[ "$ISO_MAX_VALUE" -ge 8000 ]]; then #Greater or equal than...
  731. ISO_MAX_VALUE_GNU=12800
  732. elif [[ "$ISO_MAX_VALUE" -ge 6400 ]]; then
  733. ISO_MAX_VALUE_GNU=8000
  734. elif [[ "$ISO_MAX_VALUE" -ge 3200 ]]; then
  735. ISO_MAX_VALUE_GNU=6400
  736. elif [[ "$ISO_MAX_VALUE" -ge 1600 ]]; then
  737. ISO_MAX_VALUE_GNU=3200
  738. elif [[ "$ISO_MAX_VALUE" -ge 800 ]]; then
  739. ISO_MAX_VALUE_GNU=1600
  740. elif [[ "$ISO_MAX_VALUE" -ge 400 ]]; then
  741. ISO_MAX_VALUE_GNU=800
  742. elif [[ "$ISO_MAX_VALUE" -ge 200 ]]; then
  743. ISO_MAX_VALUE_GNU=400
  744. elif [[ "$ISO_MAX_VALUE" -ge 100 ]]; then
  745. ISO_MAX_VALUE_GNU=200
  746. fi
  747. ###########################################################
  748. #Export all used ISO values
  749. GET_ISO_VALUES=$(echo -n $(awk -F ',' 'FNR> 1 {print $4}' "${RAWDATA_TMP}" | awk '!seen[$0]++' |sort -n | tr '\n' ',' | sed -e 's/,[^,]*$//'))
  750. ISO_TICSRANGE=$(echo -n $ISO_MIN_VALUE_GNU,$GET_ISO_VALUES,$ISO_MAX_VALUE_GNU)
  751. ###########################################################
  752. fi
  753. ##############################################################################################################################################
  754. ##############################################################################################################################################
  755. ##############################################################################################################################################
  756. # TEMPERATURE VALUES
  757. #Do the following stuff only, if we have checked for any ISO related stuff in the kdialog selection.
  758. if [[ $SEL3 == true ]]; then
  759. ####################################################################################################################
  760. #RAWDATA_TMP2
  761. #Get percentages for temperature values
  762. # OUTPUT template for RAWDATA_TMP2 is as follows:
  763. # <count for matching temperatures>,<temperature value>,<percentage of specific temperature in data>
  764. # Explanation for the following command:
  765. # 1) Use awk to print field 2 from RAWDATA_TMP. Ignore the first row with FNR> 1 option.
  766. # 2) awk prints equivalent numbers as output. Count and merge them with "|sort -n | uniq -c" pipe
  767. # 3) Output results leading white spaces. For each line, delete them with sed.
  768. # 4) use awk as pipe (awk starting with '{b[$2]=$1;sum=sum ...) to calculate percentage for the first column. First column has count number for each temperature value ("how many matches for XX temperature"). Temperature values are defined in column 2. Print the output to a new column 3.
  769. # 5) In step 4, the output has too many decimals. As the output of this step is written to column 3, we use another awk pipe to strip too many decimals of the current column 3. To keep two first decimals, we use %.2f option. Print column 1 ($1) and 2 ($2) as they are, respectively. Add % mark and start a new line (\n) after each printf function.
  770. # 6) Replace spaces with commas for the final output, and write the final output to RAWDATA_TMP2.
  771. awk -F ',' 'FNR> 1 {print $3}' "${RAWDATA_TMP}" |sort -n | uniq -c | sed "s/^[ \t]*//" | awk '{b[$2]=$1;sum=sum+$1} END{for (i in b) print b[i],i,(b[i]/sum)*100}' | awk '{printf "%.0f %.0f %.2f'%'\n", $1,$2,$3}' | tr ' ' ',' > "${RAWDATA_TMP3}"
  772. ####################################################################################################################
  773. #Temperature min max values (actual values from the file)
  774. TEMP_MIN=$(echo -n $(awk -F ',' 'FNR> 1 {print $3}' "${RAWDATA_TMP}" | sort -n | head -1)) #Min Temp string
  775. TEMP_MAX=$(echo -n $(awk -F ',' 'FNR> 1 {print $3}' "${RAWDATA_TMP}" | sort -n | tail -1)) #Max Temp string
  776. #Format: (1*31)+(1*38)+(2*39) ...
  777. TEMP_DIVIDEND=$(echo -n $(($(awk -F ',' '{print "("$1,$2")"}' "${RAWDATA_TMP3}" | sort -n | sed -e 's/ /*/g' | tr '\n' '+' | sed 's/+[^+]*$//'))))
  778. #Just a basic average calculation
  779. TEMP_AVERAGE=$(echo -e $(awk 'BEGIN {print "'"$TEMP_DIVIDEND"'"/"'"$ACCEPTED_TOTAL"'"}' | awk '{printf "%.2f", $1}'))
  780. ##############################################
  781. #What is the maximum number of matches for a single temperature?
  782. MAX_MATCH_FOR_TEMP=$(echo -n $(awk -F ',' '{print $1}' "${RAWDATA_TMP3}" | sort -n | tail -1))
  783. #Round temperature scale
  784. TEMP_MULTP=2 #Multiplier for temperature scale for plot 1. (scale only!)
  785. #TEMP_INCREMENT=2 #Temperature increment steps. For example, with value of 2, we get ...0, 2...10, 12, 14, 16...24... etc.
  786. # We set minimum temperature to <value> - 2, rounding down
  787. UNROUNDED_MIN=$(echo -n $(($TEMP_MIN - $TEMP_MULTP)))
  788. # BASE LAYOUT FOR THE FOLLOWING AWK STUFF:
  789. # awk '{i=int($0/4);print((i==$0||$0>0)?i:i-1)*4}'
  790. # https://stackoverflow.com/questions/33085008/bash-round-to-nearest-multiple-of-4
  791. MINVALUE_TEMP=$(echo -n $UNROUNDED_MIN | awk '{i=int("'"$UNROUNDED_MIN"'"/"'"$TEMP_MULTP"'");print((i=="'"$UNROUNDED_MIN"'"||"'"$UNROUNDED_MIN"'">0)?i:i-1)*"'"$TEMP_MULTP"'"}')
  792. # We set maximum temperature to <value> + 2, rounding up
  793. UNROUNDED_MAX=$(echo -n $(($TEMP_MAX + $TEMP_MULTP)))
  794. # BASE LAYOUT FOR THE FOLLOWING AWK STUFF:
  795. # awk '{print$0+(n-$0%n)%n}'
  796. # https://stackoverflow.com/questions/33085008/bash-round-to-nearest-multiple-of-4
  797. MAXVALUE_TEMP=$(echo -n $UNROUNDED_MAX | awk '{print"'"$UNROUNDED_MAX"'"+("'"$TEMP_MULTP"'"-"'"$UNROUNDED_MAX"'"%"'"$TEMP_MULTP"'")%"'"$TEMP_MULTP"'"}')
  798. fi
  799. ##############################################################################################################################################
  800. ##############################################################################################################################################
  801. ##############################################################################################################################################
  802. # SELECTION 3 - TEMPERATURES & ISOS
  803. ########################################################################################
  804. # PLOT 1 (Images & Temperatures & ISOS)
  805. if [[ $SEL3 == true ]]; then
  806. #GNUPLOT, ACTUAL PROGRAM EXECUTION STARTS HERE:
  807. gnuplot <<EOF &
  808. reset
  809. ####################################################
  810. #set title "$GNUPLOT_TITLE" font ",12" offset 0,1
  811. #Set Window Title in Qt environment
  812. set term qt title "$GNUPLOT_MAINWINDOW_TITLE"
  813. set boxwidth 0.75
  814. set style data histograms
  815. set style fill solid border -1
  816. #set xlabel "Image Files ($FIRST-$LAST)" noenhanced
  817. set xlabel " " noenhanced #Just a dummy label placeholder
  818. #set x2label "Time"
  819. set ylabel "Temperature (°C) -- Avg: $TEMP_AVERAGE°C"
  820. set y2label "ISO Value -- Avg: $ISO_AVERAGE"
  821. set datafile separator ","
  822. set grid
  823. #set xtics font ",7"
  824. #set x2tics
  825. # We adapt the layout for number of files...
  826. $X2_TIMESTRINGS
  827. $X_ROTATELABELS
  828. set yrange [$MINVALUE_TEMP:$MAXVALUE_TEMP]
  829. set y2range [$ISO_MIN_VALUE_GNU:$ISO_MAX_VALUE_GNU]
  830. #set xtics ($FILELIST)
  831. set y2tics ($ISO_TICSRANGE)
  832. set xrange [0:$ACCEPTED_TOTAL]
  833. set autoscale x
  834. ##set timefmt '%Y-%m-%dT%H:%M:%S'
  835. ##set mouse mouseformat 3
  836. #Write values as they are
  837. set tics noenhanced font ",8"
  838. #Don't consider the first line as data but as column titles?
  839. set key autotitle columnhead
  840. set palette model RGB
  841. ####################################################
  842. plot '${RAWDATA_TMP}' using 3:xtic(1) title 'Temperature' lc rgb "orange", \
  843. $TEMP_AVERAGE title 'Temp Avg.' lc rgb "red", \
  844. '' using 4:x2tic(sprintf("%s\n%s", stringcolumn(23), stringcolumn(24))) title 'ISO Value' lc rgb "blue" axis x1y2, \
  845. $ISO_AVERAGE title 'ISO Avg.' lc rgb "black" axis x1y2
  846. pause mouse close
  847. EOF
  848. ########################################################################################
  849. # PLOT 2 & 3 (More temperature & ISO analysis)
  850. #GNUPLOT, ACTUAL PROGRAM EXECUTION STARTS HERE:
  851. # This leads actually to a bug which kills qnuplot, making all zoom/grid etc options unavailable. This is what we want for the following plots...
  852. # The bug is discussed here: https://sourceforge.net/p/gnuplot/bugs/1419/
  853. # And here: https://sourceforge.net/p/gnuplot/bugs/1483/
  854. gnuplot --persist <<EOF &
  855. reset
  856. ####################################################
  857. set term qt title "$GNUPLOT_MAINWINDOW_TITLE"
  858. set multiplot layout 2, 1 title "ISO Speed Averages ($WHATIS_REAL_MIN_ISO, $WHATIS_REAL_MAX_ISO$WHATIS_LEASTUSED_ISO$WHATIS_MOSTUSED_ISO, Avg: $ISO_AVERAGE)" font ",10"
  859. #THIS IS A TOP LABEL FOR ISO SPEEDS!
  860. # Max ISO string: $WHATIS_REAL_MAX_ISO
  861. # Min ISO string: $WHATIS_REAL_MIN_ISO
  862. # Least used ISO string: $WHATIS_LEASTUSED_ISO
  863. # Most used ISO string: $WHATIS_MOSTUSED_ISO
  864. #
  865. ###set x2label "ISO Speed Averages" font ",10" offset 0,-1
  866. set ylabel "Image Count" font ",10"
  867. set datafile separator ","
  868. #set style data histogram
  869. set style fill solid border -1
  870. set boxwidth 0.40 relative
  871. set palette model RGB
  872. set yrange [0:$MAX_MATCH_FOR_ISO+1]
  873. set ytics 0,$SCALE,$MAX_MATCH_FOR_ISO+1
  874. set xtics rotate by 45 right
  875. set x2tics offset 0,-0.4
  876. #set offset 0,-2.00
  877. #set xtics
  878. #set xrange [$ISO_MIN_VALUE_GNU:$ISO_MAX_VALUE_GNU]
  879. unset key
  880. plot '${RAWDATA_TMP2}' using 1:xtic(2) title '$WHATIS_REAL_MAX_ISO' lc rgb "green" with boxes, \
  881. '' using 1:x2tic(3) title '' with boxes fill empty
  882. #
  883. ###unset x2label
  884. unset xtics
  885. set xtics
  886. set xlabel "Temperature Averages (Avg: $TEMP_AVERAGE°C)" font ",10" #THIS IS A TOP LABEL FOR TEMPERATURES!
  887. #set ylabel "Image Count" font ",10"
  888. set datafile separator ","
  889. #set style data histogram
  890. set boxwidth 0.40 relative
  891. set style fill solid border -1
  892. set palette model RGB
  893. set yrange [0:$MAX_MATCH_FOR_TEMP+1]
  894. set ytics 0,$SCALE,$MAX_MATCH_FOR_TEMP+1
  895. set x2tics offset 0,-0.4
  896. unset key
  897. plot '${RAWDATA_TMP3}' using 1:xtic(2) title '' lc rgb "red" with boxes, \
  898. '' using 1:x2tic(3) title '' with boxes fill empty
  899. #
  900. unset multiplot
  901. #
  902. ####################################################
  903. EOF
  904. sleep 5
  905. rm "${RAWDATA_TMP2}"
  906. rm "${RAWDATA_TMP3}"
  907. fi
  908. ####GNUPLOT STUFF - END
  909. ########################################################################################
  910. if [[ $KEEPSTATS == false ]]; then
  911. rm "${RAWDATA_TMP}"
  912. fi
  913. exit
  914. ##############################################################################################################################################
  915. ##############################################################################################################################################
  916. ##############################################################################################################################################
  917. # DEPRECATED CODE DEPRECATED CODE
  918. #######################################################################
  919. #DEPRECATED
  920. #Syntax: IMG_8217,IMG_8408,IMG_8544 ...
  921. #INPUT_FILELIST=$(echo -n $(printf '%s\n' "${@}" | rev | cut -d"/" -f1 | rev | cut -f 1 -d '.') | tr ' ' '\n' | sort -n | tr '\n' ',' | sed 's/,*\r*$//')
  922. #COMPAREFILE_FILELIST=$(echo -n $(awk -F ',' 'FNR> 1 {print $1}' "${RAWDATA_TMP}" |sort -n |tr ' ' '\n' | sort -n | tr '\n' ',' | sed 's/,*\r*$//'))
  923. #DEPRECATED
  924. # This syntax is used for md5sums check as well...
  925. #######################################################################
  926. #(For temperatures?) Deprecated:
  927. #awk -F ',' 'FNR> 1 {print $2}' "${RAWDATA_TMP}" |sort -n | uniq -c | sed "s/^[ \t]*//" | tr ' ' ',' > "${RAWDATA_TMP2}"
  928. #Add some ISO and temperature statistics
  929. #paste <(echo "${ISO_ALL_COUNT}") <(echo "${TEMP_ALL_COUNT}") -d , > "${RAWDATA_TMP2}"
  930. #RAWDATA 2 FILE DATA ORDER AS FOLLOWS:
  931. #TEMPERATURE_COUNTS,TEMPERATURE_VALUE,ISO_USAGE_COUNTS,ISO_SPEED_VALUES
  932. # |awk '{print "("$0")"}' --brackets
  933. #ARG1=$1
  934. #ARG2=$2
  935. #ARG3=$3
  936. #ARG4=$4
  937. #ARG5=$5
  938. #ARG6=$6
  939. #gnuplot --persist -e "TITLE='${GNUPLOT_TITLE}'; RAWFILE='${RAWDATA_TMP}'; WINDOWTITLE='${GNUPLOT_MAINWINDOW_TITLE}'; ISO_MIN='${ISO_MIN_VALUE}'; ISO_MAX='${ISO_MAX_VALUE}'; ISO_VALUES='${GET_ISO_VALUES}';" $GNUPLOT_SCRIPT #ARG3='${ARG3}'; ARG4='${ARG4}'; ARG5='${ARG5}'; ARG6='${ARG6}'" $GNUPLOT_FILE
  940. # Max ISO string: $WHATIS_REAL_MAX_ISO
  941. # Min ISO string: $WHATIS_REAL_MIN_ISO
  942. # Least used ISO string: $WHATIS_LEASTUSED_ISO
  943. # Most used ISO string: $WHATIS_MOSTUSED_ISO