How do I determine if a gif is animated? - bash

I have large number of files with .gif extension. I would like to move all animated gifs to another directory. How can I do this using linux shell?

Basically, if identify returns more than one line for a GIF, it's likely animated because it contains more than one image. You may get false positives, however.
Example use in shell:
for i in *.gif; do
if [ `identify "$i" | wc -l` -gt 1 ] ; then
echo move "$i"
else
echo dont move "$i"
fi
done

Related

Reduce image sequence frames

So after exporting a video to a image sequence, I have ended up with way more images than I needed. I would like to trim this down. The images are named 1.png, up to 959.png. Is there a convenient way of doing this with a bash/zsh script? Something like removing every other image and renaming the next to keep the order?
Thanks in advance.
Alright so I found somewhat of a solution.
#!/bin/zsh
c=0
cc=0
ext=".png"
for file in `ls | sort -V`
do
let c=c+1;
let cc=cc+1;
if [ $c -eq 2 ]; then
rm -f $file
c=0
else
let cc=cc-1;
new="$cc$ext"
mv $file $new
fi
done
This will list out all the files in the current directory and loop through them, deleting every other and renaming the next. Just be aware that this will rename the script file too, so you might want to create some logic to avoid that.

How to automate file transformation with simultanous execution?

I am working on transforming a lot of image files (png) into text files. I have the basic code to do this one by one, which is really time consuming. My process involves converting the image files into a black and white format and then using tesseract to transform those into a text file. This process works great but it would take days for me to acomplisyh my task if done file by file.
Here is my code:
for f in $1
do
echo "Processing $f file..."
convert $f -resample 200 -colorspace Gray ${f%.*}BW.png
echo "OCR'ing $f"
tesseract ${f.*}BW.png ${f%.*} -l tla -psm 6
echo "Removing black and white for $f"
rn ${f%.*}BW.png
done
echo "Done!"
Is there a way to perform this process to each file at the same time, that is, how would I be able to run this process simultaneously instead of one by one? My goal is to significantly reduce the amount of time it would take for me to transform these images into text files.
Thanks in advance.
You could make the content for your for loop a function then call the function multiple times but send each all to the background so you could execute another.
function my_process{
echo "Processing $1 file..."
convert $1 -resample 200 -colorspace Gray ${1%.*}BW.png
echo "OCR'ing $1"
tesseract ${1.*}BW.png ${1%.*} -l tla -psm 6
echo "Removing black and white for $1"
rn ${1%.*}BW.png
}
for file in ${files[#]}
do
# & at the end send it to the background.
my_process "$file" &
done
I want to thank contributors #Songy and #shellter.
To answer my question... I ended up using GNU Parallel in order to make these processes run in intervals of 5. Here is the code that I used:
parallel -j 5 convert {} "-resample 200 -colorspace Gray" {.}BW.png ::: *.png ; parallel -j 5 tesseract {} {} -l tla -psm 6 ::: *BW.png ; rm *BW.png
I am now in the process of splitting my dataset in order to run this command simultaneously with different subgroups of my (very large) pool of images.
Cheers

TCropping images with loop in imagemagick

A pretty basic question but I'm new to
Imagemagick (and bash) and I'm having trouble batch cropping images in a folder. I've tried using a loop:
for image in '/home/donald/Desktop/New Folder'*.jpg; do
convert "$image" -gravity center -crop 95X95% "${image%.jpg}"-modified.jpg
done
but it returns:
convert.im6: unable to open image `/home/donald/Desktop/New Folder/*.jpg': No such file or directory # error/blob.c/OpenBlob/2638.
convert.im6: no images defined `/home/donald/Desktop/New Folder/*-modified.jpg' # error/convert.c/ConvertImageCommand/3044."
What would be the proper way of doing this?
Edit: Apparently a space in the folder name was causing problems I deleted it and things seem to be working.Apparently if you want to use a folder with a space name in bash you need to escape the space.
I believe you have no jpg files in the /home/donald/Desktop/New Folder/ directory. The shell will interpret it as the literal string /home/donald/Desktop/New Folder/*.jpg if there are no files matching the wildcard-ed string.
See this example:
$ for f in *.jpg*; do echo $f; done
file.jpg
file2.jpg
$ for f in *.jpgg; do echo $f; done
*.jpgg
See how that last one is the literal string and not a real file? It should have been displayed the first time too if it was (notice the trailing asterix symbol in *.jpg*).
You can fix this by checking if the file exists before executing the command, using [ -f "${file}" ]. For instance:
for image in '/home/donald/Desktop/New Folder'*.jpg; do
[ -f "${image}" ] && convert "$image" -gravity center -crop 95X95% "${image%.jpg}"-modified.jpg
done
This will check if the file image exists (-f) and execute the following statement only if true is returned &&. Had you written || instead of && then the following statement would be executed when false was returned.
Note that bash doesn't return true or false but it's the easiest way to explain and comprehend the notation.

Update file number from file name using exiftool

I'm clicking photos on a camera (Fuji x100s) that doesn't store the filenumber tag in the exif. Though it adds this information in the file name, eg, DSCF0488.JPG, DSCF0489.JPG, DSCF0490.JPG.
How do I extract this number and set it as the file number?
To extract the numbers from the files in your example, using native bash regex, you could do something like this:
for i in *.JPG; do
[[ $i =~ ([[:digit:]]+) ]] && echo ${BASH_REMATCH[1]}
done
Running that loop from the directory containing the files in your question would give the output:
0488
0489
0490
So if you have a tool called exif_script that can add this information to your files, you could do something like:
for photo in *.JPG; do
if [[ $photo =~ ([[:digit:]]+) ]]; then
file_number="${BASH_REMATCH[1]}"
exif_script # set number to $file_number
fi
done
If you don't have a new enough bash to support regular expression matching, you could use sed:
for i in *.JPG; do file_number=$(echo "$i" | sed 's/[^0-9]\{1,\}\([0-9]\{1,\}\).JPG/\1/'); done
The value of $file_number will be the same as in the first piece of code but this approach should work on the vast majority of platforms.

Bash to determine file size

Still learning bash but I had some questions in regards to my script.
My goal with the script is to access a folder with jpg images and if an image is 34.9kb it will return file not present. 34.9kb is the size of the image that shows "image not present".
#!/bin/bash
#Location
DIR="/mnt/windows/images"
file=file.jpg
badfile=12345
actualsize=$(du -b "$file" | cut -f 1)
if [ $actualsize -ge $badfile ]; then
echo $file does not exist >> results.txt
else
echo $file exists >> results.txt
fi
I need it to print each line to a txt file named results. I did research where some people either suggested using du -b or stat -c '%s' but I could not see what the pros and cons would be for using one or the other. Would the print to file come after the if else or stay with the if since Im printing for each file?? I need to print the name and result in the same line. What would be the best way to echo the file??
stat -c '%s' will give you the file size and nothing else, while du -b will include the file name in the output, so you'll have to use for instance cut or awk to get just the file size. For your requirements I'd go with stat.
Based on your question and your comments on your following question I'm assuming what you want to do is:
Iterate through all the *.jpg files in a specific directory
Run different commands depending on the size of the image
Specifically, you want to print "[filename] does not exist" if the file is of size 40318 bytes.
If my assumptions are close, then this should get you started:
# Location
DIR="/home/lsc"
# Size to match
BADSIZE=40318
find "$DIR" -maxdepth 1 -name "*.jpg" | while read filename; do
FILESIZE=$(stat -c "%s" "$filename") # get file size
if [ $FILESIZE -eq $BADSIZE ]; then
echo "$filename has a size that matches BADSIZE"
else
echo "$filename is fine"
fi
done
Note that I've used "find ... | while read filename" instead of "for filename in *.jpg" because the former can better handle paths that contain spaces.
Also note that $filename will contain the full path the the file (e.g. /mnt/windows/images/pic.jpg). If you want to only print the filename without the path, you can use either:
echo ${filename##*/}
or:
echo $(basename $filename)
The first uses Bash string maniputation which is more efficient but less readable, and the latter does so by making a call to basename.

Resources