Retro68/gcc/libgo/go/image/jpeg/dct_test.go
2014-09-21 19:33:12 +02:00

300 lines
8.6 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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,
},
}