tinygo.org/x/drivers@v0.27.1-0.20240509133757-7dbca2a54349/image/jpeg/dct_test.go (about) 1 // Copyright 2012 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package jpeg 6 7 import ( 8 "bytes" 9 "fmt" 10 "math" 11 "math/rand" 12 "testing" 13 ) 14 15 func benchmarkDCT(b *testing.B, f func(*block)) { 16 b.StopTimer() 17 blocks := make([]block, 0, b.N*len(testBlocks)) 18 for i := 0; i < b.N; i++ { 19 blocks = append(blocks, testBlocks[:]...) 20 } 21 b.StartTimer() 22 for i := range blocks { 23 f(&blocks[i]) 24 } 25 } 26 27 func BenchmarkFDCT(b *testing.B) { 28 benchmarkDCT(b, fdct) 29 } 30 31 func BenchmarkIDCT(b *testing.B) { 32 benchmarkDCT(b, idct) 33 } 34 35 func TestDCT(t *testing.T) { 36 blocks := make([]block, len(testBlocks)) 37 copy(blocks, testBlocks[:]) 38 39 // Append some randomly generated blocks of varying sparseness. 40 r := rand.New(rand.NewSource(123)) 41 for i := 0; i < 100; i++ { 42 b := block{} 43 n := r.Int() % 64 44 for j := 0; j < n; j++ { 45 b[r.Int()%len(b)] = r.Int31() % 256 46 } 47 blocks = append(blocks, b) 48 } 49 50 // Check that the FDCT and IDCT functions are inverses, after a scale and 51 // level shift. Scaling reduces the rounding errors in the conversion from 52 // floats to ints. 53 for i, b := range blocks { 54 got, want := b, b 55 for j := range got { 56 got[j] = (got[j] - 128) * 8 57 } 58 slowFDCT(&got) 59 slowIDCT(&got) 60 for j := range got { 61 got[j] = got[j]/8 + 128 62 } 63 if differ(&got, &want) { 64 t.Errorf("i=%d: IDCT(FDCT)\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want) 65 } 66 } 67 68 // Check that the optimized and slow FDCT implementations agree. 69 // The fdct function already does a scale and level shift. 70 for i, b := range blocks { 71 got, want := b, b 72 fdct(&got) 73 for j := range want { 74 want[j] = (want[j] - 128) * 8 75 } 76 slowFDCT(&want) 77 if differ(&got, &want) { 78 t.Errorf("i=%d: FDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want) 79 } 80 } 81 82 // Check that the optimized and slow IDCT implementations agree. 83 for i, b := range blocks { 84 got, want := b, b 85 idct(&got) 86 slowIDCT(&want) 87 if differ(&got, &want) { 88 t.Errorf("i=%d: IDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want) 89 } 90 } 91 } 92 93 // differ reports whether any pair-wise elements in b0 and b1 differ by 2 or 94 // more. That tolerance is because there isn't a single definitive decoding of 95 // a given JPEG image, even before the YCbCr to RGB conversion; implementations 96 // can have different IDCT rounding errors. 97 func differ(b0, b1 *block) bool { 98 for i := range b0 { 99 delta := b0[i] - b1[i] 100 if delta < -2 || +2 < delta { 101 return true 102 } 103 } 104 return false 105 } 106 107 // alpha returns 1 if i is 0 and returns √2 otherwise. 108 func alpha(i int) float64 { 109 if i == 0 { 110 return 1 111 } 112 return math.Sqrt2 113 } 114 115 var cosines [32]float64 // cosines[k] = cos(π/2 * k/8) 116 117 func init() { 118 for k := range cosines { 119 cosines[k] = math.Cos(math.Pi * float64(k) / 16) 120 } 121 } 122 123 // slowFDCT performs the 8*8 2-dimensional forward discrete cosine transform: 124 // 125 // dst[u,v] = (1/8) * Σ_x Σ_y alpha(u) * alpha(v) * src[x,y] * 126 // cos((π/2) * (2*x + 1) * u / 8) * 127 // cos((π/2) * (2*y + 1) * v / 8) 128 // 129 // x and y are in pixel space, and u and v are in transform space. 130 // 131 // b acts as both dst and src. 132 func slowFDCT(b *block) { 133 var dst [blockSize]float64 134 for v := 0; v < 8; v++ { 135 for u := 0; u < 8; u++ { 136 sum := 0.0 137 for y := 0; y < 8; y++ { 138 for x := 0; x < 8; x++ { 139 sum += alpha(u) * alpha(v) * float64(b[8*y+x]) * 140 cosines[((2*x+1)*u)%32] * 141 cosines[((2*y+1)*v)%32] 142 } 143 } 144 dst[8*v+u] = sum / 8 145 } 146 } 147 // Convert from float64 to int32. 148 for i := range dst { 149 b[i] = int32(dst[i] + 0.5) 150 } 151 } 152 153 // slowIDCT performs the 8*8 2-dimensional inverse discrete cosine transform: 154 // 155 // dst[x,y] = (1/8) * Σ_u Σ_v alpha(u) * alpha(v) * src[u,v] * 156 // cos((π/2) * (2*x + 1) * u / 8) * 157 // cos((π/2) * (2*y + 1) * v / 8) 158 // 159 // x and y are in pixel space, and u and v are in transform space. 160 // 161 // b acts as both dst and src. 162 func slowIDCT(b *block) { 163 var dst [blockSize]float64 164 for y := 0; y < 8; y++ { 165 for x := 0; x < 8; x++ { 166 sum := 0.0 167 for v := 0; v < 8; v++ { 168 for u := 0; u < 8; u++ { 169 sum += alpha(u) * alpha(v) * float64(b[8*v+u]) * 170 cosines[((2*x+1)*u)%32] * 171 cosines[((2*y+1)*v)%32] 172 } 173 } 174 dst[8*y+x] = sum / 8 175 } 176 } 177 // Convert from float64 to int32. 178 for i := range dst { 179 b[i] = int32(dst[i] + 0.5) 180 } 181 } 182 183 func (b *block) String() string { 184 s := bytes.NewBuffer(nil) 185 fmt.Fprintf(s, "{\n") 186 for y := 0; y < 8; y++ { 187 fmt.Fprintf(s, "\t") 188 for x := 0; x < 8; x++ { 189 fmt.Fprintf(s, "0x%04x, ", uint16(b[8*y+x])) 190 } 191 fmt.Fprintln(s) 192 } 193 fmt.Fprintf(s, "}") 194 return s.String() 195 } 196 197 // testBlocks are the first 10 pre-IDCT blocks from ../testdata/video-001.jpeg. 198 var testBlocks = [10]block{ 199 { 200 0x7f, 0xf6, 0x01, 0x07, 0xff, 0x00, 0x00, 0x00, 201 0xf5, 0x01, 0xfa, 0x01, 0xfe, 0x00, 0x01, 0x00, 202 0x05, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 203 0x01, 0xff, 0xf8, 0x00, 0x01, 0xff, 0x00, 0x00, 204 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00, 205 0xff, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01, 206 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 207 0x01, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0xfe, 208 }, 209 { 210 0x29, 0x07, 0x00, 0xfc, 0x01, 0x01, 0x00, 0x00, 211 0x07, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff, 212 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 213 0x00, 0x00, 0x04, 0x00, 0xff, 0x01, 0x00, 0x00, 214 0x01, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00, 215 0x01, 0xfa, 0x01, 0x00, 0x01, 0x00, 0x01, 0xff, 216 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 217 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x02, 218 }, 219 { 220 0xc5, 0xfa, 0x01, 0x00, 0x00, 0x01, 0x00, 0xff, 221 0x02, 0xff, 0x01, 0x00, 0x01, 0x00, 0xff, 0x00, 222 0xff, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 223 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00, 224 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 225 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 226 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 227 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 228 }, 229 { 230 0x86, 0x05, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 231 0xf2, 0x06, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00, 232 0xf6, 0xfa, 0xf9, 0x00, 0xff, 0x01, 0x00, 0x00, 233 0xf9, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 234 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 235 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0x01, 0x00, 236 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01, 237 0x00, 0x01, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00, 238 }, 239 { 240 0x24, 0xfe, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00, 241 0x08, 0xfd, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00, 242 0x06, 0x03, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 243 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 244 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01, 245 0x01, 0x00, 0x01, 0xff, 0x00, 0x01, 0x00, 0x00, 246 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 247 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x01, 248 }, 249 { 250 0xcd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 251 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 252 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 253 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 254 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00, 255 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 256 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 257 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff, 258 }, 259 { 260 0x81, 0xfe, 0x05, 0xff, 0x01, 0xff, 0x01, 0x00, 261 0xef, 0xf9, 0x00, 0xf9, 0x00, 0xff, 0x00, 0xff, 262 0x05, 0xf9, 0x00, 0xf8, 0x01, 0xff, 0x01, 0xff, 263 0x00, 0xff, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00, 264 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 265 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01, 266 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 267 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 268 }, 269 { 270 0x28, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 271 0x0b, 0x02, 0x01, 0x03, 0x00, 0xff, 0x00, 0x01, 272 0xfe, 0x02, 0x01, 0x03, 0xff, 0x00, 0x00, 0x00, 273 0x01, 0x00, 0xfd, 0x00, 0x01, 0x00, 0xff, 0x00, 274 0x01, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 275 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff, 276 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 277 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 278 }, 279 { 280 0xdf, 0xf9, 0xfe, 0x00, 0x03, 0x01, 0xff, 0xff, 281 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 282 0xff, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01, 283 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00, 284 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01, 285 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 286 0x00, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x01, 287 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 288 }, 289 { 290 0x88, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff, 291 0xe1, 0x06, 0x06, 0x01, 0xff, 0x00, 0x01, 0x00, 292 0x08, 0x00, 0xfa, 0x00, 0xff, 0xff, 0xff, 0xff, 293 0x08, 0x01, 0x00, 0xff, 0x01, 0xff, 0x00, 0x00, 294 0xf5, 0xff, 0x00, 0x01, 0xff, 0x01, 0x01, 0x00, 295 0xff, 0xff, 0x01, 0xff, 0x01, 0x00, 0x01, 0x00, 296 0x00, 0x01, 0x01, 0xff, 0x00, 0xff, 0x00, 0x01, 297 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00, 298 }, 299 }