Fix #990: Display full-resolution embedded jpg from CR3 files
authorColin Clark <colin.clark@cclark.uk>
Tue, 3 Jan 2023 16:17:51 +0000 (16:17 +0000)
committerColin Clark <colin.clark@cclark.uk>
Tue, 3 Jan 2023 16:17:51 +0000 (16:17 +0000)
https://github.com/BestImageViewer/geeqie/issues/990

- Use libraw in preference to exiv2, as it will extract the largest
preview image from a .cr3 file

- Revise export-jpeg plugin to also use exiftool when it detects a
larger number of preview images than exiv2, and additionally include
image auto-rotate

doc/diagrams.dox
plugins/export-jpeg/geeqie-export-jpeg
src/image-load.cc

index 3ea6269..0fc6948 100644 (file)
  * object image_loader_setuploader
  * circle "il->memory_mapped"
  * object exif_get_preview_
- * object exif_get_preview
  * object libraw_get_preview
+ * object exif_get_preview
  * 
  * image_change : image.cc
  * image_change_complete : image.cc
  * image_loader_setuploader : -
  * image_loader_setuploader : Select backend using magic
  * image_loader_setup_source : image-load.cc
+ * libraw_get_preview : image-load-libraw.cc
  * exif_get_preview : exiv2.cc
  * exif_get_preview : EXIV2_TEST_VERSION(0,17,90)
  * exif_get_preview_ : exif.cc
  * exif_get_preview_ : -
  * exif_get_preview_ : If exiv2 not installed
- * libraw_get_preview : image-load-libraw.cc
  * 
  * image_change --> image_change_complete
  * image_change_complete --> image_load_begin
  * image_loader_start_thread --> image_loader_thread_run
  * image_loader_start_thread --> image_loader_setup_source
  * image_loader_setup_source --> exif_get_preview_
- * image_loader_setup_source --> exif_get_preview
- * image_loader_setup_source --> libraw_get_preview : Try libraw if exiv2 fails
+ * image_loader_setup_source --> exif_get_preview : Try exiv2 if libraw fails
+ * image_loader_setup_source --> libraw_get_preview
  * exif_get_preview_ ..> "il->memory_mapped"
  * exif_get_preview ..> "il->memory_mapped"
  * libraw_get_preview ..> "il->memory_mapped"
index 088979a..cf83ca5 100755 (executable)
 ## @file
 ## @brief Extract embedded jpegs from a raw file
 ##
+## Use exiftool if it detects a higher number of preview
+## images, otherwise use exiv2 if available
+## - exiv2 does not see the largest preview image in a .cr3 file.
+##
+## Note that exiftool is noticeably slower than exiv2
+##
 ## - Display a list of the embedded files
 ## - Extract the selected image to a tmp folder
+## - Rotate image if exif data is available
 ## - If jpgicc is installed, correct for currently selected rendering intent
-##  and store in a new file
+## - Store in a new file
 ## - Set Geeqie focus to the newly generated image
 ##
 
-if ! [ -x "$(command -v exiv2)" ]
+use_exiftool="false"
+
+if ! [ -x "$(command -v exiftool)" ]
 then
-       zenity --info --width=300 --height=100 --text="Export jpeg from raw file\n\nexiv2 is not installed" --title="Geeqie export jpeg" 2>/dev/null
-       exit 1
+       if ! [ -x "$(command -v exiv2)" ]
+       then
+               zenity --info --width=300 --height=100 --text="Export jpeg from raw file\n\nNeither exiftoool nor exiv2 are installed" --title="Geeqie export jpeg" 2> /dev/null
+               exit 1
+       fi
 fi
 
 if ! [ -x "$(command -v jpgicc)" ]
 then
