123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300 |
- // Copyright 2012 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package jpeg
- import (
- "bytes"
- "fmt"
- "math"
- "math/rand"
- "testing"
- )
- func benchmarkDCT(b *testing.B, f func(*block)) {
- b.StopTimer()
- blocks := make([]block, 0, b.N*len(testBlocks))
- for i := 0; i < b.N; i++ {
- blocks = append(blocks, testBlocks[:]...)
- }
- b.StartTimer()
- for i := range blocks {
- f(&blocks[i])
- }
- }
- func BenchmarkFDCT(b *testing.B) {
- benchmarkDCT(b, fdct)
- }
- func BenchmarkIDCT(b *testing.B) {
- benchmarkDCT(b, idct)
- }
- func TestDCT(t *testing.T) {
- blocks := make([]block, len(testBlocks))
- copy(blocks, testBlocks[:])
- // Append some randomly generated blocks of varying sparseness.
- r := rand.New(rand.NewSource(123))
- for i := 0; i < 100; i++ {
- b := block{}
- n := r.Int() % 64
- for j := 0; j < n; j++ {
- b[r.Int()%len(b)] = r.Int31() % 256
- }
- blocks = append(blocks, b)
- }
- // Check that the FDCT and IDCT functions are inverses, after a scale and
- // level shift. Scaling reduces the rounding errors in the conversion from
- // floats to ints.
- for i, b := range blocks {
- got, want := b, b
- for j := range got {
- got[j] = (got[j] - 128) * 8
- }
- slowFDCT(&got)
- slowIDCT(&got)
- for j := range got {
- got[j] = got[j]/8 + 128
- }
- if differ(&got, &want) {
- t.Errorf("i=%d: IDCT(FDCT)\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
- }
- }
- // Check that the optimized and slow FDCT implementations agree.
- // The fdct function already does a scale and level shift.
- for i, b := range blocks {
- got, want := b, b
- fdct(&got)
- for j := range want {
- want[j] = (want[j] - 128) * 8
- }
- slowFDCT(&want)
- if differ(&got, &want) {
- t.Errorf("i=%d: FDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
- }
- }
- // Check that the optimized and slow IDCT implementations agree.
- for i, b := range blocks {
- got, want := b, b
- idct(&got)
- slowIDCT(&want)
- if differ(&got, &want) {
- t.Errorf("i=%d: IDCT\nsrc\n%s\ngot\n%s\nwant\n%s\n", i, &b, &got, &want)
- }
- }
- }
- // differ reports whether any pair-wise elements in b0 and b1 differ by 2 or
- // more. That tolerance is because there isn't a single definitive decoding of
- // a given JPEG image, even before the YCbCr to RGB conversion; implementations
- // can have different IDCT rounding errors.
- func differ(b0, b1 *block) bool {
- for i := range b0 {
- delta := b0[i] - b1[i]
- if delta < -2 || +2 < delta {
- return true
- }
- }
- return false
- }
- // alpha returns 1 if i is 0 and returns √2 otherwise.
- func alpha(i int) float64 {
- if i == 0 {
- return 1
- }
- return math.Sqrt2
- }
- var cosines [32]float64 // cosines[k] = cos(π/2 * k/8)
- func init() {
- for k := range cosines {
- cosines[k] = math.Cos(math.Pi * float64(k) / 16)
- }
- }
- // slowFDCT performs the 8*8 2-dimensional forward discrete cosine transform:
- //
- // dst[u,v] = (1/8) * Σ_x Σ_y alpha(u) * alpha(v) * src[x,y] *
- // cos((π/2) * (2*x + 1) * u / 8) *
- // cos((π/2) * (2*y + 1) * v / 8)
- //
- // x and y are in pixel space, and u and v are in transform space.
- //
- // b acts as both dst and src.
- func slowFDCT(b *block) {
- var dst [blockSize]float64
- for v := 0; v < 8; v++ {
- for u := 0; u < 8; u++ {
- sum := 0.0
- for y := 0; y < 8; y++ {
- for x := 0; x < 8; x++ {
- sum += alpha(u) * alpha(v) * float64(b[8*y+x]) *
- cosines[((2*x+1)*u)%32] *
- cosines[((2*y+1)*v)%32]
- }
- }
- dst[8*v+u] = sum / 8
- }
- }
- // Convert from float64 to int32.
- for i := range dst {
- b[i] = int32(dst[i] + 0.5)
- }
- }
- // slowIDCT performs the 8*8 2-dimensional inverse discrete cosine transform:
- //
- // dst[x,y] = (1/8) * Σ_u Σ_v alpha(u) * alpha(v) * src[u,v] *
- // cos((π/2) * (2*x + 1) * u / 8) *
- // cos((π/2) * (2*y + 1) * v / 8)
- //
- // x and y are in pixel space, and u and v are in transform space.
- //
- // b acts as both dst and src.
- func slowIDCT(b *block) {
- var dst [blockSize]float64
- for y := 0; y < 8; y++ {
- for x := 0; x < 8; x++ {
- sum := 0.0
- for v := 0; v < 8; v++ {
- for u := 0; u < 8; u++ {
- sum += alpha(u) * alpha(v) * float64(b[8*v+u]) *
- cosines[((2*x+1)*u)%32] *
- cosines[((2*y+1)*v)%32]
- }
- }
- dst[8*y+x] = sum / 8
- }
- }
- // Convert from float64 to int32.
- for i := range dst {
- b[i] = int32(dst[i] + 0.5)
- }
- }
- func (b *block) String() string {
- s := bytes.NewBuffer(nil)
- fmt.Fprintf(s, "{\n")
- for y := 0; y < 8; y++ {
- fmt.Fprintf(s, "\t")
- for x := 0; x < 8; x++ {
- fmt.Fprintf(s, "0x%04x, ", uint16(b[8*y+x]))
- }
- fmt.Fprintln(s)
- }
- fmt.Fprintf(s, "}")
- return s.String()
- }
- // testBlocks are the first 10 pre-IDCT blocks from ../testdata/video-001.jpeg.
- var testBlocks = [10]block{
- {
- 0x7f, 0xf6, 0x01, 0x07, 0xff, 0x00, 0x00, 0x00,
- 0xf5, 0x01, 0xfa, 0x01, 0xfe, 0x00, 0x01, 0x00,
- 0x05, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0xff, 0xf8, 0x00, 0x01, 0xff, 0x00, 0x00,
- 0x00, 0x01, 0x00, 0x01, 0x00, 0xff, 0xff, 0x00,
- 0xff, 0x0c, 0x00, 0x00, 0x00, 0x00, 0xff, 0x01,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x01, 0xff, 0x01, 0x00, 0xfe,
- },
- {
- 0x29, 0x07, 0x00, 0xfc, 0x01, 0x01, 0x00, 0x00,
- 0x07, 0x00, 0x03, 0x00, 0x01, 0x00, 0xff, 0xff,
- 0xff, 0xfd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x04, 0x00, 0xff, 0x01, 0x00, 0x00,
- 0x01, 0x00, 0x01, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0xfa, 0x01, 0x00, 0x01, 0x00, 0x01, 0xff,
- 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x02,
- },
- {
- 0xc5, 0xfa, 0x01, 0x00, 0x00, 0x01, 0x00, 0xff,
- 0x02, 0xff, 0x01, 0x00, 0x01, 0x00, 0xff, 0x00,
- 0xff, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
- 0xff, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
- 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- },
- {
- 0x86, 0x05, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00,
- 0xf2, 0x06, 0x00, 0x00, 0x01, 0x02, 0x00, 0x00,
- 0xf6, 0xfa, 0xf9, 0x00, 0xff, 0x01, 0x00, 0x00,
- 0xf9, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00,
- 0xff, 0x00, 0x00, 0x01, 0x00, 0xff, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x01, 0xff, 0x01, 0x00, 0xff, 0x00, 0x00,
- },
- {
- 0x24, 0xfe, 0x00, 0xff, 0x00, 0xff, 0xff, 0x00,
- 0x08, 0xfd, 0x00, 0x01, 0x01, 0x00, 0x01, 0x00,
- 0x06, 0x03, 0x03, 0xff, 0x00, 0x00, 0x00, 0x00,
- 0x04, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
- 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x01,
- 0x01, 0x00, 0x01, 0xff, 0x00, 0x01, 0x00, 0x00,
- 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff, 0x01,
- },
- {
- 0xcd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01,
- 0x03, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
- 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x00,
- 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0xff,
- },
- {
- 0x81, 0xfe, 0x05, 0xff, 0x01, 0xff, 0x01, 0x00,
- 0xef, 0xf9, 0x00, 0xf9, 0x00, 0xff, 0x00, 0xff,
- 0x05, 0xf9, 0x00, 0xf8, 0x01, 0xff, 0x01, 0xff,
- 0x00, 0xff, 0x07, 0x00, 0x01, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x01,
- 0xff, 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00,
- 0x01, 0x01, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff,
- },
- {
- 0x28, 0x00, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x0b, 0x02, 0x01, 0x03, 0x00, 0xff, 0x00, 0x01,
- 0xfe, 0x02, 0x01, 0x03, 0xff, 0x00, 0x00, 0x00,
- 0x01, 0x00, 0xfd, 0x00, 0x01, 0x00, 0xff, 0x00,
- 0x01, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0xff, 0x01, 0x01, 0x00, 0xff,
- 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x01,
- },
- {
- 0xdf, 0xf9, 0xfe, 0x00, 0x03, 0x01, 0xff, 0xff,
- 0x04, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x00, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0xff, 0x01, 0x00, 0x00, 0x00, 0x01,
- 0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
- 0x00, 0xff, 0x00, 0xff, 0x01, 0x00, 0x00, 0x01,
- 0xff, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
- },
- {
- 0x88, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x01, 0xff,
- 0xe1, 0x06, 0x06, 0x01, 0xff, 0x00, 0x01, 0x00,
- 0x08, 0x00, 0xfa, 0x00, 0xff, 0xff, 0xff, 0xff,
- 0x08, 0x01, 0x00, 0xff, 0x01, 0xff, 0x00, 0x00,
- 0xf5, 0xff, 0x00, 0x01, 0xff, 0x01, 0x01, 0x00,
- 0xff, 0xff, 0x01, 0xff, 0x01, 0x00, 0x01, 0x00,
- 0x00, 0x01, 0x01, 0xff, 0x00, 0xff, 0x00, 0x01,
- 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0xff, 0x00,
- },
- }
|