File Coverage

blib/lib/Math/DCT.pm
Criterion Covered Total %
statement 52 52 100.0
branch 22 22 100.0
condition 16 18 88.8
subroutine 9 9 100.0
pod 5 5 100.0
total 104 106 98.1


line stmt bran cond sub pod time code
1             package Math::DCT;
2              
3 4     4   654441 use 5.008;
  4         26  
4 4     4   18 use strict;
  4         6  
  4         65  
5 4     4   14 use warnings;
  4         7  
  4         223  
6              
7             =head1 NAME
8              
9             Math::DCT - 1D and NxN 2D Fast Discreet Cosine Transforms (DCT-II)
10              
11             =head1 SYNOPSIS
12              
13             use Math::DCT qw/dct dct1d dct2d idct1d idct2d/;
14              
15             my $dct1d = dct([[1,2,3,4]]);
16             $dct1d = dct1d([1,2,3,4]);
17              
18             my $dct2d = dct([[1,2],[3,4]]);
19             $dct2d = dct2d([1,2,3,4]);
20              
21             my $idct1d = idct1d([1,2,3,4]);
22             my $idct2d = idct2d([1,2,3,4]);
23              
24             =head1 VERSION
25              
26             Version 0.04_0
27              
28             =cut
29              
30             our $VERSION = '0.04_0';
31              
32             require XSLoader;
33             XSLoader::load('Math::DCT', $VERSION);
34              
35 4     4   19 use Exporter qw(import);
  4         15  
  4         2519  
