The ability to sort images by their resolution in dpi

I haven’t tested this, but it should do what you want. All credit goes to @scotty65, I just added a line and modified another.

#!/bin/bash

# dpirename - a script to add the DPI value to the start of image files

# Set extglob which allows the use of "!" to exclude already renamed files from processing

shopt -s extglob

read -p "Image files in $PWD will be renamed with DPI. Do you want to proceed? " -n 1 -r
echo    # (optional) move to a new line

if [[ $REPLY =~ ^[Yy]$ ]]; then

# only loop over files with extensions starting with "j" or "p" that have not already had the DPI added to their file name

for f in !(dpi_*.[jJ][pP]); do

# get the DPI of the main, not embedded thumbnail, image by finding the first instance of "Resolution:" & remove space at start of result

dpi=$(identify -units PixelsPerInch -verbose "${f}" | grep -m 1 Resolution: | cut -d: -f2 | sed -e 's/^[ \t]*//')

size=$(identify  -verbose "${f}" | grep Geometry | grep -o -P '[0-9]+x[0-9]+')

# move the file unless the new name already exists (noclobber)
# For non-verbose processing change "-vn" to just "-n"

mv -vn "${f}" "dpi_${dpi}_size_${size}_${f}";

done

else

echo "Script is exiting"

fi

# Unset extglob

shopt -u extglob

exit 0
1 Like

THANK YOU SO MUCH! It works reliably!

2 Likes

@Alex24 and @scotty65

I noticed that the script doesn’t filter files properly.

Testing
#!/usr/bin/bash

exts=(jpg png jpeg JPG PNG JPEG txt)

for e in ${exts[@]}; do
  touch pic.$e
done

touch dpi_.png
touch dpi_.txt
touch pic
touch something.jpg.txt

shopt -s extglob

for f in !(dpi_*.[jJ][pP]); do
  echo "$f"
done

shopt -u extglob

Output:

dpi_.png
dpi_.txt
pic
pic.jpeg
pic.JPEG
pic.jpg
pic.JPG
pic.png
pic.PNG
pic.txt
something.jpg.txt
testy  # this is the script

So I re-wrote the script a little. @scotty65 Hope you don’t mind.

Notes:

  1. File filtering works properly :crossed_fingers:. It now uses regex instead of glob.
  2. Works for .png .jpg .jpeg, and also works with the capitalised versions. More can be added if necessary.
  3. Saves the output from identify to save time (I’ve got a file that takes ~20s per call :scream: ).
  4. Identify can’t always find a DPI or a size, this of course also affects the original.
#!/usr/bin/bash

# dpirename - a script to add the DPI and resolution to the start of image files

read -p "Image files in $PWD will be renamed with DPI. Do you want to proceed? " -n 1 -r
echo    # (optional) move to a new line

if [[ ! $REPLY =~ ^[Yy]$ ]]; then
  echo "Script is exiting"
  exit 1
fi

# loop over files in current dir
for f in *; do
  # skip already processed files
  [[ "$f" =~ ^dpi_ ]] && continue

  # skip files that haven't got the right extension
  # more can be added if needed
  [[ "$f" =~ \.(jpg|JPG)$|\.(png|PNG)$|\.(jpeg|JPEG)$ ]] || continue

  # in some cases identify can take quite some time, so save the info
  # to a temp file so we don't take twice as long
  identify -units PixelsPerInch -verbose "${f}" > /tmp/info

  dpi=$(grep -m 1 'Resolution:' /tmp/info | grep -Po '[0-9.]+x[0-9.]+')
  size=$(grep -m 1 'Geometry:' /tmp/info | grep -Po '[0-9]+x[0-9]+')

  # move the file unless the new name already exists (noclobber)
  # For non-verbose processing change "-vn" to just "-n"
  mv -vn "${f}" "dpi_${dpi}_size_${size}_${f}"
done

EDIT:

Fixed typo ^dpi_*^dpi_ though it probably shouldn’t make much difference.

The former looks for files that start with dpi followed by 0 or more _
The latter looks for files that start with dpi_

Simplified the regex that checks for extensions…removed extraneous .*s.

2 Likes

That is a great improvement, especially in speed. I’ve never been good with regex, and pretty much everything I’ve learned about writing scripts over the years has been via forums & sites such as Stack Exchange/Overflow. So, while I know in my head what my scripts should do & the order they should do stuff, I don’t always know the best way to write the scripts for optimal performance or best practice.

I’ve just saved your improved version of the script as dpirenameimproved. Even if I don’t use it for sorting my own pictures by dpi, I know that I will be definitely be referring to it in future to help me write better scripts of my own.

2 Likes

I’m no expert either, entirely self taught using the internet. Unfortunately I’m not a good teacher. :grin:

If you’re interested, here are some figures based on my test files (which are just random pictures I had laying around).

I went from ~80s to ~40s for a full run.

The identify calls take between ~55ms and ~20s. I have 5 files that take ~3.6s and 1 file that takes ~20s, the rest take between ~55ms and ~400ms.

Reading the temp file, combined with the 2 grep calls to extract the data, takes ~5.5ms.

So in the fastest cases it’s now taking ~66ms instead of ~110ms, and in the slowest it’s taking ~10s instead of ~20s, per file for that part of the code.

In this case if the command always took less than 11ms, we’d re-run the command. As the fastest I’ve seen it is ~55ms, and the slowest is slower than a granny on crutches, we definitely should save the output to a file.

A variable should be even quicker, but I couldn’t remember how to put it in a var and still have it treated as multiple lines (I didn’t try very hard to figure it out, probably missing something simple).

No solid patterns regarding how fast the files are processed, other than the 5 files that take ~3.6s are all screenshots taken with mate’s screenshot functionality and are fairly large (7680x2160).

Me either, especially best practice. Performance/optimisation comes after you’ve profiled the code to see where, if at all, it needs speeding up. Just keep timing parts and trying to find quicker ways of doing things.

If you haven’t already done so, reading up on big O notation may help.

I find the python re docs to be a good resource on regexes, but I mostly use python anyway. When using other languages best to consult their reference manuals, but the basics tend to be the same or similar.

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.