2012-10-23 06:31:11 +02:00
|
|
|
|
// 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++ {
|
2012-11-21 08:03:38 +01:00
|
|
|
|
b[r.Int()%len(b)] = r.Int31() % 256
|
2012-10-23 06:31:11 +02:00
|
|
|
|
}
|
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-06 20:49:01 +01:00
|
|
|
|
// differ reports whether any pair-wise elements in b0 and b1 differ by 2 or
|
2012-10-23 06:31:11 +02:00
|
|
|
|
// 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
|
|
|
|
|
}
|
|
|
|
|
|
2012-11-21 08:03:38 +01:00
|
|
|
|
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)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-10-23 06:31:11 +02:00
|
|
|
|
// 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]) *
|
2012-11-21 08:03:38 +01:00
|
|
|
|
cosines[((2*x+1)*u)%32] *
|
|
|
|
|
cosines[((2*y+1)*v)%32]
|
2012-10-23 06:31:11 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dst[8*v+u] = sum / 8
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-11-21 08:03:38 +01:00
|
|
|
|
// Convert from float64 to int32.
|
2012-10-23 06:31:11 +02:00
|
|
|
|
for i := range dst {
|
2012-11-21 08:03:38 +01:00
|
|
|
|
b[i] = int32(dst[i] + 0.5)
|
2012-10-23 06:31:11 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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]) *
|
2012-11-21 08:03:38 +01:00
|
|
|
|
cosines[((2*x+1)*u)%32] *
|
|
|
|
|
cosines[((2*y+1)*v)%32]
|
2012-10-23 06:31:11 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
dst[8*y+x] = sum / 8
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-11-21 08:03:38 +01:00
|
|
|
|
// Convert from float64 to int32.
|
2012-10-23 06:31:11 +02:00
|
|
|
|
for i := range dst {
|
2012-11-21 08:03:38 +01:00
|
|
|
|
b[i] = int32(dst[i] + 0.5)
|
2012-10-23 06:31:11 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
},
|
|
|
|
|
}
|