File Coverage

blib/lib/Math/PSNR.pm
Criterion Covered Total %
statement 109 111 98.2
branch 28 36 77.7
condition 18 18 100.0
subroutine 20 20 100.0
pod 4 4 100.0
total 179 189 94.7


line stmt bran cond sub pod time code
1             package Math::PSNR;
2              
3 6     6   45127 use warnings;
  6         15  
  6         211  
4 6     6   36 use strict;
  6         9  
  6         243  
5 6     6   1855 use utf8;
  6         27  
  6         489  
6 6     6   185 use Carp;
  6         13  
  6         636  
7 6     6   7091 use Mouse;
  6         876441  
  6         33  
8              
9             our $VERSION = '0.02';
10              
11             has bpp => (
12             is => 'rw',
13             isa => 'Int',
14             default => '8',
15             trigger => sub {
16             my $self = shift;
17             $self->_set_max_power( $self->_calc_max_power );
18             },
19             );
20              
21             has x => (
22             is => 'rw',
23             isa => 'ArrayRef|HashRef',
24             required => '1',
25             trigger => sub {
26             my $self = shift;
27             $self->_reset_cache;
28             },
29             );
30              
31             has y => (
32             is => 'rw',
33             isa => 'ArrayRef|HashRef',
34             required => '1',
35             trigger => sub {
36             my $self = shift;
37             $self->_reset_cache;
38             },
39             );
40              
41             has max_power => (
42             is => 'ro',
43             isa => 'Int',
44             writer => '_set_max_power',
45             init_arg => undef,
46             lazy => '1',
47             default => sub {
48             my $self = shift;
49             return $self->_calc_max_power;
50             },
51             trigger => sub {
52             my $self = shift;
53             $self->_reset_cache;
54             },
55             );
56              
57             has mse_cache => (
58             is => 'ro',
59             isa => 'Num|Undef',
60             writer => '_set_mse_cache',
61             init_arg => undef,
62             default => undef,
63             );
64              
65             has psnr_cache => (
66             is => 'ro',
67             isa => 'Num|Undef',
68             writer => '_set_psnr_cache',
69             init_arg => undef,
70             default => undef,
71             );
72              
73 6     6   3721 no Mouse;
  6         14  
  6         39  
