jpegli_tools_test.sh (10333B)
1 #!/bin/bash 2 # Copyright (c) the JPEG XL Project Authors. All rights reserved. 3 # 4 # Use of this source code is governed by a BSD-style 5 # license that can be found in the LICENSE file. 6 7 # End-to-end roundtrip tests for cjpegli and djpegli tools, and other linux 8 # tools linked with the jpegli library. 9 10 set -eux 11 12 MYDIR=$(dirname $(realpath "$0")) 13 JPEGXL_TEST_DATA_PATH="${MYDIR}/../../testdata" 14 15 # Temporary files cleanup hooks. 16 CLEANUP_FILES=() 17 cleanup() { 18 if [[ ${#CLEANUP_FILES[@]} -ne 0 ]]; then 19 rm -rf "${CLEANUP_FILES[@]}" 20 fi 21 } 22 trap 'retcode=$?; { set +x; } 2>/dev/null; cleanup' INT TERM EXIT 23 24 verify_ssimulacra2() { 25 local score="$("${ssimulacra2}" "${1}" "${2}")" 26 python3 -c "import sys; sys.exit(not ${score} >= ${3})" 27 } 28 29 verify_max_bpp() { 30 local infn="$1" 31 local jpgfn="$2" 32 local maxbpp="$3" 33 local size="$(wc -c "${jpgfn}" | cut -d' ' -f1)" 34 local pixels=$(( "$(identify "${infn}" | cut -d' ' -f3 | tr 'x' '*')" )) 35 python3 -c "import sys; sys.exit(not ${size} * 8 <= ${maxbpp} * ${pixels})" 36 } 37 38 # Test that jpeg files created with cjpegli can be decoded with normal djpeg. 39 cjpegli_test() { 40 local infn="${JPEGXL_TEST_DATA_PATH}/$1" 41 local encargs="$2" 42 local minscore="$3" 43 local maxbpp="$4" 44 local jpgfn="$(mktemp -p "${tmpdir}")" 45 local outfn="$(mktemp -p "${tmpdir}").ppm" 46 47 "${cjpegli}" "${infn}" "${jpgfn}" $encargs 48 djpeg -outfile "${outfn}" "${jpgfn}" 49 50 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}" 51 verify_max_bpp "${infn}" "${jpgfn}" "${maxbpp}" 52 } 53 54 # Test full cjpegli/djpegli roundtrip. 55 cjpegli_djpegli_test() { 56 local infn="${JPEGXL_TEST_DATA_PATH}/$1" 57 local encargs="$2" 58 local minscore="$3" 59 local maxbpp="$4" 60 local jpgfn="$(mktemp -p "${tmpdir}")" 61 local outfn="$(mktemp -p "${tmpdir}").png" 62 63 "${cjpegli}" "${infn}" "${jpgfn}" $encargs 64 "${djpegli}" "${jpgfn}" "${outfn}" 65 66 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}" 67 verify_max_bpp "${infn}" "${jpgfn}" "${maxbpp}" 68 } 69 70 # Test the --target_size command line argument of cjpegli. 71 cjpegli_test_target_size() { 72 local infn="${JPEGXL_TEST_DATA_PATH}/$1" 73 local encargs="$2" 74 local target_size="$3" 75 local jpgfn="$(mktemp -p "$tmpdir")" 76 77 "${cjpegli}" "${infn}" "${jpgfn}" $encargs --target_size "${target_size}" 78 local size="$(wc -c "${jpgfn}" | cut -d' ' -f1)" 79 python3 -c "import sys; sys.exit(not ${target_size} * 0.996 <= ${size})" 80 python3 -c "import sys; sys.exit(not ${target_size} * 1.004 >= ${size})" 81 } 82 83 # Test that jpeg files created with cjpeg binary + jpegli library can be decoded 84 # with normal libjpeg. 85 cjpeg_test() { 86 local infn="${JPEGXL_TEST_DATA_PATH}/$1" 87 local encargs="$2" 88 local minscore="$3" 89 local maxbpp="$4" 90 local jpgfn="$(mktemp -p "$tmpdir")" 91 local outfn="$(mktemp -p "${tmpdir}").png" 92 93 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 94 cjpeg $encargs -outfile "${jpgfn}" "${infn}" 95 djpeg -outfile "${outfn}" "${jpgfn}" 96 97 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}" 98 verify_max_bpp "${infn}" "${jpgfn}" "${maxbpp}" 99 } 100 101 # Test decoding of jpeg files with the djpegli binary. 102 djpegli_test() { 103 local infn="${JPEGXL_TEST_DATA_PATH}/$1" 104 local encargs="$2" 105 local minscore="$3" 106 local jpgfn="$(mktemp -p "$tmpdir")" 107 108 cjpeg $encargs -outfile "${jpgfn}" "${infn}" 109 110 # Test that disabling output works. 111 "${djpegli}" "${jpgfn}" --disable_output 112 for ext in png pgm ppm pfm pnm baz; do 113 "${djpegli}" "${jpgfn}" /foo/bar.$ext --disable_output 114 done 115 116 # Test decoding to PNG, PPM, PNM, PFM 117 for ext in png ppm pnm pfm; do 118 local outfn="$(mktemp -p "${tmpdir}").${ext}" 119 "${djpegli}" "${jpgfn}" "${outfn}" --num_reps 2 120 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}" 121 done 122 123 # Test decoding to PGM (for grayscale input) 124 if [[ "${infn: -6}" == ".g.png" ]]; then 125 local outfn="$(mktemp -p "${tmpdir}").pgm" 126 "${djpegli}" "${jpgfn}" "${outfn}" --quiet 127 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}" 128 fi 129 130 # Test decoding to 16 bit 131 for ext in png pnm; do 132 local outfn8="$(mktemp -p "${tmpdir}").8.${ext}" 133 local outfn16="$(mktemp -p "${tmpdir}").16.${ext}" 134 "${djpegli}" "${jpgfn}" "${outfn8}" 135 "${djpegli}" "${jpgfn}" "${outfn16}" --bitdepth 16 136 local score8="$("${ssimulacra2}" "${infn}" "${outfn8}")" 137 local score16="$("${ssimulacra2}" "${infn}" "${outfn16}")" 138 python3 -c "import sys; sys.exit(not ${score16} > ${score8})" 139 done 140 } 141 142 # Test decoding of jpeg files with the djpeg binary + jpegli library. 143 djpeg_test() { 144 local infn="${JPEGXL_TEST_DATA_PATH}/$1" 145 local encargs="$2" 146 local minscore="$3" 147 local jpgfn="$(mktemp -p "$tmpdir")" 148 149 cjpeg $encargs -outfile "${jpgfn}" "${infn}" 150 151 # Test default behaviour. 152 local outfn="$(mktemp -p "${tmpdir}").pnm" 153 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 154 djpeg -outfile "${outfn}" "${jpgfn}" 155 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}" 156 157 # Test color quantization. 158 local outfn="$(mktemp -p "${tmpdir}").pnm" 159 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 160 djpeg -outfile "${outfn}" -colors 128 "${jpgfn}" 161 verify_ssimulacra2 "${infn}" "${outfn}" 48 162 163 local outfn="$(mktemp -p "${tmpdir}").pnm" 164 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 165 djpeg -outfile "${outfn}" -colors 128 -onepass -dither fs "${jpgfn}" 166 verify_ssimulacra2 "${infn}" "${outfn}" 30 167 168 local outfn="$(mktemp -p "${tmpdir}").pnm" 169 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 170 djpeg -outfile "${outfn}" -colors 128 -onepass -dither ordered "${jpgfn}" 171 verify_ssimulacra2 "${infn}" "${outfn}" 30 172 173 # Test -grayscale flag. 174 local outfn="$(mktemp -p "${tmpdir}").pgm" 175 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 176 djpeg -outfile "${outfn}" -grayscale "${jpgfn}" 177 local outfn2="$(mktemp -p "${tmpdir}").pgm" 178 convert "${infn}" -set colorspace Gray "${outfn2}" 179 # JPEG color conversion is in gamma-compressed space, so it will not match 180 # the correct grayscale version very well. 181 verify_ssimulacra2 "${outfn2}" "${outfn}" 60 182 183 # Test -rgb flag. 184 local outfn="$(mktemp -p "${tmpdir}").ppm" 185 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 186 djpeg -outfile "${outfn}" -rgb "${jpgfn}" 187 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}" 188 189 # Test -crop flag. 190 for geometry in 256x256+128+128 256x127+128+117; do 191 local outfn="$(mktemp -p "${tmpdir}").pnm" 192 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 193 djpeg -outfile "${outfn}" -crop "${geometry}" "${jpgfn}" 194 local outfn2="$(mktemp -p "${tmpdir}").pnm" 195 convert "${infn}" -crop "${geometry}" "${outfn2}" 196 verify_ssimulacra2 "${outfn2}" "${outfn}" "${minscore}" 197 done 198 199 # Test output scaling. 200 for scale in 1/4 3/8 1/2 5/8 9/8; do 201 local scalepct="$(python3 -c "print(100.0*${scale})")%" 202 local geometry=96x128+0+0 203 local outfn="$(mktemp -p "${tmpdir}").pnm" 204 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \ 205 djpeg -outfile "${outfn}" -scale "${scale}" -crop "${geometry}" "${jpgfn}" 206 local outfn2="$(mktemp -p "${tmpdir}").pnm" 207 convert "${infn}" -scale "${scalepct}" -crop "${geometry}" "${outfn2}" 208 verify_ssimulacra2 "${outfn2}" "${outfn}" 80 209 done 210 } 211 212 main() { 213 local tmpdir=$(mktemp -d) 214 CLEANUP_FILES+=("${tmpdir}") 215 216 local build_dir="${1:-}" 217 if [[ -z "${build_dir}" ]]; then 218 build_dir=$(realpath "${MYDIR}/../../build") 219 fi 220 221 local cjpegli="${build_dir}/tools/cjpegli" 222 local djpegli="${build_dir}/tools/djpegli" 223 local ssimulacra2="${build_dir}/tools/ssimulacra2" 224 local rgb_in="jxl/flower/flower_small.rgb.png" 225 local gray_in="jxl/flower/flower_small.g.png" 226 local ppm_rgb="jxl/flower/flower_small.rgb.depth8.ppm" 227 local ppm_gray="jxl/flower/flower_small.g.depth8.pgm" 228 229 cjpegli_test "${rgb_in}" "" 88.5 1.7 230 cjpegli_test "${rgb_in}" "-q 80" 84 1.2 231 cjpegli_test "${rgb_in}" "-q 95" 91.5 2.4 232 cjpegli_test "${rgb_in}" "-d 0.5" 92 2.6 233 cjpegli_test "${rgb_in}" "--chroma_subsampling 420" 87 1.5 234 cjpegli_test "${rgb_in}" "--chroma_subsampling 440" 87 1.6 235 cjpegli_test "${rgb_in}" "--chroma_subsampling 422" 87 1.6 236 cjpegli_test "${rgb_in}" "--std_quant" 91 2.2 237 cjpegli_test "${rgb_in}" "--noadaptive_quantization" 88.5 1.85 238 cjpegli_test "${rgb_in}" "-p 1" 88.5 1.72 239 cjpegli_test "${rgb_in}" "-p 0" 88.5 1.75 240 cjpegli_test "${rgb_in}" "-p 0 --fixed_code" 88.5 1.8 241 cjpegli_test "${gray_in}" "" 92 1.4 242 243 cjpegli_test_target_size "${rgb_in}" "" 10000 244 cjpegli_test_target_size "${rgb_in}" "" 50000 245 cjpegli_test_target_size "${rgb_in}" "" 100000 246 cjpegli_test_target_size "${rgb_in}" "--chroma_subsampling 420" 20000 247 cjpegli_test_target_size "${rgb_in}" "--xyb" 20000 248 cjpegli_test_target_size "${rgb_in}" "-p 0 --fixed_code" 20000 249 250 cjpegli_test "jxl/flower/flower_small.rgb.depth8.ppm" "" 88.5 1.7 251 cjpegli_test "jxl/flower/flower_small.rgb.depth16.ppm" "" 89 1.7 252 cjpegli_test "jxl/flower/flower_small.g.depth8.pgm" "" 89 1.7 253 cjpegli_test "jxl/flower/flower_small.g.depth16.pgm" "" 89 1.7 254 255 cjpegli_djpegli_test "${rgb_in}" "" 89 1.7 256 cjpegli_djpegli_test "${rgb_in}" "--xyb" 87 1.5 257 258 djpegli_test "${ppm_rgb}" "-q 95" 92 259 djpegli_test "${ppm_rgb}" "-q 95 -sample 1x1" 93 260 djpegli_test "${ppm_gray}" "-q 95 -gray" 94 261 262 cjpeg_test "${ppm_rgb}" "" 89 1.9 263 cjpeg_test "${ppm_rgb}" "-optimize" 89 1.85 264 cjpeg_test "${ppm_rgb}" "-optimize -progressive" 89 1.8 265 cjpeg_test "${ppm_rgb}" "-sample 2x2" 87 1.65 266 cjpeg_test "${ppm_rgb}" "-sample 1x2" 88 1.75 267 cjpeg_test "${ppm_rgb}" "-sample 2x1" 88 1.75 268 cjpeg_test "${ppm_rgb}" "-grayscale" -50 1.45 269 cjpeg_test "${ppm_rgb}" "-rgb" 92 4.5 270 cjpeg_test "${ppm_rgb}" "-restart 1" 89 1.9 271 cjpeg_test "${ppm_rgb}" "-restart 1024B" 89 1.9 272 cjpeg_test "${ppm_rgb}" "-smooth 30" 88 1.75 273 cjpeg_test "${ppm_gray}" "-grayscale" 92 1.45 274 # The -q option works differently on v62 vs. v8 cjpeg binaries, so we have to 275 # have looser bounds than would be necessary if we sticked to a particular 276 # cjpeg version. 277 cjpeg_test "${ppm_rgb}" "-q 50" 76 0.95 278 cjpeg_test "${ppm_rgb}" "-q 80" 84 1.6 279 cjpeg_test "${ppm_rgb}" "-q 90" 89 2.35 280 cjpeg_test "${ppm_rgb}" "-q 100" 95 7.45 281 282 djpeg_test "${ppm_rgb}" "-q 95" 92 283 djpeg_test "${ppm_rgb}" "-q 95 -sample 1x1" 93 284 djpeg_test "${ppm_gray}" "-q 95 -gray" 94 285 } 286 287 main "$@"