dct_test.go 8.6 KB

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