Does anyone know a good way to batch-convert a bunch of PNGs into JPGs in linux? (I'm using Ubuntu).
A png2jpg binary that I could just drop into a shell script would be ideal.
Super User is a question and answer site for computer enthusiasts and power users. It only takes a minute to sign up.
Sign up to join this communityYour best bet would be to use ImageMagick.
I am not an expert in the actual usage, but I know you can pretty much do anything image-related with this!
An example is:
convert image.png image.jpg
which will keep the original as well as creating the converted image.
As for batch conversion, I think you need to use the Mogrify tool which is part of ImageMagick.
Keep in mind that this overwrites the old images.
The command is:
mogrify -format jpg *.png
mogrify -format jpg -background black -flatten *.png
Jun 26, 2012 at 18:54
mogrify -format jpeg img.png
I have 2 files and file img.*
reports one png, the original untouched, and a new jpeg one. So mogrify
does not overwrite original files in this case.
mogrify
documentation: "This tool is similiar to convert
except that the original image file is overwritten (unless you change the file suffix with the -format option) with any changes you request."
mogrify -quality 75 -format jpg *.png
I have a couple more solutions.
The simplest solution is like most already posted. A simple bash for loop.
for i in *.png ; do convert "$i" "${i%.*}.jpg" ; done
For some reason I tend to avoid loops in bash so here is a more unixy xargs approach, using bash for the name-mangling.
ls -1 *.png | xargs -n 1 bash -c 'convert "$0" "${0%.*}.jpg"'
The one I use. It uses GNU Parallel to run multiple jobs at once, giving you a performance boost. It is installed by default on many systems and is almost definitely in your repo (it is a good program to have around).
ls -1 *.png | parallel convert '{}' '{.}.jpg'
The number of jobs defaults to the number of CPU cores you have. I found better CPU usage using 3 jobs on my dual-core system.
ls -1 *.png | parallel -j 3 convert '{}' '{.}.jpg'
And if you want some stats (an ETA, jobs completed, average time per job...)
ls -1 *.png | parallel --eta convert '{}' '{.}.jpg'
There is also an alternative syntax if you are using GNU Parallel.
parallel convert '{}' '{.}.jpg' ::: *.png
And a similar syntax for some other versions (including debian).
parallel convert '{}' '{.}.jpg' -- *.png
done
at the end of that for loop. Also, for the parallel stuff, you could avoid using that ls
and pipe with a construct like: parallel -j 3 --eta convert '{}' '{.}.jpg' ::: *.png
(see here)
ls
method for myself because it makes more sense to me.
Jan 28, 2013 at 14:04
--
rather than :::
) - and even then, it frustratingly lacks some of the features of GNU parallel.
find . -type f -name '*.png' | parallel --eta convert '{}' '{.}.jpg'
The convert
command found on many Linux distributions is installed as part of the ImageMagick suite. Here's the bash code to run convert
on all PNG files in a directory and avoid that double extension problem:
for img in *.png; do
filename=${img%.*}
convert "$filename.png" "$filename.jpg"
done
for f in *.png; do convert "$f" "${f/%png/jpg}"; done
For those who just want the simplest commands:
Convert and keep original files:
mogrify -format jpg *.png
Convert and remove original files:
mogrify -format jpg *.png && rm *.png
Kinda late to the party, but just to clear up all of the confusion for someone who may not be very comfortable with cli, here's a super dumbed-down reference and explanation.
Example Directory
bar.png
foo.png
foobar.jpg
Keeps all original png files as well as creates jpg files.
mogrify -format jpg *.png
Result
bar.png
bar.jpg
foo.png
foo.jpg
foobar.jpg
Explanation
-format
option. (From the site: This tool is similar to convert except that the original image file is overwritten (unless you change the file suffix with the -format option)
)- format
option specifies that you will be changing the format, and the next argument needs to be the type (in this case, jpg).*.png
is the input files (all files ending in .png).Converts all png files to jpg, removes original.
mogrify -format jpg *.png && rm *.png
Result
bar.jpg
foo.jpg
foobar.jpg
Explanation
&&
is a boolean operator. In short:
0
means no errors.&&
performs short circuit evaluation, the right part will only be performed if there were no errors. This is useful because you may not want to delete all of the original files if there was an error converting them.rm
command deletes files.Now here's some goodies for the people who are comfortable with the cli.
If you want some output while it's converting files:
for i in *.png; do mogrify -format jpg "$i" && rm "$i"; echo "$i converted to ${i%.*}.jpg"; done
Convert all png files in all subdirectories and give output for each one:
find . -iname '*.png' | while read i; do mogrify -format jpg "$i" && rm "$i"; echo "Converted $i to ${i%.*}.jpg"; done
Convert all png files in all subdirectories, put all of the resulting jpgs into the all
directory, number them, remove original png files, and display output for each file as it takes place:
n=0; find . -iname '*.png' | while read i; do mogrify -format jpg "$i" && rm "$i"; fn="all/$((n++)).jpg"; mv "${i%.*}.jpg" "$fn"; echo "Moved $i to $fn"; done
while read
part (replace it or remove it all together)...
Oct 26, 2015 at 12:07
find . -name "*.png" -print0 | xargs -0 mogrify -format jpg -quality 50
*.jpg
files next to the original *.png
files, shows how to reduce file size/quality and doesn't break because of any odd characters in directory or file name.
Dec 28, 2014 at 15:21
The actual "png2jpg
" command you are looking for is in reality split into two commands called pngtopnm
and cjpeg
, and they are part of the netpbm
and libjpeg-progs
packages, respectively.
png2pnm foo.png | cjpeg > foo.jpeg
my quick solution
for i in $(ls | grep .png); do convert $i $(echo $i.jpg | sed s/.png//g); done
for f in ./*.png; do convert "$f" "${f%.*}.jpg"; done
. That avoids the completely unnecessary ls
, grep
and sed
calls (and echo
, but IIRC that's a bash builtin and so will have no/very little performance impact), and gets rid of two pipes and two subshells, and involves less typing. It's even slightly more portable, since not all versions of ls
are safe to parse.
For batch processing:
for img in *.png; do
convert "$img" "$img.jpg"
done
You will end up with file names like image1.png.jpg though.
This will work in bash, and maybe bourne. I don't know about other shells, but the only difference would likely be the loop syntax.
Many years too late, there's a png2jpeg utility specifically for this purpose, which I authored.
Adapting the code by @Marcin:
#!/bin/sh
for img in *.png
do
filename=${img%.*}
png2jpeg -q 95 -o "$filename.jpg" "$filename.png"
done
This is what I use to convert when the files span more than one directory. My original one was TGA to PNG
find . -name "*.tga" -type f | sed 's/\.tga$//' | xargs -I% convert %.tga %.png
The concept is you find
the files you need, strip off the extension then add it back in with xargs
. So for PNG to JPG, you'd change the extensions and do one extra thing to deal with alpha channels namely setting the background (in this example white, but you can change it) then flatten the image
find . -name "*.png" -type f | sed 's/\.png$//' | xargs -I% convert %.png -background white -flatten %.jpg
If your PNG is transparent, try adding a black bg before converting:
mogrify -format jpg -background black -flatten *.png
or a white bg:
mogrify -format jpg -background white -flatten *.png
Here's the same bash
solution but with ffmpeg
converting:
for i in *.png ; do ffmpeg -i "$i" "${i%.*}.jpg" ; done
.png
to .jpg
.
Apr 6, 2021 at 17:13
ffmpeg -i input.jpg output.jpg
Apr 7, 2021 at 13:20
I made this script,for jpg to png it works well:
#!/bin/bash
clear
# Remove Spaces in the names
for f in *\ *; do mv "$f" "${f// /_}"; done
# Number of files to convert
files=$(find . -maxdepth 1 -name '*.jpg')
total=$(wc -l <<< "$files")
files=$(find . -maxdepth 1 -prune -name '*.JPG')
total=$(( $total + $(wc -l <<< "$files") ))
files=$(find . -maxdepth 1 -prune -name '*.jpeg')
total=$(( $total + $(wc -l <<< "$files") ))
files=$(find . -maxdepth 1 -prune -name '*.JPEG')
total=$(( $total + $(wc -l <<< "$files") ))
echo "There is $total JPG files"
# Convert
echo " Convert is going on..."
for img in *.*; do
filename=${img%.*}
form3="${img: -3}"
form4="${img: -4}"
if [[ $form3 =~ "JPG" ]] || [[ $form3 =~ "jpg" ]];
then
convert "$filename.$form3" "$filename.png"
echo " $img ===> ${img%.$form3}.png"
elif [[ $form4 =~ "JPEG" ]] || [[ $form4 =~ "jpeg" ]];
then
convert "$filename.$form4" "$filename.png"
echo " $img ==> ${img%.$form4}.png"
else echo
fi
done
echo " Convert finished !"
# Remove
echo " Removing originals files"
echo
shopt -s nocaseglob
for i in *.jpg; do
rm -v $i
done
for i in *.jpeg; do
rm -v $i
done
echo
ls --color=auto -C
exit