Imported Upstream version 0.9.0
[platform/upstream/libjxl.git] / tools / scripts / jpegli_tools_test.sh
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 "$@"