2 # Copyright (c) the JPEG XL Project Authors. All rights reserved.
4 # Use of this source code is governed by a BSD-style
5 # license that can be found in the LICENSE file.
7 # End-to-end roundtrip tests for cjpegli and djpegli tools, and other linux
8 # tools linked with the jpegli library.
12 MYDIR=$(dirname $(realpath "$0"))
13 JPEGXL_TEST_DATA_PATH="${MYDIR}/../../testdata"
15 # Temporary files cleanup hooks.
18 if [[ ${#CLEANUP_FILES[@]} -ne 0 ]]; then
19 rm -rf "${CLEANUP_FILES[@]}"
22 trap 'retcode=$?; { set +x; } 2>/dev/null; cleanup' INT TERM EXIT
24 verify_ssimulacra2() {
25 local score="$("${ssimulacra2}" "${1}" "${2}")"
26 python3 -c "import sys; sys.exit(not ${score} >= ${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})"
38 # Test that jpeg files created with cjpegli can be decoded with normal djpeg.
40 local infn="${JPEGXL_TEST_DATA_PATH}/$1"
44 local jpgfn="$(mktemp -p "${tmpdir}")"
45 local outfn="$(mktemp -p "${tmpdir}").ppm"
47 "${cjpegli}" "${infn}" "${jpgfn}" $encargs
48 djpeg -outfile "${outfn}" "${jpgfn}"
50 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}"
51 verify_max_bpp "${infn}" "${jpgfn}" "${maxbpp}"
54 # Test full cjpegli/djpegli roundtrip.
55 cjpegli_djpegli_test() {
56 local infn="${JPEGXL_TEST_DATA_PATH}/$1"
60 local jpgfn="$(mktemp -p "${tmpdir}")"
61 local outfn="$(mktemp -p "${tmpdir}").png"
63 "${cjpegli}" "${infn}" "${jpgfn}" $encargs
64 "${djpegli}" "${jpgfn}" "${outfn}"
66 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}"
67 verify_max_bpp "${infn}" "${jpgfn}" "${maxbpp}"
70 # Test the --target_size command line argument of cjpegli.
71 cjpegli_test_target_size() {
72 local infn="${JPEGXL_TEST_DATA_PATH}/$1"
74 local target_size="$3"
75 local jpgfn="$(mktemp -p "$tmpdir")"
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})"
83 # Test that jpeg files created with cjpeg binary + jpegli library can be decoded
84 # with normal libjpeg.
86 local infn="${JPEGXL_TEST_DATA_PATH}/$1"
90 local jpgfn="$(mktemp -p "$tmpdir")"
91 local outfn="$(mktemp -p "${tmpdir}").png"
93 LD_LIBRARY_PATH="${build_dir}/lib/jpegli:${LD_LIBRARY_PATH:-}" \
94 cjpeg $encargs -outfile "${jpgfn}" "${infn}"
95 djpeg -outfile "${outfn}" "${jpgfn}"
97 verify_ssimulacra2 "${infn}" "${outfn}" "${minscore}"
98 verify_max_bpp "${infn}" "${jpgfn}" "${maxbpp}"
101 # Test decoding of jpeg files with the djpegli binary.
103 local infn="${JPEGXL_TEST_DATA_PATH}/$1"
106 local jpgfn="$(mktemp -p "$tmpdir")"
108 cjpeg $encargs -outfile "${jpgfn}" "${infn}"
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
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}"
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}"
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})"
142 # Test decoding of jpeg files with the djpeg binary + jpegli library.
144 local infn="${JPEGXL_TEST_DATA_PATH}/$1"
147 local jpgfn="$(mktemp -p "$tmpdir")"
149 cjpeg $encargs -outfile "${jpgfn}" "${infn}"
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}"
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
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
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
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
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}"
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}"
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
213 local tmpdir=$(mktemp -d)
214 CLEANUP_FILES+=("${tmpdir}")
216 local build_dir="${1:-}"
217 if [[ -z "${build_dir}" ]]; then
218 build_dir=$(realpath "${MYDIR}/../../build")
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"
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
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
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
255 cjpegli_djpegli_test "${rgb_in}" "" 89 1.7
256 cjpegli_djpegli_test "${rgb_in}" "--xyb" 87 1.5
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
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
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
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