-       zenity --info --width=300 --height=100 --text="Export jpeg from raw file\n\njpgicc is not installed\ncolor corrections will not be made\nYou may install via liblcms2-utils" --title="Geeqie export jpeg" 2>/dev/null
+       zenity --info --width=300 --height=100 --text="Export jpeg from raw file\n\njpgicc is not installed\ncolor corrections will not be made\nYou may install via liblcms2-utils" --title="Geeqie export jpeg" 2> /dev/null
+fi
+
+IFS='
+'
+
+exiv2_count=0
+if list=$(exiv2 --print preview "$1")
+then
+       if [ "$(echo "$list" | wc --words)" -gt 1 ]
+       then
+               exiv2_count=$(echo "$list" | wc --lines)
+       fi
+fi
+
+exiftool_count=0
+in_list=$(exiftool -veryshort -preview:all -orientation# "$1")
+if echo "$in_list" | grep --quiet Orientation -
+then
+       exiftool_count=$(($(echo "$in_list" | wc --lines) - 1))
+else
+       exiftool_count=$(echo "$in_list" | wc --words)
+fi
+
+if [ "$exiftool_count" -gt "$exiv2_count" ]
+then
+       use_exiftool="true"
+       count="$exiftool_count"
+else
+       count="$exiv2_count"
+fi
+
+if [ "$use_exiftool" = "true" ]
+then
+       # An integer value is returned by a # suffix
+       in_list=$(exiftool -veryshort -preview:all -orientation# "$1")
+
+       if [ "$(echo "$in_list" | wc --lines)" -gt 0 ]
+       then
+               # $in_list is in the form of lines e.g.
+               #
+               # OtherImage: (Binary data 138367 bytes, use -b option to extract)
+               # PreviewImage: (Binary data 138367 bytes, use -b option to extract)
+               # ...
+               # Orientation: 8
+
+               if echo "$in_list" | grep --quiet Orientation -
+               then
+                       orientation=$(echo "$in_list" | tail --lines=1 | cut --delimiter=' ' --fields=2)
+                       count=$(($(echo "$in_list" | wc --lines) - 1))
+                       list=$(echo "$in_list" | head --lines=-1 | sort --field-separator=: --key=2 --sort=human-numeric --reverse)
+               else
+                       orientation=0
+                       list="$in_list"
+               fi
+       else
+               count=0
+       fi
+else
+       orientation_str=$(exiv2 -g Exif.Image.Orientation -pv "$1")
+
+       # orientation in the form e.g.
+       # 0x0112 Image        Orientation                 Short       1  8
+
+       orientation=$(echo "$orientation_str" | tr --squeeze-repeats ' ' | cut --delimiter=' ' --fields=6)
 fi
 
-count=$(exiv2 -pp "$1" | wc -l)
 if [ "$count" -eq 0 ]
 then
-       zenity --info --width=300 --height=100 --text="Export jpeg from raw file\n\nFile contains no embedded images" --title="Geeqie export jpeg" 2>/dev/null
-       exit 1
+       zenity --info --width=300 --height=100 --text="Export jpeg from raw file\n\nFile contains no embedded images" --title="Geeqie export jpeg" 2> /dev/null
+       exit 0
 fi
 
-IFS='
-'
+case "$orientation" in
+       2)
+               rotation="-flop"
+               ;;
+       3)
+               rotation="-rotate 180"
+               ;;
+       4)
+               rotation="-flip"
+               ;;
+       5)
+               rotation="-transpose"
+               ;;
+       6)
+               rotation="-rotate 90"
+               ;;
+       7)
+               rotation="-transverse"
+               ;;
+       8)
+               rotation="-rotate -90"
+               ;;
+       *)
+               rotation=""
+               ;;
+esac
 
-list=$(exiv2 -pp "$1")
-image_list=""
 n=1
+image_list=""
 
 for image in $list
 do
-       if [ "$n" -eq "$count" ]
+       if [ "$use_exiftool" = "true" ]
        then
-               image_list="${image_list:+${image_list}}TRUE\n$image\n$n"
+               # $image is in the form of e.g.
+               # OtherImage: (Binary data 138367 bytes, use -b option to extract)
+
+               preview_name_colon=$(echo "$image" | cut --delimiter=' ' --fields=1)
+               preview_name=$(echo "$preview_name_colon" | cut --delimiter=':' --fields=1)
+               bytes=$(echo "$image" | cut --delimiter=' ' --fields=4)
+
+               params=$(exiftool -b -"$preview_name" "$1" | exiftool -veryshort -short -ImageSize -MIMEType -)
+               size=$(echo "$params" | head -1)
+               mime=$(echo "$params" | tail -1)
+
+               if [ "$n" -eq "$count" ]
+               then
+                       image_list="${image_list:+${image_list}}TRUE\nPreview $n: $mime,\t $size pixels,\t $bytes bytes\n$n:$preview_name:$mime"
+               else
+                       image_list="${image_list:+${image_list}}FALSE\nPreview $n: $mime,\t $size pixels,\t $bytes bytes\n$n:$preview_name:$mime\n"
+               fi
        else
-               image_list="${image_list:+${image_list}}FALSE\n$image\n$n\n"
+               if [ "$n" -eq "$count" ]
+               then
+                       image_list="${image_list:+${image_list}}TRUE\n$image\n$n"
+               else
+                       image_list="${image_list:+${image_list}}FALSE\n$image\n$n\n"
+               fi
        fi
+
        n=$((n + 1))
 done
 
-image_selected=$(echo "$image_list" | zenity  --width=500 --height=250 --title="Geeqie export jpeg" --list  --text "Select embedded image" --radiolist  --column "Select" --column "Image" --column "n" --hide-column=3 --print-column=3 2>/dev/null) 
+image_selected=$(echo "$image_list" | zenity --width=500 --height=250 --title="Geeqie export jpeg" --list --text "Select embedded image" --radiolist --column "Select" --column "Image" --column "n" --hide-column=3 --print-column=3 2> /dev/null)
 
 if [ -n "$image_selected" ]
 then
        tmpdir=$(mktemp -d "${TMPDIR:-/tmp}/geeqie.XXXXXXXXXX")
 