36              
37             our @EXPORT_OK = qw(
38             dct
39             dct1d
40             dct2d
41             idct1d
42             idct2d
43             );
44              
45             our %EXPORT_TAGS;
46             $EXPORT_TAGS{all} = [@EXPORT_OK];
47              
48             =head1 DESCRIPTION
49              
50             An unscaled DCT-II implementation for 1D and NxN 2D matrices implemented in XS.
51             For array sizes which are a power of 2, a fast algorithm (FCT) described by Lee is
52             used (with the addition of a coefficient table that makes it even faster than some
53             common implementations), plus an unscaled version of the Arai, Agui, Nakajima
54             algorithm is used for size 1x8, 8x8 matrices. A less optimized algorithm is used
55             for the generic case, so any 1D or square 2D matrix can be processed.
56              
57             For convenience the inverse functions are provided (inverse DCT-II, usually called
58             iDCT, is essentially a scaled DCT-III), although using just one generic algorithm.
59              
60             The module was written for a perceptual hash project that needed 32x32 DCT-II, and
61             on a 2.5GHz i7 2015 Macbook Pro about 18000/s per thread are processed (C function).
62             The common 8x8 DCT-II uses a special path, (about 380000/s on that same CPU),
63             although for most image/video applications that require 8x8 DCT there are much faster
64             implementations (SIMD, approximations etc) that usually produce an already scaled result
65             for the specific application.
66              
67             None of the algorithms used on this module are approximate, the test suite verifies
68             against a naive DCT-II implementation with a tolerance of 1e-08.
69              
70             =head1 METHODS
71            
72             =head2 C
73              
74             my $dct = dct([[1,2],[3,4]]);
75              
76             Pass an array (ref) of either a single array, or N N-length arrays for 1D and 2D
77             DCT-II calculation respectivelly. The output will be an arrayref of array(s) with
78             the result of the transform.
79              
80             =cut
81              
82             sub dct {
83 19     19 1 4592 my $vector = shift;
84 19 100 100     205 die "Expect array of array(s)"
      100        
      100        
85             unless $vector && ref $vector eq 'ARRAY'
86             && $vector->[0] && ref $vector->[0] eq 'ARRAY';
87              
88 15         30 my $dim = scalar(@$vector);
89 15         17 my $sz = scalar(@{$vector->[0]});
  15         52  
90 15 100 100     70 die "Expect 1d or NxN 2d arrays" unless $dim == 1 || $dim == $sz;
91              
92 14         39 my $pack;
93 14         45 for (my $x = 0; $x < $dim; $x++) {
94 131         167 $pack .= pack "d$sz", @{$vector->[$x]}
  131         481  
95             }
96              
97 14 100       43 if ($dim > 1) {
98 7 100       688 $sz == 8
    100          
99             ? fct8_2d($pack)
100             : 0 == ($sz & ($sz - 1)) ? fast_dct_2d($pack, $sz) : dct_2d($pack, $sz);
101             } else {
102 7 100       67 $sz == 8
    100          
103             ? fct8_1d($pack)
104             : 0 == ($sz & ($sz - 1)) ? fast_dct_1d($pack, $sz) : dct_1d($pack, $sz);
105             }
106              
107 14         25 my $result;
108 14         45 foreach (0..$dim-1) {
109 131         701 $result->[$_] = [unpack "d".($sz), substr $pack, $_ * $sz*8, $sz*8];
110             }
111 14         55 return $result;
112             }
113              
114             =head2 C
115              
116             my $dct = dct1d([1,2,3]);
117              
118             Pass an array (ref) for a 1D DCT-II calculation. The output will be an arrayref
119             with the result of the transform.
120              
121             =cut
122              
123             sub dct1d {
124 7     7 1 582255 my $input = shift;
125 7         15 my $sz = scalar @$input;
126 7         50 my $pack = pack "d$sz", @$input;
127 7 100       103 $sz == 8
    100          
128             ? fct8_1d($pack)
129             : 0 == ($sz & ($sz - 1)) ? fast_dct_1d($pack, $sz) : dct_1d($pack, $sz);
130 7         42 my @result = unpack "d$sz", $pack;
131 7         29 return \@result;
132             }
133              
134             =head2 C
135              
136             my $idct = idct1d([1,2,3]);
137              
138             Pass an array (ref) for a 1D iDCT-II calculation. The output will be an arrayref
139             with the result of the transform. This is essentially a DCT-III transform scaled
140             by 2/N.
141              
142             =cut
143              
144             sub idct1d {
145 6     6 1 75586 my $input = shift;
146 6         13 my $sz = scalar @$input;
147 6         39 my $pack = pack "d$sz", @$input;
148 6         366 idct_1d($pack, $sz);
149 6         68 my @result = unpack "d$sz", $pack;
150 6         25 return \@result;
151             }
152              
153             =head2 C
154              
155             my $dct = dct2d(
156             [1,2,3,4], # Arrayref containing your NxN matrix
157             2 # Optionally, the size N of your array (sqrt of its length)
158             );
159              
160             Pass an array (ref) for a 2D DCT-II calculation. The length of the array is expected
161             to be a square (as only NxN arrays are supported) - you can optionally pass N as
162             the second argument to avoid a C calculation.
163             The output will be an arrayref with the result of the transform.
164              
165             If your 2D data is available in a 1D array as is usual with most image manipulation
166             etc cases, this function will be faster than C, as the DCT calculation is
167             in any case done on a 1D array, hence you save the cost of conversion.
168              
169             =cut
170              
171             sub dct2d {
172 10     10 1 650221 my $input = shift;
173 10   100     86 my $sz = shift || sqrt(scalar @$input);
174 9         461 my $pack = pack "d".($sz*$sz), @$input;
175 9 100       710 $sz == 8
    100          
176             ? fct8_2d($pack)
177             : 0 == ($sz & ($sz - 1)) ? fast_dct_2d($pack, $sz) : dct_2d($pack, $sz);
178 9         340 my @result = unpack "d".($sz*$sz), $pack;
179 9         58 return \@result;
180             }
181              
182             =head2 C
183              
184             my $idct = idct2d(
185             [1,2,3,4], # Arrayref containing your NxN matrix
186             2 # Optionally, the size N of your array (sqrt of its length)
187             );
188              
189             Pass an array (ref) for a 2D iDCT-II calculation. The length of the array is expected
190             to be a square (as only NxN arrays are supported) - you can optionally pass N as
191             the second argument to avoid a C calculation.
192             This is essentially a DCT-III transform scaled by 2/N.
193             The output will be an arrayref with the result of the transform.
194              
195             =cut
196              
197             sub idct2d {
198 6     6 1 3143055 my $input = shift;
199 6   33     49 my $sz = shift || sqrt(scalar @$input);
200 6         197 my $pack = pack "d".($sz*$sz), @$input;
201 6         2735 idct_2d($pack, $sz);
202 6         254 my @result = unpack "d".($sz*$sz), $pack;
203 6         42 return \@result;
204             }
205              
206             =head1 USAGE NOTES
207              
208             The C functions are not exported, but theoretically you could use them directly
209             if you do your own C. The fast versions for power-of-2 size arrays
210             are C and C, while the generic versions are C
211             and C (with inverse functions as C and C).
212             The specialized size-8 versions are C and C.
213             First argument is a C (use C), second is the size N (except
214             for the fct8* functions which don't need a second argument).
215              
216             =head1 ACKNOWLEDGEMENTS
217              
218             C code for 1D DCT from Project Nayuki was adapted and improved where possible.
219              
220             (L)
221              
222             =head1 AUTHOR
223              
224             Dimitrios Kechagias, C<< >>
225            
226             =head1 BUGS
227              
228             Please report any bugs or feature requests to C,
229             or through the web interface at L.
230             I will be notified, and then you'll automatically be notified of progress on your
231             bug as I make changes.
232              
233             =head1 GIT
234              
235             L
236            
237             =head1 COPYRIGHT & LICENSE
238              
239             Copyright (C) 2019, SpareRoom.com
240              
241             This program is free software; you can redistribute
242             it and/or modify it under the same terms as Perl itself.
243              
244             =cut
245              
246             1;