74              
75             sub _reset_cache {
76 71     71   84 my $self = shift;
77 71         220 $self->_set_mse_cache(undef);
78 71         287 $self->_set_psnr_cache(undef);
79             }
80              
81             sub _sqr {
82 79     79   79 my $var = shift;
83 79         243 return $var * $var;
84             }
85              
86             sub _log10 {
87 2     2   3 my $var = shift;
88 2         13 return log($var) / log(10);
89             }
90              
91             sub _calc_max_power {
92 8     8   14 my $self = shift;
93 8         76 return 2**$self->bpp - 1;
94             }
95              
96             sub _limit {
97 158     158   171 my ( $self, $var ) = @_;
98              
99 158 50       466 if ( $var < 0 ) {
    50          
100 0         0 return 0;
101             }
102             elsif ( $var > $self->max_power ) {
103 0         0 return $self->max_power;
104             }
105 158         222 return $var;
106             }
107              
108             sub _square_remainder {
109 79     79   95 my ( $self, $x, $y ) = @_;
110              
111 79         131 $x = $self->_limit($x);
112 79         151 $y = $self->_limit($y);
113              
114 79         150 return _sqr( $x - $y );
115             }
116              
117             sub _calc_psnr {
118 2     2   5 my ( $self, $mse ) = @_;
119 2         24 return 20 * _log10( $self->max_power / sqrt($mse) );
120             }
121              
122             sub _check_exist_key {
123 102     102   145 my ( $self, $key ) = @_;
124              
125 102 100 100     751 unless ( exists $self->x->{$key} && exists $self->y->{$key} ) {
126 12         147 croak "Hash of signal must have key of '$key'.";
127             }
128              
129 90 100 100     642 unless ( ref $self->x->{$key} eq 'ARRAY'
130             && ref $self->y->{$key} eq 'ARRAY' )
131             {
132 12         166 croak "Value of '$key' must be numerical array reference. ";
133             }
134             }
135              
136             sub _check_exist_rgb_keys {
137 42     42   64 my $self = shift;
138              
139 42         173 $self->_check_exist_key('r');
140 34         68 $self->_check_exist_key('g');
141 26         51 $self->_check_exist_key('b');
142             }
143              
144             sub _check_signal_length_each {
145 18     18   25 my $self = shift;
146              
147 18         25 my $signal_length_x = $#{ $self->x->{'r'} };
  18         58  
148 18 100 100     25 unless ( $signal_length_x == $#{ $self->x->{'g'} }
  18         99  
  14         69  
149             && $signal_length_x == $#{ $self->x->{'b'} } )
150             {
151 6         94 croak
152             "Each elements of signal must be the same length. Please check out the length of 'r', 'g', and 'b' of signal x.";
153             }
154              
155 12         15 my $signal_length_y = $#{ $self->y->{'r'} };
  12         55  
156 12 100 100     18 unless ( $signal_length_y == $#{ $self->y->{'g'} }
  12         53  
  8         40  
157             && $signal_length_y == $#{ $self->y->{'b'} } )
158             {
159 6         81 croak
160             "Each elements of signal must be the same length. Please check out the length of 'r', 'g', and 'b' of signal y.";
161             }
162              
163 6 100       20 unless ( $signal_length_x == $signal_length_y ) {
164 2         42 croak "Signal length are different between 'Signal x' and 'Signal y'.";
165             }
166             }
167              
168             sub mse {
169 12     12 1 4405 my $self = shift;
170              
171 12 50       40 return $self->mse_cache if defined( $self->mse_cache ); # Enable Cache
172              
173 12 100 100     87 unless ( ref $self->x eq 'ARRAY' && ref $self->y eq 'ARRAY' ) {
174 4         41 croak 'Signals must be array reference.';
175             }
176              
177 8         10 my $signal_length = scalar @{ $self->x };
  8         20  
178 8 100       9 unless ( $signal_length == scalar @{ $self->y } ) {
  8         23  
179 4         53 croak 'Signals must be the same length.';
180             }
181              
182 4         13 my ( $x, $y ) = ( $self->x, $self->y );
183 4         8 my $sum = 0;
184 4         12 for my $i ( 0 .. $signal_length - 1 ) {
185 19         43 $sum += $self->_square_remainder( $x->[$i], $y->[$i] );
186             }
187              
188 4         9 my $mse = $sum / $signal_length;
189              
190 4         14 $self->_set_mse_cache($mse);
191 4         7 return $mse;
192             }
193              
194             sub psnr {
195 2     2 1 942 my $self = shift;
196              
197 2 50       10 return $self->psnr_cache if defined( $self->psnr_cache ); # Enable Cache
198              
199 2 50       36 my $mse = defined( $self->mse_cache ) ? $self->mse_cache : $self->mse;
200 2 100       6 if ( $mse == 0 ) {
201 1         25 carp 'Given signals are the same.';
202 1         554 return 'same';
203             }
204              
205 1         4 my $psnr = $self->_calc_psnr($mse);
206              
207 1         3 $self->_set_psnr_cache($psnr);
208 1         3 return $psnr;
209             }
210              
211             sub mse_rgb {
212 46     46 1 29564 my $self = shift;
213              
214 46 50       179 return $self->mse_cache if defined( $self->mse_cache ); # Enable Cache
215              
216 46 100 100     441 unless ( ref $self->x eq 'HASH' && ref $self->y eq 'HASH' ) {
217 4         255 croak 'Signals must be hash reference.';
218             }
219              
220 42         105 $self->_check_exist_rgb_keys;
221 18         46 $self->_check_signal_length_each;
222              
223 4         5 my $signal_length = scalar @{ $self->x->{'r'} };
  4         11  
224              
225 4         16 my ( $x, $y ) = ( $self->x, $self->y );
226 4         8 my $sum = 0;
227 4         32 for my $i ( 0 .. $signal_length - 1 ) {
228 20         56 $sum +=
229             $self->_square_remainder( $x->{'r'}->[$i], $y->{'r'}->[$i] ) +
230             $self->_square_remainder( $x->{'g'}->[$i], $y->{'g'}->[$i] ) +
231             $self->_square_remainder( $x->{'b'}->[$i], $y->{'b'}->[$i] );
232             }
233              
234 4         13 my $mse = $sum / ( 3 * $signal_length );
235              
236 4         15 $self->_set_mse_cache($mse);
237 4         11 return $mse;
238             }
239              
240             sub psnr_rgb {
241 2     2 1 1115 my $self = shift;
242              
243 2 50       11 return $self->psnr_cache if defined( $self->psnr_cache ); # Enable Cache
244              
245 2 50       23 my $mse = defined( $self->mse_cache ) ? $self->mse_cache : $self->mse_rgb;
246 2 100       7 if ( $mse == 0 ) {
247 1         30 carp 'Given signals are the same.';
248 1         627 return 'same';
249             }
250              
251 1         4 my $psnr = $self->_calc_psnr($mse);
252              
253 1         11 $self->_set_psnr_cache($psnr);
254 1         4 return $psnr;
255             }
256              
257             1;
258             __END__