-       exiv2 --location "$tmpdir" -ep"$image_selected" "$1"
+       if [ "$use_exiftool" = "true" ]
+       then
+               # $image_selected is in the form e.g.
+               # 4:jpegfromraw:image/jpeg
 
-       render_str=$(geeqie --remote --get-render-intent)
+               image_no=$(echo "$image_selected" | cut --delimiter=':' --fields=1)
+               image_id=$(echo "$image_selected" | cut --delimiter=':' --fields=2)
+               image_mime=$(echo "$image_selected" | cut --delimiter=':' --fields=3)
+               image_extension=$(echo "$image_mime" | cut --delimiter='/' --fields=2)
+               base_name=$(basename "$1")
+               image_name="${base_name%.*}"
+               exiftool -b -"$image_id" "$1" > "$tmpdir/$image_name-preview$image_no.$image_extension"
+       else
+               exiv2 --location "$tmpdir" -ep"$image_selected" "$1"
+       fi
 
-       case $render_str in
-               "Perceptual" )
-               render_key=0;;
-               "Relative Colorimetric" )
-               render_key=1;;
-               "Saturation" )
-               render_key=2;;
-               "Absolute Colorimetric" )
-               render_key=3;;
-       esac
+       if [ -n "$rotation" ]
+       then
+               command_str="mogrify $rotation \"$tmpdir/*\""
+               sh -c "$command_str"
+       fi
+
+       base_name=$(basename "$tmpdir/"*)
+       image_extension="${base_name##*.}"
 
-       filename=$(basename "$tmpdir/"* ".jpg")
-       if [ -x "$(command -v jpgicc)" ]
+       if echo "$base_name" | grep --quiet --ignore-case "\.jpeg$" || echo "$base_name" | grep --quiet --ignore-case "\.jpg$"
        then
-               filename_ri="$tmpdir/""$filename""-ri.jpg"
-               jpgicc -t $render_key "$tmpdir/""$filename"".jpg"  "$filename_ri"
+               render_str=$(geeqie --remote --get-render-intent)
 
-               rm "$tmpdir/""$filename"".jpg"
+               case $render_str in
+                       "Perceptual")
+                               render_key=0
+                               ;;
+                       "Relative Colorimetric")
+                               render_key=1
+                               ;;
+                       "Saturation")
+                               render_key=2
+                               ;;
+                       "Absolute Colorimetric")
+                               render_key=3
+                               ;;
+                       *)
+                               render_key=0
+                               ;;
+               esac
 
-               geeqie --remote view:"$filename_ri"
-       else
-               geeqie --remote view:"$tmpdir/""$filename"".jpg"
+               filename=$(basename "$tmpdir/"* ".$image_extension")
+               if [ -x "$(command -v jpgicc)" ]
+               then
+                       filename_ri="$tmpdir/""$filename""-ri.jpg"
+                       jpgicc -t "$render_key" "$tmpdir/""$filename"".$image_extension" "$filename_ri"
+
+                       rm "$tmpdir/$filename.$image_extension"
+               fi
        fi
+
+       geeqie --remote view:"$tmpdir/"
 fi
index 8516ef2..44db91e 100644 (file)
@@ -1008,7 +1008,7 @@ static gboolean image_loader_setup_source(ImageLoader *il)
                        }
                else
                        {
-                       il->mapped_file = exif_get_preview(exif, (guint *)&il->bytes_total, 0, 0); /* get the largest available preview image or NULL for normal images*/
+                       il->mapped_file = libraw_get_preview(il, (guint *)&il->bytes_total);
 
                        if (il->mapped_file)
                                {
@@ -1020,25 +1020,27 @@ static gboolean image_loader_setup_source(ImageLoader *il)
                                        }
                                else
                                        {
-                                       il->preview = IMAGE_LOADER_PREVIEW_EXIF;
+                                       il->preview = IMAGE_LOADER_PREVIEW_LIBRAW;
                                        }
                                }
                        }
 
-               /* If exiv2 does not find a thumbnail, try libraw (which may be slower) */
+               /* If libraw does not find a thumbnail, try exiv2 */
                if (!il->mapped_file)
                        {
-                       il->mapped_file = libraw_get_preview(il, (guint *)&il->bytes_total);
+                       il->mapped_file = exif_get_preview(exif, (guint *)&il->bytes_total, 0, 0); /* get the largest available preview image or NULL for normal images*/
 
                        if (il->mapped_file)
                                {
+                               /* Both exiv2 and libraw sometimes return a pointer to a file
+                                * section that is not a jpeg */
                                if (!(il->mapped_file[0] == 0xFF && il->mapped_file[1] == 0xD8))
                                        {
                                        il->mapped_file = NULL;
                                        }
                                else
                                        {
-                                       il->preview = IMAGE_LOADER_PREVIEW_LIBRAW;
+                                       il->preview = IMAGE_LOADER_PREVIEW_EXIF;
                                        }
                                }
                        }