照片的批量改名与缩放

原创
2014/01/20 16:26
阅读数 1K

今天要处理一批JPEG格式的照片,这些照片的扩展名居然都是大写的 "JPG",另外,这些照片是以1600万像素拍摄的,分辨率为 4608x3456,文件大小在7M左右,对我来说太大了。另外,我还希望能用一种统一的规则来命名这些照片。

* 注:下面的工作需要 Unix/Linux 环境,或者,在 Windows 上使用 Cygwin 。

批量修改扩展名

我需要将所有照片的扩展名由大写的 JPG 改为小写的 jpg。在Linux下,可以使用rename命令很简单地完成这个任务:

rename .JPG .jpg *.JPG

更复杂一点,假设要处理的文件并不限于 JPG 文件,可以写一个脚本来处理:

#!/bin/sh
## ExtName2L.sh: 将指定目录下的所有文件的扩展名改为小写
## Usage: ExtName2L.sh <dirname>
## Example: ExtName2L.sh /dir1

ext_rename(){
  D=`dirname $1`
  B=`basename $1`
  N=${B%.*}  # 去除扩展名,只去除最后一个"."后面的字串,若需要从第一个"."开始算,应改成 ${B%%.*}
  if [[ "$N" != "$B" ]]; then
    X=${B##*.}  # 得到扩展名,只取最后一个"."后面的字串,若需要从第一个"."开始算,应改成 ${B#*.}
    Y=`echo $X | tr A-Z a-z`  # 扩展名转为小写
    [[ "$X" != "$Y" ]] && mv $1 $D/$N.$Y
  fi
}

find $@ -type f |
while read F; do
  ext_rename $F
done

修改照片尺寸

我使用 ImageMagick 来缩放照片,比如,要将一张名为 A.jpg 的照片缩放至 800 万像素 (分辨率为 3264x2448),输出到 $OUTPUT 文件夹下,可以使用 ImageMagick 的 convert 命令:

convert -resize 3264x2448 A.jpg $OUTPUT/A.jpg

只给出宽度值也可以,ImageMagic 会自动计算高度,因此,上面的命令可写为:

convert -resize 3264 A.jpg $OUTPUT/A.jpg
为了有效地缩小文件的体积,可以使用 -quality 参数来指定 JPEG 图片的品质,取值范围在0-100之间,一般情况下,quality 为85就足够了,肉眼很难看出与原图的区别,但文件尺寸会大大缩小。
convert -resize 3264 -quality 85 A.jpg $OUTPUT/A.jpg

使用上面的命令,我的一张7.5M的照片缩小到了900K,而图像质量上与原图相比,我看不出来什么差别。

关于使用 ImageMagick 修改图像尺寸,可以参考这篇博文:《ImageMagick之图片缩放》。在这篇文章中,介绍了更多的 ImageMagick 的用法,比如有条件地进行图片缩放。

如果确实需要保存较高品质的照片,可以指定一个较高的 quality 值,但有一个误区是有人认为 quality 设为100是最好的,事实上未必。quality 设为100只是设定了一个优化底线,并不是最高质量的图片,最终有可能会得到一个品质并无提升却占用更多存储空间的大文件。
这里有一篇文章,可以作为一个很好的参考:图片原理与优化

我认为,如果原图是 BMP 或 PNG 之类的位图文件,quality 设为 100 才有意义。然而,对于那些有损压缩过的 JPG 文件来说,使用比原图更高的压缩品质进行压缩转换意义不大。比如,如果 A.jpg 的压缩品质是 85,那么,在使用 convert 时,指定 quality 为 95 并不能提升图片的品质。也就是说,对于 JPEG 这个有损压缩的图像格式来说,在转储时,只可能不断降低品质,却无法从低品质进行实质性的提升,因为每次压缩,都有可能失真。

可以使用 ImageMagick 的 identify 命令来读取图片的 quality 参数,比如:

identify -format "%Q" A.jpg
我的这些相片给出的 quality 值是 99,那么我在进行转换时设定 quality 为 95 就足以“保真”了。

批量修改照片尺寸

使用 find 命令配合 imagemagick,可以很轻松地完成这个任务:

find $INPUT/ -name "*.jpg" -exec convert -resize 3264 -quality 85 {} $OUTPUT/{} \;

上面的这个任务其实也可以使用 ImageMagick 的 mogrify 命令来完成:

mogrify -resize 3264 -quality 85 $INPUT/* -path $OUTPUT

修改EXIF日期信息

现在,还剩下一个问题,我有些照片没有 EXIF 信息,还有些照片的 EXIF 日期信息是错误的(我忘了设置相机的日期)。我找到一个叫 jhead 的小工具来解决这个问题。jhead 的使用方法比较简单,详细的用法可以参考它的说明文档。
比如,下面的命令可将 A.jpg 的拍摄日期设为 2014-01-20:

jhead -ds2014:01:20 A.jpg

可以使用如下命令来查看照片的 EXIF 日期:

identify -format %[EXIF:DateTime] A.jpg

编写脚本进行批量处理

通过上面的步骤,基本上已经可以很好地处理我手中的这些照片了,但是...新问题出现了,当我使用 quality 为95 来处理照片时,我发现我的这些照片中居然还混杂着较低压缩品质的,对于这些照片,我不想再以不同的 quality 值进行重新压缩处理。并且,我还希望以 EXIF 时间为基础来统一命名处理后的图片,我希望写一个脚本,能完成以下目标:

  • 读取指定目录下的所有文件并做处理
  • 若原文件小于3M,不做 convert 处理
  • 根据原文件的 quality 与所指定的 quality 来决定所使用的 quality 值
  • 读取原文件的 EXIF 时间,据此来命名处理后的文件名,若无法读取 EXIF 时间,仍使用原文件名
  • 目标文件名的扩展名需要小写形式
#!/bin/sh
## 照片批量缩放与命名程序
# Usage: $0 <input> <output> [size] [quality]

INPUT=$1  # 图片源地址
OUTPUT=$2  # 输出地址
SIZE=$3  # resize 参数, 如:4000x3000, 4000, 4000>, etc
QUALITY=$4  # quality 参数

[[ -z "$INPUT" ]] && read -p "Input source:" INPUT
[[ -z "$OUTPUT" ]] && read -p "Input output:" OUTPUT

if [[ ! -d $OUTPUT ]]; then
  echo "$OUTPUT isn't a directory!"
  exit 1
fi

[[ -z "$SIZE" ]] || OP_SIZE="-resize $SIZE"  # 设定 resize 参数

find $INPUT -type f |
while read FILE; do
  DIR=`dirname $FILE`
  FNAME=`basename $FILE`
  F_PRE=${FNAME%.*} # 去除扩展名
  F_EXT=${FNAME##*.} # 得到扩展名
  [[ "$F_PRE" == "$F_EXT" ]] && F_EXT=""
  F_EXT=`echo $F_EXT | tr A-Z a-z` # 转小写
  N=`identify -format %[EXIF:DateTime] $FILE`  # 读取 EXIF 时间
  N=`echo ${N//:/} | tr " " "_"` # 将 YYYY:MM:DD hh:mm:ss 转变为 YYYYMMDD_hhmmss
  [[ -z "$N" ]] && N=$F_PRE # 若读取不到 EXIF 时间,使用原文件名
  OBJ=$N
  F_SIZE=`du -m $FILE | cut -f 1`
  if [ $F_SIZE -le 3 ]; then
    [[ -z "$F_EXT" ]] || OBJ=$N.$F_EXT
    echo cp $FILE $OUTPUT/$OBJ
    cp $FILE $OUTPUT/$OBJ
    continue
  fi
  OP_Q=""
  if [[ ! -z "$QUALITY" ]]; then
    Q=$(identify -format "%Q" $FILE)  # 读取原图的 quality,仅对 JPEG 格式有效
    # 读取不到 quality 或是 quality 高于参数所给的值时,将 quality 设为$QUALITY
    [[ -z "$Q" || $Q -gt $QUALITY ]] && OP_Q="-quality $QUALITY"
  fi
  OBJ=${N}.jpg
  [[ -f $OUTPUT/$OBJ ]] && OBJ=${N}_${F_PRE}.jpg  # 若文件已存在,附加上原文件名
  echo convert $OP_SIZE $OP_Q $FILE $OUTPUT/$OBJ
  convert $OP_SIZE $OP_Q $FILE $OUTPUT/$OBJ
done

假设上述脚本命名为 jpgconvert,可以这样使用它:

# 转换 src 目录下的所有图片,分辨率为1600x1200,压缩品质为85,输出到 dest 目录下:
jpgconvert src dest 1600x1200 85
# 转换 src 目录下的所有图片,宽度为4000,高度自适应,压缩品质为90,输出到 dest 目录下:
jpgconvert src dest 4000 90
# 转换 src 目录下的所有图片,宽度为4000,高度自适应,压缩品质为90,输出到 dest 目录下,若原文件宽度小于4000,则不改变原图尺寸:
jpgconvert src dest 4000">" 90


展开阅读全文
打赏
0
12 收藏
分享
加载中
更多评论
打赏
0 评论
12 收藏
0
分享
返回顶部
顶部