line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# ============================================================================= |
2
|
|
|
|
|
|
|
package Color::Model::Munsell::Util; |
3
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
4
|
|
|
|
|
|
|
$Color::Model::Munsell::Util::VERSION = '0.03'; |
5
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
6
|
1
|
|
|
1
|
|
30074
|
use warnings; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
33
|
|
7
|
1
|
|
|
1
|
|
5
|
use strict; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
38
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
=head1 NAME |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
Color::Model::Munsell::Util - Utility functions for Color::Model::Munsell |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
=head1 SYNOPSIS |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
use Color::Model::Munsell; |
16
|
|
|
|
|
|
|
use Color::Model::Munsell::Util; |
17
|
|
|
|
|
|
|
use Color::Model::RGB; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
my $m = Color::Model::Munsell->new("5R 4.5/14"); |
20
|
|
|
|
|
|
|
printf("Munsell: %s = RGB: #%s\n", $m, Munsell2RGB($m)); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 DESCRIPTION |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
C gives some utility functions for color |
25
|
|
|
|
|
|
|
conversion from Munsell to CIE xyY, XYZ or RGB, etc. |
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=cut |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
# ============================================================================= |
30
|
1
|
|
|
1
|
|
5
|
use Carp qw(); |
|
1
|
|
|
|
|
6
|
|
|
1
|
|
|
|
|
17
|
|
31
|
1
|
|
|
1
|
|
5
|
use List::Util qw(first); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
99
|
|
32
|
1
|
|
|
1
|
|
4
|
use vars qw(@ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
74
|
|
33
|
1
|
|
|
1
|
|
4
|
use base qw(Exporter); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
114
|
|
34
|
|
|
|
|
|
|
@EXPORT = qw(Munsell2RGB); |
35
|
|
|
|
|
|
|
@EXPORT_OK = qw( |
36
|
|
|
|
|
|
|
huedegree calc_Yc |
37
|
|
|
|
|
|
|
Munsell2xyY Munsell2XYZ Munsell2XYZD65 Munsell2rgb |
38
|
|
|
|
|
|
|
); |
39
|
|
|
|
|
|
|
|
40
|
1
|
|
|
1
|
|
911
|
use Math::VectorReal; |
|
1
|
|
|
|
|
3142
|
|
|
1
|
|
|
|
|
64
|
|
41
|
1
|
|
|
1
|
|
7
|
use Color::Model::Munsell qw(@hue_order %hue_number); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
145
|
|
42
|
1
|
|
|
1
|
|
1041
|
use Color::Model::RGB; |
|
1
|
|
|
|
|
12869
|
|
|
1
|
|
|
|
|
4000
|
|
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
my $_debug = 0; |
45
|
0
|
|
|
0
|
|
0
|
sub _debug { warn join(" ",@_); } |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# ============================================================================= |
48
|
|
|
|
|
|
|
my @_tableAll = (); |
49
|
|
|
|
|
|
|
my %_tableC = (); |
50
|
|
|
|
|
|
|
my $_tableC_loaded = 0; |
51
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
54
|
|
|
|
|
|
|
sub _table |
55
|
|
|
|
|
|
|
{ |
56
|
10
|
|
|
10
|
|
24
|
my $v = shift; |
57
|
10
|
|
|
|
|
84
|
$v = sprintf('%d',$v); |
58
|
10
|
|
|
|
|
32
|
_load_table_C(); |
59
|
10
|
50
|
33
|
|
|
87
|
if ( $v && $_tableC{$v} ){ |
60
|
|
|
|
|
|
|
# return same value data sorted by chroma |
61
|
10
|
|
|
|
|
16
|
return sort { $a->[2] <=> $b->[2] } @{$_tableC{$v}}; |
|
35265
|
|
|
|
|
49930
|
|
|
10
|
|
|
|
|
341
|
|
62
|
|
|
|
|
|
|
} else { |
63
|
0
|
0
|
|
|
|
0
|
return wantarray? @_tableAll: \@_tableAll; |
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
sub _load_table_C |
68
|
|
|
|
|
|
|
{ |
69
|
15
|
100
|
|
15
|
|
50
|
unless ( $_tableC_loaded ){ |
70
|
1
|
|
|
|
|
2
|
my $d; |
71
|
|
|
|
|
|
|
# append neutral color for convenience |
72
|
1
|
|
|
|
|
5
|
foreach my $v ( 0.2, 0.4, 0.6, 0.8, 1..10 ){ |
73
|
14
|
|
|
|
|
30
|
$d = [ 'N', $v, 0, _neutralxyY($v) ]; |
74
|
14
|
|
|
|
|
55
|
push @_tableAll, $d; |
75
|
14
|
|
|
|
|
18
|
push @{$_tableC{$v}}, $d; |
|
14
|
|
|
|
|
78
|
|
76
|
|
|
|
|
|
|
} |
77
|
|
|
|
|
|
|
|
78
|
1
|
|
|
|
|
8
|
while (){ |
79
|
5000
|
|
|
|
|
9077
|
tr/\r\n//d; |
80
|
5000
|
|
|
|
|
19403
|
s/^ +//g; |
81
|
5000
|
50
|
|
|
|
26267
|
next unless $_; |
82
|
5000
|
100
|
|
|
|
10442
|
next if /^#/; |
83
|
4995
|
|
|
|
|
25982
|
my @d = split(/\s+/); |
84
|
4995
|
|
|
|
|
10932
|
$d[5] /= 100; # rescale Y to (0,1) |
85
|
4995
|
|
|
|
|
15657
|
$d = [ @d ]; |
86
|
4995
|
|
|
|
|
10297
|
push @_tableAll, $d; |
87
|
4995
|
|
|
|
|
4946
|
push @{$_tableC{$d[1]}}, $d; # key is value |
|
4995
|
|
|
|
|
30931
|
|
88
|
|
|
|
|
|
|
} |
89
|
1
|
|
|
|
|
7
|
$_tableC_loaded++; |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=head1 EXPORT SUBROUTINES |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
Only subroutine Munsell2RGB is exported by defalut. |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=head2 huedegree() |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
$n = huedegree( "5R" ); |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
Return degree number of Hue; considered 10.0RP is 0, 10R to be 10, 10YR 20, |
105
|
|
|
|
|
|
|
..., and ends 9.9RP as 99.9. |
106
|
|
|
|
|
|
|
If bad formatted hue given, this returns undef. |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
=cut |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
sub huedegree |
111
|
|
|
|
|
|
|
{ |
112
|
31
|
50
|
33
|
31
|
1
|
825
|
if ( defined($_[0]) && $_[0] =~ /^(\d{1,2}|\d{1,2}\.\d)(R|YR|Y|GY|G|BG|B|PB|P|RP)$/ ){ |
113
|
31
|
|
|
|
|
128
|
my ($n,$c) = ($1,$2); |
114
|
31
|
50
|
|
|
|
175
|
if ( $n <= 10 ){ |
115
|
31
|
|
|
|
|
220
|
return $hue_number{$2} * 10 + $1; |
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
} |
118
|
0
|
|
|
|
|
0
|
return undef; |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# ============================================================================= |
123
|
|
|
|
|
|
|
# get xyY with linear interpolation |
124
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
125
|
|
|
|
|
|
|
my @_Wcxy = (0.310061, 0.316150); |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=head2 Munsell2xyY() |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
Munsell2xyY() returns an array of CIE x, y and Y which are calculated with |
130
|
|
|
|
|
|
|
linear interpolation from Munsell-xyY table. |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
use Color::Model::Munsell::Util qw(Munsell2xyY); |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
$m = Color::Model::Munsell->new("2.5G 5.5/10"); |
135
|
|
|
|
|
|
|
($x, $y, $Y) = Munsell2xyY($m); |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
This Munsell-xyY table is from MCSL, R.I.T. which condition is using illuminant |
138
|
|
|
|
|
|
|
C and the CIE 1931 2 degree observer. |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=cut |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
sub _getHueRange |
143
|
|
|
|
|
|
|
{ |
144
|
10
|
|
|
10
|
|
18
|
my ($h1, $h2); |
145
|
0
|
|
|
|
|
0
|
my ($hueStep, $hueCol); |
146
|
10
|
50
|
|
|
|
74
|
if ( $_[0] =~ /^([0-9\.]+)([A-Z]+)$/ ){ |
147
|
10
|
|
|
|
|
44
|
($hueStep, $hueCol) = ($1, $2); |
148
|
|
|
|
|
|
|
} |
149
|
10
|
50
|
|
|
|
74
|
if ( $hueStep < 2.5 ){ |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
150
|
0
|
0
|
|
|
|
0
|
my $pre_hC = ($hueCol eq 'R')? 'RP': $hue_order[$hue_number{$hueCol}-1]; |
151
|
0
|
|
|
|
|
0
|
($h1, $h2) = ( "10$pre_hC", "2.5$hueCol" ); |
152
|
|
|
|
|
|
|
} elsif ( $hueStep < 5.0 ){ |
153
|
0
|
|
|
|
|
0
|
($h1, $h2) = ( "2.5$hueCol", "5$hueCol" ); |
154
|
|
|
|
|
|
|
} elsif ( $hueStep < 7.5 ){ |
155
|
10
|
|
|
|
|
58
|
($h1, $h2) = ( "5$hueCol", "7.5$hueCol" ); |
156
|
|
|
|
|
|
|
} else { |
157
|
0
|
|
|
|
|
0
|
($h1, $h2) = ( "7.5$hueCol", "10$hueCol" ); |
158
|
|
|
|
|
|
|
} |
159
|
10
|
|
|
|
|
39
|
return ($h1, $h2); |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
sub _neutralxyY |
163
|
|
|
|
|
|
|
{ |
164
|
14
|
|
|
14
|
|
21
|
my $value = shift; |
165
|
14
|
|
|
|
|
26
|
return ( @_Wcxy, calc_Yc($value)/100 ); # White Point of illuminant C |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub _getVectorOnValuePlane |
169
|
|
|
|
|
|
|
{ |
170
|
10
|
|
|
10
|
|
131
|
my ($h0,$v0,$c0) = @_; |
171
|
|
|
|
|
|
|
|
172
|
10
|
50
|
|
|
|
33
|
_debug(" * Cheking: $h0 $v0 / $c0\n") if $_debug; |
173
|
|
|
|
|
|
|
|
174
|
10
|
|
|
|
|
36
|
my ($h1,$h2) = _getHueRange($h0); |
175
|
10
|
50
|
|
|
|
26
|
_debug(" - - hue range = [ $h1, $h2 ]\n") if $_debug; |
176
|
|
|
|
|
|
|
|
177
|
10
|
|
|
|
|
47
|
my @luptable = _table($v0); |
178
|
10
|
50
|
|
5100
|
|
486
|
my $eqcheck = first { $_->[0] eq $h0 && $_->[2] eq $c0 } @luptable; |
|
5100
|
|
|
|
|
11521
|
|
179
|
10
|
50
|
|
|
|
85
|
return vector(@{$eqcheck}[3,4,5]) if ( $eqcheck ); # found just same data! |
|
0
|
|
|
|
|
0
|
|
180
|
10
|
100
|
|
|
|
47
|
my @h1 = grep { $_->[0] eq 'N' or $_->[0] eq $h1 } @luptable; |
|
5100
|
|
|
|
|
21796
|
|
181
|
10
|
100
|
|
|
|
81
|
my @h2 = grep { $_->[0] eq 'N' or $_->[0] eq $h2 } @luptable; |
|
5100
|
|
|
|
|
19960
|
|
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
# h1,h2 ... hue line ( h1 < h2) |
184
|
|
|
|
|
|
|
# c1,c2 ... chroma line (c1 < c2 ) |
185
|
|
|
|
|
|
|
# |
186
|
|
|
|
|
|
|
# [ on some value-plane ] |
187
|
|
|
|
|
|
|
# c1 c2 |
188
|
|
|
|
|
|
|
# | P C/ |
189
|
|
|
|
|
|
|
# h2 ----+-----+----------/-- - |
190
|
|
|
|
|
|
|
# B| | / ) 1-l |
191
|
|
|
|
|
|
|
# | *Z / - |
192
|
|
|
|
|
|
|
# | / | / ` l |
193
|
|
|
|
|
|
|
# | / | / / |
194
|
|
|
|
|
|
|
# h1 ----+-----+-----/--- - |
195
|
|
|
|
|
|
|
# A Q D |
196
|
|
|
|
|
|
|
# ` n ^ 1-n ' |
197
|
|
|
|
|
|
|
# |
198
|
|
|
|
|
|
|
# ( = vector x ) |
199
|
|
|
|
|
|
|
# = (1-n) + n |
200
|
|
|
|
|
|
|
# = n |
201
|
|
|
|
|
|
|
# = m + (1-m) |
202
|
|
|
|
|
|
|
# = (m-mn) + mn + (n-mn) |
203
|
|
|
|
|
|
|
# = m + n + mn(--) |
204
|
|
|
|
|
|
|
# = m + n + mn(-) |
205
|
|
|
|
|
|
|
|
206
|
10
|
|
|
|
|
22
|
my ($dA,$dB,$dC,$dD); # lookup data |
207
|
0
|
|
|
|
|
0
|
my ($OA,$OB,$OC,$OD); # vectors |
208
|
0
|
|
|
|
|
0
|
my ($c1,$c2); # chroma values of two points |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# - on line of h1 |
211
|
10
|
|
|
80
|
|
159
|
$dD = first { $_->[2] >= $c0 } @h1; |
|
80
|
|
|
|
|
142
|
|
212
|
10
|
50
|
|
|
|
84
|
unless ( $dD ){ |
213
|
|
|
|
|
|
|
# not found, maybe target's chroma is larger than max chroma |
214
|
|
|
|
|
|
|
# of lookup table |
215
|
0
|
|
|
|
|
0
|
return undef; |
216
|
|
|
|
|
|
|
} |
217
|
10
|
|
|
|
|
27
|
$OD = vector( @{$dD}[3,4,5] ); |
|
10
|
|
|
|
|
114
|
|
218
|
10
|
|
|
|
|
365
|
($c1,$c2) = ($dD->[2]-2, $dD->[2]); |
219
|
|
|
|
|
|
|
|
220
|
10
|
|
|
70
|
|
67
|
$dA = first { $_->[2] == $c1 } @h1; |
|
70
|
|
|
|
|
130
|
|
221
|
10
|
|
|
|
|
35
|
$OA = vector( @{$dA}[3,4,5] ); |
|
10
|
|
|
|
|
46
|
|
222
|
|
|
|
|
|
|
|
223
|
10
|
50
|
|
|
|
170
|
if ( $_debug ){ |
224
|
0
|
|
|
|
|
0
|
_debug(" - - - $h1> OA = ($dA->[0] $dA->[1]/ $dA->[2]) \n"); |
225
|
0
|
|
|
|
|
0
|
_debug(" - - - $h1> OD = ($dD->[0] $dD->[1]/ $dD->[2]) \n"); |
226
|
|
|
|
|
|
|
} |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# - on line of h2 |
229
|
10
|
|
|
80
|
|
89
|
$dC = first { $_->[2] >= $c0 } @h2; |
|
80
|
|
|
|
|
126
|
|
230
|
10
|
50
|
|
|
|
56
|
unless ( $dC ){ |
231
|
|
|
|
|
|
|
# not found on lookup table |
232
|
0
|
|
|
|
|
0
|
return undef; |
233
|
|
|
|
|
|
|
} |
234
|
10
|
|
|
|
|
22
|
$OC = vector( @{$dC}[3,4,5] ); |
|
10
|
|
|
|
|
32
|
|
235
|
10
|
|
|
|
|
182
|
($c1,$c2) = ($dC->[2]-2, $dC->[2]); |
236
|
|
|
|
|
|
|
|
237
|
10
|
|
|
70
|
|
75
|
$dB = first { $_->[2] == $c1 } @h2; |
|
70
|
|
|
|
|
155
|
|
238
|
10
|
|
|
|
|
38
|
$OB = vector( @{$dB}[3,4,5] ); |
|
10
|
|
|
|
|
43
|
|
239
|
|
|
|
|
|
|
|
240
|
10
|
50
|
|
|
|
157
|
if ( $_debug ){ |
241
|
0
|
|
|
|
|
0
|
_debug(" - - - $h2> OB = ($dB->[0] $dB->[1]/ $dB->[2]) \n"); |
242
|
0
|
|
|
|
|
0
|
_debug(" - - - $h2> OC = ($dC->[0] $dC->[1]/ $dC->[2]) \n"); |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# calculate ratio |
246
|
10
|
|
|
|
|
45
|
my $n = ($c0 - $c1) / ($c2 - $c1); # ratio between chromas |
247
|
10
|
|
|
|
|
20
|
my ($h0n, $h1n, $h2n) = map { huedegree($_) } ($h0, $h1, $h2); |
|
30
|
|
|
|
|
80
|
|
248
|
10
|
|
|
|
|
34
|
my $l = ($h0n - $h1n) / ($h2n - $h1n); # ratio between hues |
249
|
10
|
50
|
|
|
|
44
|
if ( $_debug ){ |
250
|
0
|
|
|
|
|
0
|
_debug(" - - - Ratio chroma; ($c0 - $1) / ($c2 - $c1) = $n\n"); |
251
|
0
|
|
|
|
|
0
|
_debug(" - - - Ratio hue; ($h0n - $h1n) / ($h2n - $h1n) = $l\n"); |
252
|
|
|
|
|
|
|
} |
253
|
|
|
|
|
|
|
|
254
|
10
|
|
|
|
|
158
|
my $AB = $OB - $OA; |
255
|
10
|
|
|
|
|
717
|
my $AD = $OD - $OA; |
256
|
10
|
|
|
|
|
434
|
my $BC = $OC - $OB; |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# return value is a vector with Math::RealVector; |
259
|
10
|
|
|
|
|
499
|
my $AZ = $AB*$l + $AD*$n+ ($BC-$AD)*$l*$n; |
260
|
10
|
|
|
|
|
2566
|
my $OZ = $OA + $AZ; |
261
|
|
|
|
|
|
|
|
262
|
10
|
50
|
|
|
|
482
|
_debug(map { sprintf("%s = (%.4f, %.4f, %.4f)\n", $_, eval"\$$_->array()") } qw(OA OB OC OD OZ)) if $_debug; |
|
0
|
|
|
|
|
0
|
|
263
|
10
|
|
|
|
|
352
|
return $OZ; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
sub Munsell2xyY |
267
|
|
|
|
|
|
|
{ |
268
|
5
|
|
|
5
|
1
|
11
|
my $m = shift; |
269
|
5
|
50
|
33
|
|
|
41
|
unless ( defined($m) && ref($m) eq 'Color::Model::Munsell' ){ |
270
|
0
|
|
|
|
|
0
|
Carp::croak("Color::Model::Munsell object is not given"); |
271
|
|
|
|
|
|
|
} |
272
|
5
|
50
|
|
|
|
16
|
_debug("<< Target: $m >>\n") if $_debug; |
273
|
5
|
|
|
|
|
28
|
my $v = $m->value; |
274
|
|
|
|
|
|
|
|
275
|
5
|
50
|
|
|
|
57
|
if ( $m->isneutral ){ |
276
|
0
|
|
|
|
|
0
|
my @ret = _neutralxyY($v); |
277
|
0
|
0
|
|
|
|
0
|
_debug(sprintf("xyY = (%.6f, %.6f, %.6f)\n",@ret)) if $_debug; |
278
|
0
|
|
|
|
|
0
|
return @ret; |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
5
|
|
|
|
|
53
|
_load_table_C(); |
282
|
|
|
|
|
|
|
|
283
|
5
|
|
|
|
|
12
|
my ($LZ, $UZ, $Z); |
284
|
|
|
|
|
|
|
# get lower value-plane |
285
|
5
|
50
|
|
|
|
31
|
my $lv = ($v<1.0)? (int($v*10/2)*2/10): int($v); |
286
|
5
|
50
|
|
|
|
21
|
_debug(" - nearest lower value of $m is $lv\n") if $_debug; |
287
|
5
|
50
|
|
|
|
14
|
if ( $lv == 0 ){ |
288
|
0
|
|
|
|
|
0
|
$LZ = vector( _neutralxyY($v) ); |
289
|
|
|
|
|
|
|
} else { |
290
|
5
|
|
|
|
|
40
|
$LZ = _getVectorOnValuePlane( $m->hue, $lv, $m->chroma ); |
291
|
5
|
50
|
|
|
|
23
|
unless ( defined($LZ) ){ |
292
|
0
|
|
|
|
|
0
|
Carp::croak("$m is out of calculatable color space."); |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
# get upper value-plane |
296
|
5
|
50
|
|
|
|
30
|
my $uv = ($v<1.0)? (int($v*10/2)*2/10+0.2): int($v+1); |
297
|
5
|
50
|
|
|
|
15
|
_debug(" - nearest upper value of $m is $uv\n") if $_debug; |
298
|
5
|
|
|
|
|
39
|
$UZ = _getVectorOnValuePlane( $m->hue, $uv, $m->chroma ); |
299
|
5
|
50
|
|
|
|
24
|
unless ( defined($UZ) ){ |
300
|
0
|
|
|
|
|
0
|
Carp::croak("$m is out of calculatable color space."); |
301
|
|
|
|
|
|
|
} |
302
|
|
|
|
|
|
|
|
303
|
5
|
50
|
|
|
|
21
|
_debug(" - value range = [ $lv, $uv ]\n") if $_debug; |
304
|
|
|
|
|
|
|
|
305
|
5
|
|
|
|
|
18
|
my $s = ($v - $lv) / ($uv - $lv ); # ratio between values; |
306
|
5
|
50
|
|
|
|
14
|
_debug(" - value ratio = ($v - $lv) / ($uv - $lv) = $s\n") if $_debug; |
307
|
|
|
|
|
|
|
|
308
|
5
|
|
|
|
|
19
|
$Z = $UZ*$s + $LZ*(1-$s); |
309
|
5
|
50
|
|
|
|
527
|
_debug(sprintf("xyY = (%.6f, %.6f, %.6f)\n",$Z->array())) if $_debug; |
310
|
|
|
|
|
|
|
|
311
|
5
|
|
|
|
|
18
|
return $Z->array(); # ( xc, yc , Yc ); |
312
|
|
|
|
|
|
|
} |
313
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
317
|
|
|
|
|
|
|
# multiply matrix * vector |
318
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
319
|
|
|
|
|
|
|
sub _mmult |
320
|
|
|
|
|
|
|
{ |
321
|
5
|
|
|
5
|
|
10
|
my ($m,$v) = @_; |
322
|
|
|
|
|
|
|
return ( |
323
|
5
|
|
|
|
|
55
|
$$m[0][0]*$$v[0] + $$m[0][1]*$$v[1] + $$m[0][2]*$$v[2], |
324
|
|
|
|
|
|
|
$$m[1][0]*$$v[0] + $$m[1][1]*$$v[1] + $$m[1][2]*$$v[2], |
325
|
|
|
|
|
|
|
$$m[2][0]*$$v[0] + $$m[2][1]*$$v[1] + $$m[2][2]*$$v[2] |
326
|
|
|
|
|
|
|
); |
327
|
|
|
|
|
|
|
} |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
|
330
|
|
|
|
|
|
|
# ============================================================================= |
331
|
|
|
|
|
|
|
# get XYZ from Munsell with xyY |
332
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
333
|
|
|
|
|
|
|
my %_matCAdaptD65 = ( |
334
|
|
|
|
|
|
|
'XYZ' => [ |
335
|
|
|
|
|
|
|
[ 0.9691356, 0.0000000, 0.0000000], |
336
|
|
|
|
|
|
|
[ 0.0000000, 1.0000000, 0.0000000], |
337
|
|
|
|
|
|
|
[ 0.0000000, 0.0000000, 0.9209267]], |
338
|
|
|
|
|
|
|
'Bradford' => [ |
339
|
|
|
|
|
|
|
[ 0.9904476,-0.0071683,-0.0116156], |
340
|
|
|
|
|
|
|
[-0.0123712, 1.0155950,-0.0029282], |
341
|
|
|
|
|
|
|
[-0.0035635, 0.0067697, 0.9181569]], |
342
|
|
|
|
|
|
|
'vonKries' => [ |
343
|
|
|
|
|
|
|
[ 0.9972812,-0.0093756,-0.0154171], |
344
|
|
|
|
|
|
|
[-0.0010298, 1.0007636, 0.0002084], |
345
|
|
|
|
|
|
|
[ 0.0000000, 0.0000000, 0.9209267]] |
346
|
|
|
|
|
|
|
); |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=head2 Munsell2XYZ() |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
=head2 Munsell2XYZD65() |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
Munsell2XYZ( $m ) |
353
|
|
|
|
|
|
|
Munsell2XYZD65( $m [, "ChromaticAdaptType" ] ) |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
Munsell2XYZ() and Munsell2XYZD65() returns an array of CIE X,Y and Z. |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
$m = Color::Model::Munsell->new("7.5B 6/10"); |
358
|
|
|
|
|
|
|
printf("%s -> CIE XYZ (%.f, %.f, %.f)", $m, Munsell2XYZ($m)); |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
# perform cromatic adaptation from C to D65 |
361
|
|
|
|
|
|
|
printf("%s -> CIE XYZ (%.f, %.f, %.f) via Chromatic Adaptation", $m, Munsell2XYZD65($m)); |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
Munsell2XYZ() simply calculates XYZ from xyY. And Munsell2XYZD65() calculates |
364
|
|
|
|
|
|
|
them with chromatic adaptatation to illuminant D65. Adaptation type must be |
365
|
|
|
|
|
|
|
"XYZ", "vonKries", "Bradford" or "None". If Omitted, "Bradford" is used. |
366
|
|
|
|
|
|
|
Specifying "None" is same as calling Munsell2XYZ(). |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=cut |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
sub Munsell2XYZ |
371
|
|
|
|
|
|
|
{ |
372
|
4
|
|
|
4
|
1
|
449
|
my $m = shift; # Color::Model::Munsell object |
373
|
4
|
50
|
33
|
|
|
30
|
unless ( defined($m) && ref($m) eq 'Color::Model::Munsell' ){ |
374
|
0
|
|
|
|
|
0
|
Carp::croak("Munsell2XYZ() needs Color::Model::Munsell object."); |
375
|
|
|
|
|
|
|
} |
376
|
|
|
|
|
|
|
|
377
|
4
|
|
|
|
|
12
|
my ($x,$y,$Y) = Munsell2xyY($m); |
378
|
4
|
|
|
|
|
57
|
my $X = ($x * $Y) / $y; # X = (Y/y) * x |
379
|
4
|
|
|
|
|
12
|
my $Z = ( (1 - $x - $y) * $Y ) / $y; # Z = ( (1-x-y)/y ) * Y |
380
|
4
|
|
|
|
|
45
|
return ($X, $Y, $Z); |
381
|
|
|
|
|
|
|
} |
382
|
|
|
|
|
|
|
|
383
|
|
|
|
|
|
|
sub Munsell2XYZD65 |
384
|
|
|
|
|
|
|
{ |
385
|
3
|
|
|
3
|
1
|
7
|
my $m = shift; # Color::Model::Munsell object |
386
|
3
|
50
|
33
|
|
|
30
|
unless ( defined($m) && ref($m) eq 'Color::Model::Munsell' ){ |
387
|
0
|
|
|
|
|
0
|
Carp::croak("Munsell2XYZD65() needs Color::Model::Munsell object."); |
388
|
|
|
|
|
|
|
} |
389
|
|
|
|
|
|
|
|
390
|
3
|
|
100
|
|
|
14
|
my $atype = shift || 'Bradford'; # Chromatic Adaptation type |
391
|
3
|
50
|
|
|
|
20
|
unless ( $atype =~ /^(None|XYZ|Bradford|vonKries)$/ ){ |
392
|
0
|
|
|
|
|
0
|
Carp::croak(qq(Chromatic Adaptation must be "XYZ", "Bradford" or "vonKries")); |
393
|
|
|
|
|
|
|
} |
394
|
3
|
50
|
|
|
|
9
|
_debug(" - Chromatic Adaptation = $atype\n") if $_debug; |
395
|
|
|
|
|
|
|
|
396
|
3
|
|
|
|
|
6
|
my @XYZ; |
397
|
3
|
50
|
|
|
|
10
|
if ( $atype ne 'None' ){ |
398
|
3
|
|
|
|
|
14
|
@XYZ = _mmult($_matCAdaptD65{$atype}, [ Munsell2XYZ($m) ]); |
399
|
|
|
|
|
|
|
} else { |
400
|
0
|
|
|
|
|
0
|
@XYZ = Munsell2XYZ($m); |
401
|
|
|
|
|
|
|
} |
402
|
3
|
50
|
|
|
|
15
|
_debug(sprintf("XYZ = (%.6f, %.6f, %.6F)\n",@XYZ)) if $_debug; |
403
|
3
|
|
|
|
|
20
|
return @XYZ; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
# ============================================================================= |
408
|
|
|
|
|
|
|
# get RGB from XYZ |
409
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
410
|
|
|
|
|
|
|
my %_matXYZ2rgb = ( |
411
|
|
|
|
|
|
|
'AdobeRGB' => [ # D65 |
412
|
|
|
|
|
|
|
[ 2.0413690, -0.5649464, -0.3446944], |
413
|
|
|
|
|
|
|
[-0.9692660, 1.8760108, 0.0415560], |
414
|
|
|
|
|
|
|
[ 0.0134474, -0.1183897, 1.0154096]], |
415
|
|
|
|
|
|
|
'AppleRGB' => [ # D65 |
416
|
|
|
|
|
|
|
[ 2.9515373, -1.2894116, -0.4738445], |
417
|
|
|
|
|
|
|
[-1.0851093, 1.9908566, 0.0372026], |
418
|
|
|
|
|
|
|
[ 0.0854934, -0.2694964, 1.0912975]], |
419
|
|
|
|
|
|
|
'PAL' => [ # D65 |
420
|
|
|
|
|
|
|
[ 3.0628971, -1.3931791, -0.4757517], |
421
|
|
|
|
|
|
|
[-0.9692660, 1.8760108, 0.0415560], |
422
|
|
|
|
|
|
|
[ 0.0678775, -0.2288548, 1.0693490]], |
423
|
|
|
|
|
|
|
'sRGB' => [ # D65 |
424
|
|
|
|
|
|
|
[ 3.2404542, -1.5371385, -0.4985314], |
425
|
|
|
|
|
|
|
[-0.9692660, 1.8760108, 0.0415560], |
426
|
|
|
|
|
|
|
[ 0.0556434, -0.2040259, 1.0572252]], |
427
|
|
|
|
|
|
|
'NTSC' => [ # C |
428
|
|
|
|
|
|
|
[ 1.9099961, -0.5324542, -0.2882091], |
429
|
|
|
|
|
|
|
[-0.9846663, 1.9991710, -0.0283082], |
430
|
|
|
|
|
|
|
[ 0.0583056, -0.1183781, 0.8975535]], |
431
|
|
|
|
|
|
|
); |
432
|
|
|
|
|
|
|
|
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
=head2 Munsell2rgb() |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=head2 Munsell2RGB() |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
Munsell2rgb( $m [, "RGBModel" [, "ChromaticAdaptType" ]] ) |
439
|
|
|
|
|
|
|
Munsell2RGB( $m [, "RGBModel" [, "ChromaticAdaptType" [, $gamma ]]] ) |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
Munsell2rgb() returns an array of R, G and B values which calculated from |
442
|
|
|
|
|
|
|
XYZ with transformation matrix. |
443
|
|
|
|
|
|
|
And Munsell2RGB() returns RGB values with C object which applied gamma value. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
$m = Color::Model::Munsell->new("7PB 2.5/3"); |
446
|
|
|
|
|
|
|
printf("%s -> RGB %s of sRGB", $m, Munsell2RGB($m)); |
447
|
|
|
|
|
|
|
printf("%s -> RGB %s of AdobeRGB(1998)", $m, Munsell2RGB($m,"AdobeRGB"), 2.2); |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
RGBModel must be "sRGB", "AdobeRGB" that means Adobe RGB(1998), "AppleRGB" |
450
|
|
|
|
|
|
|
or "NTSC". A gamma value will be used when RGB model is not sRGB. |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
=cut |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
sub Munsell2rgb |
455
|
|
|
|
|
|
|
{ |
456
|
2
|
|
|
2
|
1
|
5
|
my $m = shift; # Color::Model::Munsell object |
457
|
2
|
50
|
33
|
|
|
20
|
unless ( defined($m) && ref($m) eq 'Color::Model::Munsell' ){ |
458
|
0
|
|
|
|
|
0
|
Carp::croak("Munsell2rgb() needs Color::Model::Munsell object."); |
459
|
|
|
|
|
|
|
} |
460
|
2
|
|
100
|
|
|
11
|
my $rgbtype = shift || 'sRGB'; # RGB type |
461
|
2
|
|
100
|
|
|
10
|
my $atype = shift || 'Bradford'; # Chromatic Adaptation type |
462
|
2
|
50
|
|
|
|
15
|
unless ( $rgbtype =~ /^(AdobeRGB|AppleRGB|PAL|sRGB|NTSC)$/ ){ |
463
|
0
|
|
|
|
|
0
|
Carp::croak(qq(RGB type must be "AdobeRGB", "AppleRGB", "PAL", "NTSC" or "sRGB")); |
464
|
|
|
|
|
|
|
} |
465
|
2
|
50
|
|
|
|
8
|
_debug(" - RGB = $rgbtype\n") if $_debug; |
466
|
|
|
|
|
|
|
|
467
|
2
|
|
|
|
|
3
|
my @XYZ; |
468
|
2
|
50
|
|
|
|
9
|
if ( $rgbtype eq 'NTSC' ){ |
469
|
0
|
|
|
|
|
0
|
@XYZ = Munsell2XYZ($m); |
470
|
|
|
|
|
|
|
} else { |
471
|
2
|
|
|
|
|
8
|
@XYZ = Munsell2XYZD65($m, $atype); |
472
|
|
|
|
|
|
|
} |
473
|
|
|
|
|
|
|
|
474
|
2
|
|
|
|
|
13
|
my @rgb = _mmult($_matXYZ2rgb{$rgbtype}, \@XYZ); |
475
|
2
|
50
|
|
|
|
8
|
_debug(sprintf("rgb = (%.6f, %.6f, %.6f)\n",@rgb)) if $_debug; |
476
|
2
|
|
|
|
|
16
|
return @rgb; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
sub Munsell2RGB |
480
|
|
|
|
|
|
|
{ |
481
|
1
|
|
|
1
|
1
|
4
|
my $m = shift; # Color::Model::Munsell object |
482
|
1
|
50
|
33
|
|
|
12
|
unless ( defined($m) && ref($m) eq 'Color::Model::Munsell' ){ |
483
|
0
|
|
|
|
|
0
|
Carp::croak("Munsell2RGB() needs Color::Model::Munsell object."); |
484
|
|
|
|
|
|
|
} |
485
|
1
|
|
50
|
|
|
8
|
my $rgbtype = shift || 'sRGB'; # RGB type |
486
|
1
|
|
50
|
|
|
8
|
my $atype = shift || 'Bradford'; # Chromatic Adaptation type |
487
|
1
|
|
50
|
|
|
75
|
my $gamma = shift || 2.2; # Gamma value (uses when not sRGB) |
488
|
|
|
|
|
|
|
|
489
|
1
|
|
|
|
|
3
|
my @RGB; |
490
|
1
|
50
|
|
|
|
7
|
if ( $rgbtype eq 'sRGB' ){ |
491
|
3
|
50
|
|
|
|
1049
|
@RGB = map { |
492
|
1
|
|
|
|
|
4
|
$_ = $_>0.0031308? |
493
|
|
|
|
|
|
|
(1.055 * ($_ ** (1/2.4)) - 0.055): |
494
|
|
|
|
|
|
|
12.92 * $_; |
495
|
3
|
50
|
|
|
|
30
|
$_ = $_<0 ? 0: ( $_>1? 1: $_); |
|
|
50
|
|
|
|
|
|
496
|
|
|
|
|
|
|
} Munsell2rgb($m, 'sRGB', $atype); |
497
|
|
|
|
|
|
|
} else { |
498
|
0
|
|
|
|
|
0
|
@RGB = map { |
499
|
0
|
|
|
|
|
0
|
$_ = $_ ** (1/$gamma); |
500
|
0
|
0
|
|
|
|
0
|
$_ = $_<0 ? 0: ( $_>1? 1: $_); |
|
|
0
|
|
|
|
|
|
501
|
|
|
|
|
|
|
} Munsell2rgb($m, $rgbtype, $atype); |
502
|
|
|
|
|
|
|
} |
503
|
1
|
50
|
|
|
|
21
|
_debug(sprintf("RGB = (%.6f, %.6f, %.6f)\n",@RGB)) if $_debug; |
504
|
1
|
|
|
|
|
15
|
return Color::Model::RGB->new(@RGB); |
505
|
|
|
|
|
|
|
} |
506
|
|
|
|
|
|
|
|
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
# ============================================================================= |
509
|
|
|
|
|
|
|
# Calculation Y of illuminant C |
510
|
|
|
|
|
|
|
# ----------------------------------------------------------------------------- |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=head2 calc_Yc() |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
calc_Yc( $m ) |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
calc_Yc() calculates Y value of illuminant C and 2 degree observer from Munsell |
517
|
|
|
|
|
|
|
value with approximate calculation. Argument must be a I object or Munsell value. |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
$m = Color::Model::Munsell->new("5R 4/14") |
520
|
|
|
|
|
|
|
printf "%.4f", calc_Yc( $m ); |
521
|
|
|
|
|
|
|
printf "%.4f", calc_Yc( 7.5 ); |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=cut |
524
|
|
|
|
|
|
|
|
525
|
1
|
|
|
1
|
|
11
|
use Scalar::Util qw(looks_like_number); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
229
|
|
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
sub calc_Yc |
528
|
|
|
|
|
|
|
{ |
529
|
|
|
|
|
|
|
# Yc = 1.1913*V - 0.22532*V^2 + 0.23351*V^3 - 0.020483*V^4 + 0.00081936*V^5 |
530
|
|
|
|
|
|
|
# (ref. JIS Z 8721(1993) ) |
531
|
|
|
|
|
|
|
|
532
|
16
|
|
|
16
|
1
|
49
|
my $m = shift; |
533
|
16
|
|
|
|
|
21
|
my $v = undef; |
534
|
16
|
50
|
|
|
|
39
|
if ( defined($m) ){ |
535
|
16
|
50
|
|
|
|
74
|
if ( ref($m) eq 'Color::Model::Munsell' ){ |
|
|
50
|
|
|
|
|
|
536
|
0
|
|
|
|
|
0
|
$v = $m->value; |
537
|
|
|
|
|
|
|
} elsif ( looks_like_number($m) ){ |
538
|
16
|
|
|
|
|
31
|
$v = $m; |
539
|
|
|
|
|
|
|
} |
540
|
|
|
|
|
|
|
} |
541
|
16
|
50
|
|
|
|
36
|
unless ( defined($v) ){ |
542
|
0
|
|
|
|
|
0
|
Carp::croak("Bad argument. calc_Yc() needs a number"); |
543
|
|
|
|
|
|
|
} |
544
|
16
|
|
|
|
|
25
|
my $v2 = $v*$v; |
545
|
16
|
|
|
|
|
28
|
my $v3 = $v*$v2; |
546
|
16
|
|
|
|
|
18
|
my $v4 = $v2*$v2; |
547
|
16
|
|
|
|
|
21
|
my $v5 = $v2*$v3; |
548
|
16
|
|
|
|
|
123
|
return 1.1913*$v - 0.22532*$v2 + 0.23351*$v3 - 0.020483*$v4 + 0.00081936*$v5; |
549
|
|
|
|
|
|
|
} |
550
|
|
|
|
|
|
|
|
551
|
|
|
|
|
|
|
|
552
|
|
|
|
|
|
|
=head1 AUTHOR |
553
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
Takahiro Onodera, C<< >> |
555
|
|
|
|
|
|
|
|
556
|
|
|
|
|
|
|
=head1 BUGS |
557
|
|
|
|
|
|
|
|
558
|
|
|
|
|
|
|
Please report any bugs or feature requests to C, or through |
559
|
|
|
|
|
|
|
the web interface at L. I will be notified, and then you'll |
560
|
|
|
|
|
|
|
automatically be notified of progress on your bug as I make changes. |
561
|
|
|
|
|
|
|
|
562
|
|
|
|
|
|
|
=head1 SEE ALSO |
563
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
L, L |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
=head1 REFERENCES |
567
|
|
|
|
|
|
|
|
568
|
|
|
|
|
|
|
Munsell Color Science Laboratory, R.I.T - L - Munsell-xyY data are from this site. |
569
|
|
|
|
|
|
|
|
570
|
|
|
|
|
|
|
BruceLindbloom.com - L - Chromatic adaptation matrixes, transformation matrixes and important knowledge are from this site. |
571
|
|
|
|
|
|
|
|
572
|
|
|
|
|
|
|
Japanese Industrial Standards(JIS) JIS Z 8721(1993) |
573
|
|
|
|
|
|
|
|
574
|
|
|
|
|
|
|
=head1 LICENSE AND COPYRIGHT |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
Copyright 2010 Takahiro Onodera. |
577
|
|
|
|
|
|
|
|
578
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
579
|
|
|
|
|
|
|
under the terms of either: the GNU General Public License as published |
580
|
|
|
|
|
|
|
by the Free Software Foundation; or the Artistic License. |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
=cut |
583
|
|
|
|
|
|
|
|
584
|
|
|
|
|
|
|
1; # End of Color::Model::Munsell::Util |
585
|
|
|
|
|
|
|
__DATA__ |