File Coverage

blib/lib/Color/Similarity/HCL.pm
Criterion Covered Total %
statement 53 54 98.1
branch 14 14 100.0
condition 12 12 100.0
subroutine 13 14 92.8
pod 3 3 100.0
total 95 97 97.9


line stmt bran cond sub pod time code
1             package Color::Similarity::HCL;
2              
3             =head1 NAME
4              
5             Color::Similarity::HCL - compute color similarity using the HCL color space
6              
7             =head1 SYNOPSIS
8              
9             use Color::Similarity::HCL qw(distance rgb2hcl distance_hcl);
10             # the greater the distance, more different the colors
11             my $distance = distance( [ $r1, $g1, $b1 ], [ $r2, $g2, $b2 ] );
12              
13             =head1 DESCRIPTION
14              
15             Computes color similarity using the color space and distance metric
16             defined in the research report:
17              
18             HCL: a new Color Space for a more Effective Content-based Image
19             Retrieval
20              
21             M. Sarifuddin - Rokia Missaoui
22             DEpartement d'informatique et d'ingEnierie,
23             UniversitE du QuEbec en Outaouais
24             C.P. 1250, Succ. B Gatineau
25             QuEebec Canada, J8X 3X7
26              
27             L
28              
29             =cut
30              
31 2     2   1461 use strict;
  2         6  
  2         74  
32 2     2   11 use base qw(Exporter);
  2         3  
  2         253  
33              
34             our $VERSION = '0.05';
35             our @EXPORT_OK = qw(rgb2hcl distance distance_hcl);
36              
37 2     2   22 use List::Util qw(max min);
  2         3  
  2         274  
38 2     2   2856 use Math::Trig qw(pi rad2deg deg2rad atan);
  2         55126  
  2         258  
39              
40 2     2   23 use constant pip2 => pi / 2; # work around old Math::Trig
  2         4  
  2         161  
41 2     2   11 use constant Y0 => 100;
  2         3  
  2         81  
42 2     2   10 use constant gamma => 3;
  2         5  
  2         103  
43 2     2   9 use constant Al => 1.4456;
  2         5  
  2         173  
44 2     2   11 use constant Ah_inc => 0.16;
  2         2  
  2         1284  
45              
46             =head1 FUNCTIONS
47              
48             =head2 distance
49              
50             my $distance = distance( [ $r1, $g1, $b1 ], [ $r2, $g2, $b2 ] );
51              
52             Converts the colors to the HCL space and computes their distance.
53              
54             =cut
55              
56             sub distance {
57 50653     50653 1 29540705 my( $t1, $t2 ) = @_;
58              
59 50653         134396 return distance_hcl( rgb2hcl( @$t1 ), rgb2hcl( @$t2 ) );
60             }
61              
62             =head2 rgb2hcl
63              
64             [ $h, $c, $l ] = rgb2hcl( $r, $g, $b );
65              
66             Converts between RGB and HCL color spaces.
67              
68             =cut
69              
70             sub _atan {
71 101304     101304   152092 my( $y, $x ) = @_;
72              
73 101304 100       250274 return $y < 0 ? - pip2 : pip2 if $x == 0;
    100          
74 98568         345255 return atan( $y / $x );
75             }
76              
77             sub rgb2hcl {
78 101306     101306 1 181043 my( $r, $g, $b ) = @_;
79              
80 101306         356915 my( $min, $max ) = ( min( $r, $g, $b ), max( $r, $g, $b ) );
81 101306 100       266403 return [ 0, 0, 0 ] if $max == 0; # special-case black
82 101304         209952 my $alpha = ( $min / $max ) / Y0;
83 101304         203167 my $Q = exp( $alpha * gamma );
84              
85 101304         206371 my( $rg, $gb, $br ) = ( $r - $g, $g - $b, $b - $r );
86 101304         203367 my $L = ( $Q * $max + ( 1 - $Q ) * $min ) / 2;
87 101304         212769 my $C = $Q * ( abs( $rg ) + abs( $gb ) + abs( $br ) ) / 3;
88 101304         226437 my $H = rad2deg( _atan( $gb, $rg ) );
89              
90             # The paper uses 180, not 90, but using 180 gives
91             # red the same HCL value as green...
92             # Alternative A
93             # $H = 90 + $H if $rg < 0 && $gb >= 0;
94             # $H = $H - 90 if $rg < 0 && $gb < 0;
95             # Alternative B
96             # $H = 2 * $H / 3 if $rg >= 0 && $gb >= 0;
97             # $H = 4 * $H / 3 if $rg >= 0 && $gb < 0;
98             # $H = 90 + 4 * $H / 3 if $rg < 0 && $gb >= 0;
99             # $H = 3 * $H / 4 - 90 if $rg < 0 && $gb < 0;
100             # From http://w3.uqo.ca/missaoui/Publications/TRColorSpace.zip
101 101304 100 100     1663423 $H = 2 * $H / 3 if $rg >= 0 && $gb >= 0;
102 101304 100 100     352594 $H = 4 * $H / 3 if $rg >= 0 && $gb < 0;
103 101304 100 100     400070 $H = 180 + 4 * $H / 3 if $rg < 0 && $gb >= 0;
104 101304 100 100     369729 $H = 2 * $H / 3 - 180 if $rg < 0 && $gb < 0;
105              
106 101304         415273 return [ $H, $C, $L ];
107             }
108              
109             =head2 distance_hcl
110              
111             my $distance = distance_hcl( [ $h1, $c1, $l1 ], [ $h2, $c2, $l2 ] );
112              
113             Computes the distance between two colors in the HCL color space.
114              
115             =cut
116              
117             sub distance_hcl {
118 50653     50653 1 72427 my( $t1, $t2 ) = @_;
119 50653         86533 my( $h1, $c1, $l1 ) = @$t1;
120 50653         74070 my( $h2, $c2, $l2 ) = @$t2;
121              
122 50653         91665 my $Ah = abs( $h1 - $h2 ) + Ah_inc;
123 50653         96715 my( $Dl, $Dh ) = ( abs( $l1 - $l2 ), abs( $h1 - $h2 ) );
124             # here it used to use ** 2 to compute squares, but this causes
125             # some rounding problems
126 50653         64425 my $AlDl = Al * $Dl;
127 50653         174458 return sqrt( $AlDl * $AlDl
128             + $Ah * ( $c1 * $c1
129             + $c2 * $c2
130             - 2 * $c1 * $c2 * cos( deg2rad( $Dh ) )
131             )
132             );
133             }
134              
135             =head1 SEE ALSO
136              
137             L
138              
139             Corrected the RGB -> HCL transformation (see C) as per the
140             research report by the same authors (thanks to David Hoerl for finding
141             the document with the corrected formula).
142              
143             L, L, L
144              
145             =head1 AUTHOR
146              
147             Mattia Barbon, C<< >>
148              
149             =head1 COPYRIGHT
150              
151             Copyright (C) 2007, Mattia Barbon
152              
153             This program is free software; you can redistribute it or modify it
154             under the same terms as Perl itself.
155              
156             =cut
157              
158             sub _vtable {
159 0     0     return { distance_rgb => \&distance,
160             convert_rgb => \&rgb2hcl,
161             distance => \&distance_hcl,
162             };
163             }
164              
165             1;