PDL-Graphics-ColorSpace-0.206/0000755000175000017500000000000014743227146015666 5ustar osboxesosboxesPDL-Graphics-ColorSpace-0.206/META.json0000644000175000017500000000256714743227146017321 0ustar osboxesosboxes{ "abstract" : "unknown", "author" : [ "Maggie J. Xiong " ], "dynamic_config" : 1, "generated_by" : "ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010", "license" : [ "perl_5" ], "meta-spec" : { "url" : "http://search.cpan.org/perldoc?CPAN::Meta::Spec", "version" : 2 }, "name" : "PDL-Graphics-ColorSpace", "no_index" : { "directory" : [ "t", "inc" ] }, "prereqs" : { "build" : { "requires" : { "ExtUtils::MakeMaker" : "0" } }, "configure" : { "requires" : { "PDL" : "2.094" } }, "runtime" : { "requires" : { "PDL" : "2.094" } }, "test" : { "requires" : { "Test::More" : "0.88" } } }, "release_status" : "stable", "resources" : { "bugtracker" : { "web" : "https://github.com/PDLPorters/PDL-Graphics-ColorSpace/issues" }, "repository" : { "type" : "git", "url" : "git://github.com/PDLPorters/PDL-Graphics-ColorSpace.git", "web" : "https://github.com/PDLPorters/PDL-Graphics-ColorSpace" }, "x_IRC" : "irc://irc.perl.org/#pdl" }, "version" : "0.206", "x_serialization_backend" : "JSON::PP version 4.04" } PDL-Graphics-ColorSpace-0.206/ColorSpace/0000755000175000017500000000000014743227146017720 5ustar osboxesosboxesPDL-Graphics-ColorSpace-0.206/ColorSpace/RGBSpace.pm0000644000175000017500000004507014357646650021660 0ustar osboxesosboxespackage PDL::Graphics::ColorSpace::RGBSpace; use strict; use warnings; use PDL::LiteF; use Carp; =head1 NAME PDL::Graphics::ColorSpace::RGBSpace -- defines RGB space conversion parameters and white points. =head1 DESCRIPTION Sourced from Graphics::ColorObject (Izvorski & Reibenschuh, 2005). =head1 Usage use Data::Dumper; print Dumper $PDL::Graphics::ColorSpace::RGBSpace::RGB_SPACE; print Dumper $PDL::Graphics::ColorSpace::RGBSpace::WHITE_POINT; =cut our $WHITE_POINT = { 'D50' => [ '0.34567', '0.3585' ], 'A' => [ '0.44757', '0.40745' ], 'D75' => [ '0.29902', '0.31485' ], 'D55' => [ '0.33242', '0.34743' ], 'D65' => [ '0.312713', '0.329016' ], 'E' => [ '0.333333', '0.333333' ], 'B' => [ '0.34842', '0.35161' ], 'F11' => [ '0.38054', '0.37691' ], 'F2' => [ '0.37207', '0.37512' ], 'C' => [ '0.310063', '0.316158' ], 'D93' => [ '0.2848', '0.2932' ], 'F7' => [ '0.31285', '0.32918' ] }; our $RGB_SPACE = { 'BruceRGB' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.467384242424242', '0.240995', '0.0219086363636363' ], [ '0.294454030769231', '0.683554', '0.0736135076923076' ], [ '0.18863', '0.075452', '0.993451333333334' ] ]), 'white_point' => $WHITE_POINT->{'D65'}, }, 'Adobe RGB (1998)' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.576700121212121', '0.297361', '0.0270328181818181' ], [ '0.185555704225352', '0.627355', '0.0706878873239437' ], [ '0.1882125', '0.075285', '0.9912525' ] ]), 'white_point' => $WHITE_POINT->{'D65'}, }, 'WideGamut' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.716103566037736', '0.258187', '0' ], [ '0.100929624697337', '0.724938', '0.0517812857142858' ], [ '0.1471875', '0.016875', '0.7734375' ] ]), 'white_point' => $WHITE_POINT->{'D50'}, }, 'NTSC' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.606733727272727', '0.298839', '-1e-16' ], [ '0.173563816901409', '0.586811', '0.0661195492957747' ], [ '0.2001125', '0.11435', '1.1149125' ] ]), 'white_point' => $WHITE_POINT->{'C'}, }, 'Ekta Space PS5' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.59389231147541', '0.260629', '0' ], [ '0.272979942857143', '0.734946', '0.0419969142857143' ], [ '0.09735', '0.004425', '0.783225' ] ]), 'white_point' => $WHITE_POINT->{'D50'}, }, 'PAL/SECAM' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.430586181818182', '0.222021', '0.0201837272727273' ], [ '0.341545083333333', '0.706645', '0.129551583333333' ], [ '0.178335', '0.071334', '0.939231' ] ]), 'white_point' => $WHITE_POINT->{'D65'}, }, 'Apple RGB' => { 'gamma' => '1.8', 'm' => pdl([ [ '0.449694852941176', '0.244634', '0.0251829117647059' ], [ '0.316251294117647', '0.672034', '0.141183613445378' ], [ '0.184520857142857', '0.083332', '0.922604285714286' ] ]), 'white_point' => $WHITE_POINT->{'D65'}, }, 'sRGB' => { 'gamma' => '-1', # mark it for special case 'm' => pdl([ [ '0.412423757575757', '0.212656', '0.0193323636363636' ], [ '0.357579', '0.715158', '0.119193' ], [ '0.180465', '0.072186', '0.950449' ] ]), 'white_point' => $WHITE_POINT->{'D65'}, }, 'lsRGB' => { 'gamma' => 1.0, 'm' => pdl([ [ '0.412423757575757', '0.212656', '0.0193323636363636' ], [ '0.357579', '0.715158', '0.119193' ], [ '0.180465', '0.072186', '0.950449' ] ]), 'white_point' => $WHITE_POINT->{'D65'}, }, 'ColorMatch' => { 'gamma' => '1.8', 'm' => pdl([ [ '0.509343882352941', '0.274884', '0.0242544705882353' ], [ '0.320907338842975', '0.658132', '0.108782148760331' ], [ '0.13397', '0.066985', '0.692178333333333' ] ]), 'white_point' => $WHITE_POINT->{'D50'}, }, 'SMPTE-C' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.393555441176471', '0.212395', '0.0187407352941176' ], [ '0.365252420168067', '0.701049', '0.111932193277311' ], [ '0.191659714285714', '0.086556', '0.958298571428571' ] ]), 'white_point' => $WHITE_POINT->{'D65'}, }, 'CIE' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.488716754716981', '0.176204', '0' ], [ '0.310680460251046', '0.812985', '0.0102048326359833' ], [ '0.200604111111111', '0.010811', '0.989807111111111' ] ]), 'white_point' => $WHITE_POINT->{'E'}, }, 'ProPhoto' => { 'gamma' => '1.8', 'm' => pdl([ [ '0.797674285714286', '0.28804', '0' ], [ '0.135191683008091', '0.711874', '0' ], [ '0.031476', '8.6e-05', '0.828438' ] ]), 'white_point' => $WHITE_POINT->{'D50'}, }, 'BestRGB' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.632670026008293', '0.228457', '0' ], [ '0.204555716129032', '0.737352', '0.0095142193548387' ], [ '0.126995142857143', '0.034191', '0.815699571428571' ] ]), 'white_point' => $WHITE_POINT->{'D50'}, }, 'DonRGB4' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.645772', '0.27835', '0.0037113333333334' ], [ '0.193351045751634', '0.68797', '0.0179861437908497' ], [ '0.125097142857143', '0.03368', '0.803508571428572' ] ]), 'white_point' => $WHITE_POINT->{'D50'}, }, 'Beta RGB' => { 'gamma' => '2.2', 'm' => pdl([ [ '0.67125463496144', '0.303273', '1e-16' ], [ '0.1745833659118', '0.663786', '0.0407009558998808' ], [ '0.11838171875', '0.032941', '0.784501144886363' ] ]), 'white_point' => $WHITE_POINT->{'D50'}, }, 'ECI' => { 'gamma' => '1.8', 'm' => pdl([ [ '0.650204545454545', '0.32025', '-1e-16' ], [ '0.178077338028169', '0.602071', '0.067838985915493' ], [ '0.13593825', '0.077679', '0.75737025' ] ]), 'white_point' => $WHITE_POINT->{'D50'}, }, }; $RGB_SPACE->{$_}{white_point} = PDL->topdl($RGB_SPACE->{$_}{white_point}), $RGB_SPACE->{$_}{mstar} = $RGB_SPACE->{$_}{m}->inv for keys %$RGB_SPACE; # aliases $RGB_SPACE->{Adobe} = $RGB_SPACE->{'Adobe RGB (1998)'}; $RGB_SPACE->{'601'} = $RGB_SPACE->{NTSC}; $RGB_SPACE->{Apple} = $RGB_SPACE->{'Apple RGB'}; $RGB_SPACE->{'CIE ITU'} = $RGB_SPACE->{'PAL/SECAM'}; $RGB_SPACE->{PAL} = $RGB_SPACE->{'PAL/SECAM'}; $RGB_SPACE->{'709'} = $RGB_SPACE->{sRGB}; $RGB_SPACE->{SMPTE} = $RGB_SPACE->{'SMPTE-C'}; $RGB_SPACE->{'CIE Rec 709'} = $RGB_SPACE->{sRGB}; $RGB_SPACE->{'CIE Rec 601'} = $RGB_SPACE->{NTSC}; my @NEED_KEYS = grep $_ ne 'mstar', keys %{ $RGB_SPACE->{sRGB} }; sub add_rgb_space { my ($new_space) = @_; my @dup = grep $RGB_SPACE->{$_}, sort keys %$new_space; croak "Already existing RGB space definition with names @dup" if @dup; while (my ($name, $profile) = each %$new_space) { carp "Missing definition for custom RGB space $name: $_" for grep !defined $profile->{$_}, @NEED_KEYS; my $copy = {%$profile}; $copy->{m} = PDL->topdl($copy->{m}); $copy->{mstar} = $copy->{m}->inv if !exists $copy->{mstar}; $copy->{mstar} = PDL->topdl($copy->{mstar}); $copy->{white_point} = PDL->topdl($copy->{white_point}); $RGB_SPACE->{$name} = $copy; } } sub get_space { my ($space) = @_; croak "Please specify RGB Space ('sRGB' for generic JPEG images)!" if !$space; my $spec = ref($space) ? $space : $RGB_SPACE->{$space}; } 1; PDL-Graphics-ColorSpace-0.206/t/0000755000175000017500000000000014743227146016131 5ustar osboxesosboxesPDL-Graphics-ColorSpace-0.206/t/color_space.t0000644000175000017500000001411714743224110020577 0ustar osboxesosboxesuse strict; use warnings; use Test::More; use PDL::LiteF; use PDL::Graphics::ColorSpace; use Test::PDL; my $rgb = pdl( [255,255,255],[0,0,0],[255,10,50],[1,48,199] ) / 255; my $hsl = pdl( [0,0,1], [0,0,0], [350.204081632653,1,0.519607843137255], [225.757575757576,0.99,0.392156862745098] ); my $rgb_bad = $rgb->copy->setbadat(1,2); # rgb_to_hsl hsl_to_rgb { is_pdl rgb_to_hsl( $rgb ), $hsl, 'rgb_to_hsl'; is_pdl hsl_to_rgb( $hsl ), $rgb, 'hsl_to_rgb'; is_pdl rgb_to_hsl($rgb_bad), pdl('0 0 1; 0 0 0; BAD BAD BAD; 225.75758 0.99 0.39215686'), 'rgb_to_hsl with bad value'; is_pdl hsl_to_rgb($hsl->copy->setbadat(0,3)), pdl('1 1 1; 0 0 0; 1 0.039215686 0.19607843; BAD BAD BAD'), 'hsl_to_rgb with bad value'; } # rgb_to_cmyk cmyk_to_rgb { my $cmyk = pdl( [0,0,0,0], [0,0,0,1], [0,0.960784313725491,0.803921568627451,1.11022302462516e-16], [0.776470588235295, 0.592156862745098, 0, 0.219607843137255] ); is_pdl rgb_to_cmyk( $rgb ), $cmyk, 'rgb_to_cmyk'; is_pdl cmyk_to_rgb( $cmyk ), $rgb, 'cmyk_to_rgb'; is_pdl rgb_to_cmyk($rgb_bad), pdl('0 0 0 0; 0 0 0 1; BAD BAD BAD BAD; 0.77647059 0.59215686 0 0.21960784'), 'rgb_to_cmyk with bad value'; is_pdl cmyk_to_rgb($cmyk->copy->setbadat(0,3)), pdl('1 1 1; 0 0 0; 1 0.039215686 0.19607843; BAD BAD BAD'), 'cmyk_to_rgb with bad value'; } # rgb_to_hsv hsv_to_rgb { my $hsv = pdl( [0,0,1], [0,0,0], [350.204081632653,0.960784313725491,1], [225.757575757576,0.994974874371861,0.780392156862745] ); is_pdl rgb_to_hsv( $rgb ), $hsv, 'rgb_to_hsv'; is_pdl hsv_to_rgb( $hsv ), $rgb, 'hsv_to_rgb'; is_pdl rgb_to_hsv($rgb_bad), pdl('0 0 1; 0 0 0; BAD BAD BAD; 225.75758 0.99497487 0.78039216'), 'rgb_to_hsv with bad value'; is_pdl hsv_to_rgb($hsv->copy->setbadat(0,3)), pdl('1 1 1; 0 0 0; 1 0.039215686 0.19607843; BAD BAD BAD'), 'hsv_to_rgb with bad value'; } # rgb_to_xyz and rgb_{to,from}_linear { my $xyz = pdl( [0.950467757575757, 1, 1.08897436363636], [0,0,0], [0.419265223936783, 0.217129144548417, 0.0500096992920757], [0.113762127389896, 0.0624295703768394, 0.546353858701224] ); my $linear_ans = pdl( [1,1,1],[0,0,0], [1, 0.0030352698, 0.031896033], [0.00030352698, 0.029556834, 0.57112483] ); is_pdl rgb_to_xyz( $rgb, 'sRGB' ), $xyz, 'rgb_to_xyz sRGB'; my $rgb_linear = rgb_to_linear($rgb, -1); is_pdl $rgb_linear, $linear_ans, 'rgb_to_linear sRGB'; is_pdl rgb_from_linear($rgb_linear, -1), $rgb, 're-rgb_from_linear sRGB'; is_pdl rgb_to_xyz( $rgb_linear, 'lsRGB' ), $xyz, 'rgb_to_xyz lsRGB'; is_pdl xyz_to_rgb( $xyz, 'sRGB' ), $rgb, 'xyz_to_rgb sRGB'; my $rgb_ = pdl(255, 10, 50) / 255; my $ans = pdl( 0.582073320819542, 0.299955362786115, 0.0546021884576833 ); is_pdl rgb_to_xyz( $rgb_, 'Adobe' ), $ans, 'rgb_to_xyz Adobe'; $rgb_->inplace->rgb_to_xyz( 'Adobe' ); is_pdl $rgb_, $ans, 'rgb_to_xyz inplace'; } is_pdl xyY_to_xyz(pdl(0.312713, 0.329016, 1)), pdl(0.950449218275099, 1, 1.08891664843047), 'xyY_to_xyz'; # xyz_to_lab lab_to_xyz { my $xyz = pdl([0.4, 0.2, 0.02], [0,0,1]); my $lab = pdl([51.8372115265385, 82.2953523409701, 64.1921650722979], [0,0,-166.814773017556]); is_pdl $xyz->xyz_to_lab('sRGB'), $lab, 'xyz_to_lab sRGB'; is_pdl $lab->lab_to_xyz('sRGB'), $xyz, 'lab_to_xyz sRGB'; is_pdl $lab->lab_to_xyz($PDL::Graphics::ColorSpace::RGBSpace::RGB_SPACE->{sRGB}), $xyz, 'lab_to_xyz with hash-ref'; is_pdl $xyz->copy->setbadat(0,1)->xyz_to_lab('sRGB'), pdl('51.837212 82.295352 64.192165; BAD BAD BAD'), 'xyz_to_lab sRGB with bad value'; is_pdl $lab->copy->setbadat(0,1)->lab_to_xyz('sRGB'), pdl('0.4 0.2 0.02; BAD BAD BAD'), 'lab_to_xyz sRGB with bad value'; } # lab_to_lch and lch_to_lab { my $lab = pdl([53.380244, 79.817473, 64.822569], [0,0,1]); my $lch = pdl([53.380244, 102.824094685368, 39.081262060261], [0,1,90]); is_pdl lab_to_lch($lab), $lch, 'lab_to_lch'; is_pdl lch_to_lab($lch), $lab, 'lch_to_lab'; } # rgb_to_lch { my $rgb = pdl([25, 10, 243], [0,0,1]) / 255; my $lch = pdl([31.5634666908367, 126.828356633829, 306.221274674578], [ 0.0197916632671635, 0.403227926549451, 290.177020167939 ]); is_pdl rgb_to_lch($rgb, 'sRGB'), $lch, 'rgb_to_lch sRGB'; is_pdl lch_to_rgb($lch, 'sRGB'), $rgb, 'lch_to_rgb sRGB'; is_pdl $rgb->copy->setbadat(1,1)->rgb_to_lch('sRGB'), pdl('31.563467 126.82836 306.22127; BAD BAD BAD'), 'rgb_to_lch sRGB with bad value'; is_pdl $lch->copy->setbadat(1,1)->lch_to_rgb('sRGB'), pdl('0.098039216 0.039215686 0.95294118; BAD BAD BAD'), 'lch_to_rgb sRGB with bad value'; } # rgb_to_lab { my $rgb = pdl([25, 10, 243], [0,0,1]) / 255; my $lab = pdl([31.5634666908367, 74.943543, -102.31763], [ 0.0197916632671635, 0.13908209, -0.37848241]); is_pdl rgb_to_lab($rgb, 'sRGB'), $lab, 'rgb_to_lab sRGB'; is_pdl lab_to_rgb($lab, 'sRGB'), $rgb, 'lab_to_rgb sRGB'; is_pdl $rgb->copy->setbadat(1,1)->rgb_to_lab('sRGB'), pdl('31.563467 74.943543 -102.31763; BAD BAD BAD'), 'rgb_to_lab sRGB with bad value'; is_pdl $lab->copy->setbadat(1,1)->lab_to_rgb('sRGB'), pdl('0.098039195 0.039215694 0.95294118; BAD BAD BAD'), 'lab_to_rgb sRGB with bad value'; } # add_rgb_space { my %custom_space = ( custom_1 => { 'gamma' => '2.2', 'm' => [ [ '0.467384242424242', '0.240995', '0.0219086363636363' ], [ '0.294454030769231', '0.683554', '0.0736135076923076' ], [ '0.18863', '0.075452', '0.993451333333334' ] ], 'white_point' => [ '0.312713', '0.329016' ], }, ); PDL::Graphics::ColorSpace::add_rgb_space( \%custom_space ); my $rgb = pdl([25, 10, 243], [0,0,1]) / 255; # custom_1 is in fact copied from BruceRGB my $a_lch = rgb_to_lch($rgb, 'BruceRGB'); my $lch = rgb_to_lch($rgb, 'custom_1'); is_pdl $a_lch, $lch, 'rgb_to_lch with add_rgb_space'; eval {PDL::Graphics::ColorSpace::add_rgb_space( \%custom_space )}; like $@, qr/existing/, 'add duplicate croaks'; } done_testing(); PDL-Graphics-ColorSpace-0.206/color_space.h0000644000175000017500000000164414357645650020342 0ustar osboxesosboxes/* prototypes of functions in color_space.c */ double rgb_quant( double p, double q, double h ); void rgb2cmyk( double *rgb, double *cmyk ); void cmyk2rgb( double *cmyk, double *rgb ); void rgb2hsl( double *rgb, double *hsl ); void hsl2rgb( double *hsl, double *rgb ); void rgb2hsv( double *rgb, double *hsv ); void hsv2rgb( double *hsv, double *rgb ); void rgb2xyz( double *rgb, double gamma, double *m0, double *m1, double *m2, double *xyz ); void xyz2rgb( double *xyz, double gamma, double *m0, double *m1, double *m2, double *rgb ); void rgb2linear( double *rgb, double gamma, double *out ); void rgb2gamma( double *rgb, double gamma, double *out ); void xyY2xyz( double *xyY, double *xyz ); void xyz2lab( double *xyz, double *w, double *lab ); void lab2lch( double *lab, double *lch ); void lch2lab( double *lch, double *lab ); void lab2xyz( double *lab, double *w, double *xyz ); PDL-Graphics-ColorSpace-0.206/color_space.pd0000644000175000017500000006731414743226771020522 0ustar osboxesosboxespp_add_exported( 'rgb_to_xyz', 'xyz_to_rgb', 'xyz_to_lab', 'lab_to_xyz', 'rgb_to_lch', 'lch_to_rgb', 'rgb_to_lab', 'lab_to_rgb', 'add_rgb_space', ); $PDL::Graphics::ColorSpace::VERSION = '0.206'; pp_setversion("'$PDL::Graphics::ColorSpace::VERSION'"); pp_addpm({At=>'Top'}, <<'EOD'); =encoding utf8 =head1 NAME PDL::Graphics::ColorSpace - colour-space conversions for PDL =head1 SYNOPSIS use PDL::LiteF; use PDL::IO::Pic; use PDL::Graphics::ColorSpace; my $image_rgb = PDL->rpic('photo.jpg') if PDL->rpiccan('JPEG'); # convert RGB value from [0,255] to [0,1] $image_rgb = $image_rgb->double / 255; my $image_xyz = $image_rgb->rgb_to_xyz( 'sRGB' ); Or my $image_xyz = rgb_to_xyz( $image_rgb, 'sRGB' ); =head1 DESCRIPTION Does image color space conversions such as RGB to XYZ and Lab to LCH. Derived from Graphics::ColorObject (Izvorski & Reibenschuh, 2005) but since it's implemented in C and PDL, it runs *much* faster. Often the conversion will return out-of-gamut values. Retaining out-of-gamut values allows chained conversions to be lossless and reverse conversions to produce the original values. You can clip the values to be within-gamut if necessary. Please check the specific color space for the gamut range. =head1 COLOR SPACES =head2 RGB An RGB color space is any additive color space based on the RGB color model. A particular RGB color space is defined by the three chromaticities of the red, green, and blue additive primaries, and can produce any chromaticity that is the triangle defined by those primary colors. The complete specification of an RGB color space also requires a white point chromaticity and a gamma correction curve. For more info on the RGB color space, see L. This module expects and produces RGB values normalized to be in the range of [0,1]. If you have / need integer value between [0,255], divide or multiply the values by 255. =head2 CMYK CMYK refers to the four inks used in some color printing: cyan, magenta, yellow, and key (black). The CMYK model works by partially or entirely masking colors on a lighter, usually white, background. The ink reduces the light that would otherwise be reflected. Such a model is called subtractive because inks "subtract" brightness from white. In additive color models such as RGB, white is the "additive" combination of all primary colored lights, while black is the absence of light. In the CMYK model, it is the opposite: white is the natural color of the paper or other background, while black results from a full combination of colored inks. To save money on ink, and to produce deeper black tones, unsaturated and dark colors are produced by using black ink instead of the combination of cyan, magenta and yellow. For more info, see L. =head2 HSL Hue, Saturation and Luminance (or brightness). The HSL color space defines colors more naturally: Hue specifies the base color, the other two values then let you specify the saturation of that color and how bright the color should be. Hue is specified here as degrees ranging from 0 to 360. There are 6 base colors: 0 red 60 yellow 120 green 180 cyan 240 blue 300 magenta 360 red Saturation specifies the distance from the middle of the color wheel. So a saturation value of 0 (0%) means "center of the wheel", i.e. a grey value, whereas a saturation value of 1 (100%) means "at the border of the wheel", where the color is fully saturated. Luminance describes how "bright" the color is. 0 (0%) means 0 brightness and the color is black. 1 (100%) means maximum brightness and the color is white. For more info, see L. =head2 XYZ and xyY The CIE XYZ color space was derived the CIE RGB color space. XYZ are three hypothetical primaries. Y means brightness, Z is quasi-equal to blue stimulation, and X is a mix which looks like red sensitivity curve of cones. All visible colors can be represented by using only positive values of X, Y, and Z. The main advantage of the CIE XYZ space (and any color space based on it) is that this space is completely device-independent. For more info, see L. =head2 Lab A Lab color space is a color-opponent space with dimension L for lightness and a and b for the color-opponent dimensions, based on nonlinearly compressed CIE XYZ color space coordinates. It's derived from the "master" space CIE 1931 XYZ color space but is more perceptually uniform than XYZ. The Lab space is relative to the white point of the XYZ data they were converted from. Lab values do not define absolute colors unless the white point is also specified. For more info, see L. =head2 LCH This is possibly a little easier to comprehend than the Lab colour space, with which it shares several features. It is more correctly known as L*C*H*. Essentially it is in the form of a sphere. There are three axes; L* and C* and H°. The L* axis represents Lightness. This is vertical; from 0, which has no lightness (i.e. absolute black), at the bottom; through 50 in the middle, to 100 which is maximum lightness (i.e. absolute white) at the top. The C* axis represents Chroma or "saturation". This ranges from 0 at the centre of the circle, which is completely unsaturated (i.e. a neutral grey, black or white) to 100 or more at the edge of the circle for very high Chroma (saturation) or "colour purity". If we take a horizontal slice through the centre, we see a coloured circle. Around the edge of the circle we see every possible saturated colour, or Hue. This circular axis is known as H° for Hue. The units are in the form of degrees° (or angles), ranging from 0 (red) through 90 (yellow), 180 (green), 270 (blue) and back to 0. For more info, see L. =head1 OPTIONS Some conversions require specifying the RGB space which includes gamma curve and white point definitions. Supported RGB spaces include (aliases in square brackets): Adobe RGB (1998) [Adobe] Apple RGB [Apple] BestRGB Beta RGB BruceRGB CIE ColorMatch DonRGB4 ECI Ekta Space PS5 NTSC [601] [CIE Rec 601] PAL/SECAM [PAL] [CIE ITU] ProPhoto SMPTE-C [SMPTE] WideGamut sRGB [709] [CIE Rec 709] lsRGB You can also add custom RGB space definitions via the function add_rgb_space. Alternatively, as of 0.202 you can directly supply the function with a hash-ref in the same format. =head1 CONVERSIONS Some conversions, if not already included as functions, can be achieved by chaining existing functions. For example, LCH to HSV conversion can be achieved by chaining lch_to_rgb and rgb_to_hsv: my $hsv = rgb_to_hsv( lch_to_rgb( $lch, 'sRGB' ), 'sRGB' ); To generate a local diagram of what conversions are available between formats, using GraphViz, you can use this script: use blib; use PDL::Graphics::ColorSpace; print "digraph {\n"; print join(' -> ', split /_to_/), "\n" for grep !/^_/ && /_to_/, @PDL::Graphics::ColorSpace::EXPORT_OK; print "}\n"; # then: perl scriptname >d.dot; dot -Tsvg d.dot >d.svg; display d.svg (As of 0.202, this is everything to and from C, plus C <-> C <-> C) =cut use strict; use warnings; use Carp; use PDL::LiteF; use PDL::Graphics::ColorSpace::RGBSpace; my $RGB_SPACE = $PDL::Graphics::ColorSpace::RGBSpace::RGB_SPACE; EOD pp_addhdr(' #include #include "color_space.h" /* Local decs */ ' ); pp_def('rgb_to_cmyk', Pars => 'rgb(c=3); [o]cmyk(d=4)', HandleBad => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(rgb(c=>0)) || $ISBAD(rgb(c=>1)) || $ISBAD(rgb(c=>2))) { loop (d) %{ $SETBAD(cmyk()); %} /* skip to the next cmyk triple */ } else,) { rgb2cmyk($P(rgb), $P(cmyk)); } ', Doc => <<'DOCUMENTATION', =pod =for ref Converts an RGB color triple to an CMYK color quadruple. The first dimension of the ndarrays holding the rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). The first dimension of the ndarrays holding the cmyk values must be size 4. =for usage Usage: my $cmyk = rgb_to_cmyk( $rgb ); =cut DOCUMENTATION BadDoc => < encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated C, M, Y, and K values will all be marked as bad. =cut BADDOC ); pp_def('cmyk_to_rgb', Pars => 'cmyk(d=4); [o]rgb(c=3)', HandleBad => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(cmyk(d=>0)) || $ISBAD(cmyk(d=>1)) || $ISBAD(cmyk(d=>2)) || $ISBAD(cmyk(d=>3))) { loop (c) %{ $SETBAD(rgb()); %} /* skip to the next cmyk triple */ } else,) { cmyk2rgb($P(cmyk), $P(rgb)); } ', Doc => <<'DOCUMENTATION', =pod =for ref Converts an CMYK color quadruple to an RGB color triple The first dimension of the ndarrays holding the cmyk values must be size 4, i.e. the dimensions must look like (4, m, n, ...). The first dimension of the ndarray holding the rgb values must be 3. =for usage Usage: my $rgb = cmyk_to_rgb( $cmyk ); =cut DOCUMENTATION BadDoc => < encounters a bad value in any of the C, M, Y, or K quantities, the output ndarray will be marked as bad and the associated R, G, and B color values will all be marked as bad. =cut BADDOC ); pp_def('rgb_to_hsl', Pars => 'rgb(c=3); [o]hsl(c=3)', HandleBad => 1, Inplace => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(rgb(c=>0)) || $ISBAD(rgb(c=>1)) || $ISBAD(rgb(c=>2))) { loop (c) %{ $SETBAD(hsl()); %} /* skip to the next hsl triple */ } else,) { rgb2hsl($P(rgb), $P(hsl)); } ', Doc => <<'DOCUMENTATION', =pod =for ref Converts an RGB color triple to an HSL color triple. The first dimension of the ndarrays holding the hsl and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $hsl = rgb_to_hsl( $rgb ); =cut DOCUMENTATION BadDoc => < encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated H, S, and L values will all be marked as bad. =cut BADDOC ); pp_def('hsl_to_rgb', Pars => 'hsl(c=3); [o]rgb(c=3)', HandleBad => 1, Inplace => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(hsl(c=>0)) || $ISBAD(hsl(c=>1)) || $ISBAD(hsl(c=>2))) { loop (c) %{ $SETBAD(rgb()); %} /* skip to the next hsl triple */ } else,) { hsl2rgb($P(hsl), $P(rgb)); } ', Doc => <<'DOCUMENTATION', =pod =for ref Converts an HSL color triple to an RGB color triple The first dimension of the ndarrays holding the hsl and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $rgb = hsl_to_rgb( $hsl ); =cut DOCUMENTATION BadDoc => < encounters a bad value in any of the H, S, or V quantities, the output ndarray will be marked as bad and the associated R, G, and B color values will all be marked as bad. =cut BADDOC ); pp_def('rgb_to_hsv', Pars => 'rgb(c=3); [o]hsv(c=3)', HandleBad => 1, Inplace => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(rgb(c=>0)) || $ISBAD(rgb(c=>1)) || $ISBAD(rgb(c=>2))) { loop (c) %{ $SETBAD(hsv()); %} /* skip to the next hsv triple */ } else,) { rgb2hsv($P(rgb), $P(hsv)); } ', Doc => <<'DOCUMENTATION', =pod =for ref Converts an RGB color triple to an HSV color triple. The first dimension of the ndarrays holding the hsv and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $hsv = rgb_to_hsv( $rgb ); =cut DOCUMENTATION BadDoc => < encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated H, S, and V values will all be marked as bad. =cut BADDOC ); pp_def('hsv_to_rgb', Pars => 'hsv(c=3); [o]rgb(c=3)', HandleBad => 1, Inplace => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(hsv(c=>0)) || $ISBAD(hsv(c=>1)) || $ISBAD(hsv(c=>2))) { loop (c) %{ $SETBAD(rgb()); %} /* skip to the next rgb triple */ } else,) { hsv2rgb($P(hsv), $P(rgb)); } ', Doc => <<'DOCUMENTATION', =pod =for ref Converts an HSV color triple to an RGB color triple The first dimension of the ndarrays holding the hsv and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $rgb = hsv_to_rgb( $hsv ); =cut DOCUMENTATION BadDoc => < encounters a bad value in any of the H, S, or V quantities, the output ndarray will be marked as bad and the associated R, G, and B color values will all be marked as bad. =cut BADDOC ); pp_def('xyY_to_xyz', Pars => 'xyY(c=3); [o]xyz(c=3)', Doc => 'Internal function for white point calculation. Use it if you must.', HandleBad => 1, Inplace => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(xyY(c=>0)) || $ISBAD(xyY(c=>1)) || $ISBAD(xyY(c=>2))) { loop (c) %{ $SETBAD(xyz()); %} /* skip to the next hsl triple */ } else,) { xyY2xyz($P(xyY), $P(xyz)); } ', ); pp_def('_rgb_to_xyz', Pars => 'rgb(c=3); gamma(); m(i=3,rows=3); [o]xyz(c=3)', HandleBad => 1, Inplace => ['rgb'], GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(rgb(c=>0)) || $ISBAD(rgb(c=>1)) || $ISBAD(rgb(c=>2))) { loop (c) %{ $SETBAD(xyz()); %} /* skip to the next xyz triple */ } else,) { rgb2xyz($P(rgb), $gamma(), &$m(i=>0,rows=>0), &$m(i=>0,rows=>1), &$m(i=>0,rows=>2), $P(xyz)); } ', Doc => undef, ); pp_def('_xyz_to_rgb', Pars => 'xyz(c=3); gamma(); mstar(i=3,rows=3); [o]rgb(c=3)', HandleBad => 1, Inplace => ['xyz'], GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(xyz(c=>0)) || $ISBAD(xyz(c=>1)) || $ISBAD(xyz(c=>2))) { loop (c) %{ $SETBAD(rgb()); %} /* skip to the next rgb triple */ } else,) { xyz2rgb($P(xyz), $gamma(), &$mstar(i=>0,rows=>0), &$mstar(i=>0,rows=>1), &$mstar(i=>0,rows=>2), $P(rgb)); } ', Doc => undef, ); pp_def('_xyz_to_lab', Pars => 'xyz(c=3); w(d=2); [o]lab(c=3)', Doc => undef, HandleBad => 1, Inplace => ['xyz'], GenericTypes=>['D'], Code => ' /* construct white point */ double xyY[3] = { $w(d=>0), $w(d=>1), 1.0 }; double xyz_white[3]; xyY2xyz( &xyY[0], &xyz_white[0] ); threadloop %{ /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(xyz(c=>0)) || $ISBAD(xyz(c=>1)) || $ISBAD(xyz(c=>2))) { loop (c) %{ $SETBAD(lab()); %} /* skip to the next xyz triple */ } else,) { xyz2lab( $P(xyz), &xyz_white[0], $P(lab) ); } %} ', ); pp_def('_lab_to_xyz', Pars => 'lab(c=3); w(d=2); [o]xyz(c=3)', Doc => undef, HandleBad => 1, Inplace => ['lab'], GenericTypes=>['D'], Code => ' /* construct white point */ double xyY[3] = { $w(d=>0), $w(d=>1), 1.0 }; double xyz_white[3]; xyY2xyz( &xyY[0], &xyz_white[0] ); threadloop %{ /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(lab(c=>0)) || $ISBAD(lab(c=>1)) || $ISBAD(lab(c=>2))) { loop (c) %{ $SETBAD(xyz()); %} /* skip to the next lab triple */ } else,) { lab2xyz( $P(lab), &xyz_white[0], $P(xyz) ); } %} ', ); pp_def('lab_to_lch', Pars => 'lab(c=3); [o]lch(c=3)', HandleBad => 1, Inplace => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(lab(c=>0)) || $ISBAD(lab(c=>1)) || $ISBAD(lab(c=>2))) { loop (c) %{ $SETBAD(lch()); %} /* skip to the next lch triple */ } else,) { lab2lch( $P(lab), $P(lch) ); } ', Doc => <<'DOCUMENTATION', =pod =for ref Converts an Lab color triple to an LCH color triple. The first dimension of the ndarrays holding the lab values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $lch = lab_to_lch( $lab ); =cut DOCUMENTATION BadDoc => < encounters a bad value in any of the L, a, or b values the output ndarray will be marked as bad and the associated L, C, and H values will all be marked as bad. =cut BADDOC ); pp_def('lch_to_lab', Pars => 'lch(c=3); [o]lab(c=3)', HandleBad => 1, Inplace => 1, GenericTypes=>['D'], Code => ' /* First check for bad values */ PDL_IF_BAD(if ($ISBAD(lch(c=>0)) || $ISBAD(lch(c=>1)) || $ISBAD(lch(c=>2))) { loop (c) %{ $SETBAD(lab()); %} /* skip to the next lab triple */ } else,) { lch2lab( $P(lch), $P(lab) ); } ', Doc => <<'DOCUMENTATION', =pod =for ref Converts an LCH color triple to an Lab color triple. The first dimension of the ndarrays holding the lch values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $lab = lch_to_lab( $lch ); =cut DOCUMENTATION BadDoc => < encounters a bad value in any of the L, C, or H values the output ndarray will be marked as bad and the associated L, a, and b values will all be marked as bad. =cut BADDOC ); pp_def('rgb_to_linear', Pars => 'rgb(c=3); gamma(); [o]out(c=3)', HandleBad => 1, Inplace => ['rgb'], GenericTypes=>['D'], Code => ' PDL_IF_BAD(if ($ISBAD(rgb(c=>0)) || $ISBAD(rgb(c=>1)) || $ISBAD(rgb(c=>2))) { loop (c) %{ $SETBAD(out()); %} } else,) { rgb2linear( $P(rgb), $gamma(), $P(out) ); } ', Doc => <<'DOCUMENTATION', =for ref Converts an RGB color triple (presumably with gamma) to an RGB color triple with linear values. =for usage Usage: my $rgb_linear = rgb_to_linear( $gammaed, 2.2 ); DOCUMENTATION BadDoc => < encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. BADDOC ); pp_def('rgb_from_linear', Pars => 'rgb(c=3); gamma(); [o]out(c=3)', HandleBad => 1, Inplace => ['rgb'], GenericTypes=>['D'], Code => ' PDL_IF_BAD(if ($ISBAD(rgb(c=>0)) || $ISBAD(rgb(c=>1)) || $ISBAD(rgb(c=>2))) { loop (c) %{ $SETBAD(out()); %} } else,) { rgb2gamma( $P(rgb), $gamma(), $P(out) ); } ', Doc => <<'DOCUMENTATION', =for ref Converts an RGB color triple (presumably linear) to an RGB color triple with the specified gamma. =for usage Usage: my $gammaed = rgb_from_linear( $rgb_linear, 2.2 ); DOCUMENTATION BadDoc => < encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. BADDOC ); pp_addpm(<<'EOD'); =head2 rgb_to_xyz =for ref Converts an RGB color triple to an XYZ color triple. The first dimension of the ndarrays holding the rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated X, Y, and Z values will all be marked as bad. =for usage Usage: my $xyz = rgb_to_xyz( $rgb, 'sRGB' ); my $xyz = rgb_to_xyz( $rgb, \%rgb_spec ); =cut *rgb_to_xyz = \&PDL::rgb_to_xyz; sub PDL::rgb_to_xyz { my ($rgb, $space) = @_; my $spec = get_space($space); my $m = PDL->topdl( $spec->{m} ); return _rgb_to_xyz( $rgb, $spec->{gamma}, $m ); } =head2 xyz_to_rgb =for ref Converts an XYZ color triple to an RGB color triple. The first dimension of the ndarrays holding the xyz and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the X, Y, or Z values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. =for usage Usage: my $rgb = xyz_to_rgb( $xyz, 'sRGB' ); my $rgb = xyz_to_rgb( $xyz, \%rgb_spec ); =cut *xyz_to_rgb = \&PDL::xyz_to_rgb; sub PDL::xyz_to_rgb { my ($xyz, $space) = @_; my $spec = get_space($space); my $mstar = exists $spec->{mstar} ? PDL->topdl( $spec->{mstar} ) : PDL->topdl( $spec->{m} )->inv; return _xyz_to_rgb( $xyz, $spec->{gamma}, $mstar ); } =head2 xyz_to_lab =for ref Converts an XYZ color triple to an Lab color triple. The first dimension of the ndarrays holding the xyz values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the X, Y, or Z values the output ndarray will be marked as bad and the associated L, a, and b values will all be marked as bad. =for usage Usage: my $lab = xyz_to_lab( $xyz, 'sRGB' ); my $lab = xyz_to_lab( $xyz, \%rgb_spec ); =cut *xyz_to_lab = \&PDL::xyz_to_lab; sub PDL::xyz_to_lab { my ($xyz, $space) = @_; my $spec = get_space($space); my $w = PDL->topdl($spec->{white_point}); return _xyz_to_lab( $xyz, $w ); } =head2 lab_to_xyz =for ref Converts an Lab color triple to an XYZ color triple. The first dimension of the ndarrays holding the lab values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the L, a, or b values the output ndarray will be marked as bad and the associated X, Y, and Z values will all be marked as bad. =for usage Usage: my $xyz = lab_to_xyz( $lab, 'sRGB' ); my $xyz = lab_to_xyz( $lab, \%rgb_spec ); =cut *lab_to_xyz = \&PDL::lab_to_xyz; sub PDL::lab_to_xyz { my ($lab, $space) = @_; my $spec = get_space($space); my $w = PDL->topdl($spec->{white_point}); return _lab_to_xyz( $lab, $w ); } =head2 rgb_to_lch =for ref Converts an RGB color triple to an LCH color triple. The first dimension of the ndarrays holding the rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated L, C, and H values will all be marked as bad. =for usage Usage: my $lch = rgb_to_lch( $rgb, 'sRGB' ); my $lch = rgb_to_lch( $rgb, \%rgb_spec ); =cut *rgb_to_lch = \&PDL::rgb_to_lch; sub PDL::rgb_to_lch { my ($rgb, $space) = @_; my $spec = get_space($space); my $lab = xyz_to_lab( rgb_to_xyz( $rgb, $spec ), $spec ); return lab_to_lch( $lab ); } =head2 lch_to_rgb =for ref Converts an LCH color triple to an RGB color triple. The first dimension of the ndarrays holding the lch values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the L, C, or H values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. =for usage Usage: my $rgb = lch_to_rgb( $lch, 'sRGB' ); my $rgb = lch_to_rgb( $lch, \%rgb_spec ); =cut *lch_to_rgb = \&PDL::lch_to_rgb; sub PDL::lch_to_rgb { my ($lch, $space) = @_; my $spec = get_space($space); my $xyz = lab_to_xyz( lch_to_lab( $lch ), $spec ); return xyz_to_rgb( $xyz, $spec ); } =head2 rgb_to_lab =for ref Converts an RGB color triple to an LAB color triple. The first dimension of the ndarrays holding the rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated L, A, and B values will all be marked as bad. =for usage Usage: my $lab = rgb_to_lab( $rgb, 'sRGB' ); my $lab = rgb_to_lab( $rgb, \%rgb_spec ); =cut *rgb_to_lab = \&PDL::rgb_to_lab; sub PDL::rgb_to_lab { my ($rgb, $space) = @_; my $spec = get_space($space); return xyz_to_lab( rgb_to_xyz( $rgb, $spec ), $spec ); } =head2 lab_to_rgb =for ref Converts an LAB color triple to an RGB color triple. The first dimension of the ndarrays holding the lab values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the L, A, or B values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. =for usage Usage: my $rgb = lab_to_rgb( $lab, 'sRGB' ); my $rgb = lab_to_rgb( $lab, \%rgb_spec ); =cut *lab_to_rgb = \&PDL::lab_to_rgb; sub PDL::lab_to_rgb { my ($lab, $space) = @_; my $spec = get_space($space); return xyz_to_rgb( lab_to_xyz( $lab, $spec ), $spec ); } =head2 add_rgb_space Supports adding custom RGB space definitions. The C and C can be supplied as PDL ndarrays if desired. As of 0.202, you don't need to provide an C since the inverse of the C will be calculated (once) as a default. Usage: my %custom_space = ( custom_1 => { 'gamma' => '2.2', 'm' => [ [ '0.467384242424242', '0.240995', '0.0219086363636363' ], [ '0.294454030769231', '0.683554', '0.0736135076923076' ], [ '0.18863', '0.075452', '0.993451333333334' ] ], 'white_point' => [ '0.312713', '0.329016' ], }, custom_2 => { ... }, ); add_rgb_space( \%custom_space ); my $rgb = lch_to_rgb( $lch, 'custom_1' ); =cut *add_rgb_space = \&PDL::Graphics::ColorSpace::RGBSpace::add_rgb_space; *get_space = \&PDL::Graphics::ColorSpace::RGBSpace::get_space; =head1 SEE ALSO Graphics::ColorObject =head1 AUTHOR ~~~~~~~~~~~~ ~~~~~ ~~~~~~~~ ~~~~~ ~~~ `` ><((("> Copyright (C) 2012 Maggie J. Xiong Original work sponsored by Shutterstock, LLC L All rights reserved. There is no warranty. You are allowed to redistribute this software / documentation as described in the file COPYING in the PDL distribution. =cut EOD pp_done(); PDL-Graphics-ColorSpace-0.206/GENERATED/0000755000175000017500000000000014743227146017164 5ustar osboxesosboxesPDL-Graphics-ColorSpace-0.206/GENERATED/PDL/0000755000175000017500000000000014743227146017603 5ustar osboxesosboxesPDL-Graphics-ColorSpace-0.206/GENERATED/PDL/Graphics/0000755000175000017500000000000014743227146021343 5ustar osboxesosboxesPDL-Graphics-ColorSpace-0.206/GENERATED/PDL/Graphics/ColorSpace.pm0000644000175000017500000005433514743227146023745 0ustar osboxesosboxes# # GENERATED WITH PDL::PP from color_space.pd! Don't modify! # package PDL::Graphics::ColorSpace; our @EXPORT_OK = qw(rgb_to_xyz xyz_to_rgb xyz_to_lab lab_to_xyz rgb_to_lch lch_to_rgb rgb_to_lab lab_to_rgb add_rgb_space rgb_to_cmyk cmyk_to_rgb rgb_to_hsl hsl_to_rgb rgb_to_hsv hsv_to_rgb xyY_to_xyz _rgb_to_xyz _xyz_to_rgb _xyz_to_lab _lab_to_xyz lab_to_lch lch_to_lab rgb_to_linear rgb_from_linear ); our %EXPORT_TAGS = (Func=>\@EXPORT_OK); use PDL::Core; use PDL::Exporter; use DynaLoader; our $VERSION = '0.206'; our @ISA = ( 'PDL::Exporter','DynaLoader' ); push @PDL::Core::PP, __PACKAGE__; bootstrap PDL::Graphics::ColorSpace $VERSION; #line 13 "color_space.pd" =encoding utf8 =head1 NAME PDL::Graphics::ColorSpace - colour-space conversions for PDL =head1 SYNOPSIS use PDL::LiteF; use PDL::IO::Pic; use PDL::Graphics::ColorSpace; my $image_rgb = PDL->rpic('photo.jpg') if PDL->rpiccan('JPEG'); # convert RGB value from [0,255] to [0,1] $image_rgb = $image_rgb->double / 255; my $image_xyz = $image_rgb->rgb_to_xyz( 'sRGB' ); Or my $image_xyz = rgb_to_xyz( $image_rgb, 'sRGB' ); =head1 DESCRIPTION Does image color space conversions such as RGB to XYZ and Lab to LCH. Derived from Graphics::ColorObject (Izvorski & Reibenschuh, 2005) but since it's implemented in C and PDL, it runs *much* faster. Often the conversion will return out-of-gamut values. Retaining out-of-gamut values allows chained conversions to be lossless and reverse conversions to produce the original values. You can clip the values to be within-gamut if necessary. Please check the specific color space for the gamut range. =head1 COLOR SPACES =head2 RGB An RGB color space is any additive color space based on the RGB color model. A particular RGB color space is defined by the three chromaticities of the red, green, and blue additive primaries, and can produce any chromaticity that is the triangle defined by those primary colors. The complete specification of an RGB color space also requires a white point chromaticity and a gamma correction curve. For more info on the RGB color space, see L. This module expects and produces RGB values normalized to be in the range of [0,1]. If you have / need integer value between [0,255], divide or multiply the values by 255. =head2 CMYK CMYK refers to the four inks used in some color printing: cyan, magenta, yellow, and key (black). The CMYK model works by partially or entirely masking colors on a lighter, usually white, background. The ink reduces the light that would otherwise be reflected. Such a model is called subtractive because inks "subtract" brightness from white. In additive color models such as RGB, white is the "additive" combination of all primary colored lights, while black is the absence of light. In the CMYK model, it is the opposite: white is the natural color of the paper or other background, while black results from a full combination of colored inks. To save money on ink, and to produce deeper black tones, unsaturated and dark colors are produced by using black ink instead of the combination of cyan, magenta and yellow. For more info, see L. =head2 HSL Hue, Saturation and Luminance (or brightness). The HSL color space defines colors more naturally: Hue specifies the base color, the other two values then let you specify the saturation of that color and how bright the color should be. Hue is specified here as degrees ranging from 0 to 360. There are 6 base colors: 0 red 60 yellow 120 green 180 cyan 240 blue 300 magenta 360 red Saturation specifies the distance from the middle of the color wheel. So a saturation value of 0 (0%) means "center of the wheel", i.e. a grey value, whereas a saturation value of 1 (100%) means "at the border of the wheel", where the color is fully saturated. Luminance describes how "bright" the color is. 0 (0%) means 0 brightness and the color is black. 1 (100%) means maximum brightness and the color is white. For more info, see L. =head2 XYZ and xyY The CIE XYZ color space was derived the CIE RGB color space. XYZ are three hypothetical primaries. Y means brightness, Z is quasi-equal to blue stimulation, and X is a mix which looks like red sensitivity curve of cones. All visible colors can be represented by using only positive values of X, Y, and Z. The main advantage of the CIE XYZ space (and any color space based on it) is that this space is completely device-independent. For more info, see L. =head2 Lab A Lab color space is a color-opponent space with dimension L for lightness and a and b for the color-opponent dimensions, based on nonlinearly compressed CIE XYZ color space coordinates. It's derived from the "master" space CIE 1931 XYZ color space but is more perceptually uniform than XYZ. The Lab space is relative to the white point of the XYZ data they were converted from. Lab values do not define absolute colors unless the white point is also specified. For more info, see L. =head2 LCH This is possibly a little easier to comprehend than the Lab colour space, with which it shares several features. It is more correctly known as L*C*H*. Essentially it is in the form of a sphere. There are three axes; L* and C* and H°. The L* axis represents Lightness. This is vertical; from 0, which has no lightness (i.e. absolute black), at the bottom; through 50 in the middle, to 100 which is maximum lightness (i.e. absolute white) at the top. The C* axis represents Chroma or "saturation". This ranges from 0 at the centre of the circle, which is completely unsaturated (i.e. a neutral grey, black or white) to 100 or more at the edge of the circle for very high Chroma (saturation) or "colour purity". If we take a horizontal slice through the centre, we see a coloured circle. Around the edge of the circle we see every possible saturated colour, or Hue. This circular axis is known as H° for Hue. The units are in the form of degrees° (or angles), ranging from 0 (red) through 90 (yellow), 180 (green), 270 (blue) and back to 0. For more info, see L. =head1 OPTIONS Some conversions require specifying the RGB space which includes gamma curve and white point definitions. Supported RGB spaces include (aliases in square brackets): Adobe RGB (1998) [Adobe] Apple RGB [Apple] BestRGB Beta RGB BruceRGB CIE ColorMatch DonRGB4 ECI Ekta Space PS5 NTSC [601] [CIE Rec 601] PAL/SECAM [PAL] [CIE ITU] ProPhoto SMPTE-C [SMPTE] WideGamut sRGB [709] [CIE Rec 709] lsRGB You can also add custom RGB space definitions via the function add_rgb_space. Alternatively, as of 0.202 you can directly supply the function with a hash-ref in the same format. =head1 CONVERSIONS Some conversions, if not already included as functions, can be achieved by chaining existing functions. For example, LCH to HSV conversion can be achieved by chaining lch_to_rgb and rgb_to_hsv: my $hsv = rgb_to_hsv( lch_to_rgb( $lch, 'sRGB' ), 'sRGB' ); To generate a local diagram of what conversions are available between formats, using GraphViz, you can use this script: use blib; use PDL::Graphics::ColorSpace; print "digraph {\n"; print join(' -> ', split /_to_/), "\n" for grep !/^_/ && /_to_/, @PDL::Graphics::ColorSpace::EXPORT_OK; print "}\n"; # then: perl scriptname >d.dot; dot -Tsvg d.dot >d.svg; display d.svg (As of 0.202, this is everything to and from C, plus C <-> C <-> C) =cut use strict; use warnings; use Carp; use PDL::LiteF; use PDL::Graphics::ColorSpace::RGBSpace; my $RGB_SPACE = $PDL::Graphics::ColorSpace::RGBSpace::RGB_SPACE; #line 181 "ColorSpace.pm" =head1 FUNCTIONS =cut =head2 rgb_to_cmyk =for sig Signature: (rgb(c=3); [o]cmyk(d=4)) Types: (double) =pod =for ref Converts an RGB color triple to an CMYK color quadruple. The first dimension of the ndarrays holding the rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). The first dimension of the ndarrays holding the cmyk values must be size 4. =for usage Usage: my $cmyk = rgb_to_cmyk( $rgb ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated C, M, Y, and K values will all be marked as bad. =cut *rgb_to_cmyk = \&PDL::rgb_to_cmyk; =head2 cmyk_to_rgb =for sig Signature: (cmyk(d=4); [o]rgb(c=3)) Types: (double) =pod =for ref Converts an CMYK color quadruple to an RGB color triple The first dimension of the ndarrays holding the cmyk values must be size 4, i.e. the dimensions must look like (4, m, n, ...). The first dimension of the ndarray holding the rgb values must be 3. =for usage Usage: my $rgb = cmyk_to_rgb( $cmyk ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the C, M, Y, or K quantities, the output ndarray will be marked as bad and the associated R, G, and B color values will all be marked as bad. =cut *cmyk_to_rgb = \&PDL::cmyk_to_rgb; =head2 rgb_to_hsl =for sig Signature: (rgb(c=3); [o]hsl(c=3)) Types: (double) =pod =for ref Converts an RGB color triple to an HSL color triple. The first dimension of the ndarrays holding the hsl and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $hsl = rgb_to_hsl( $rgb ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated H, S, and L values will all be marked as bad. =cut *rgb_to_hsl = \&PDL::rgb_to_hsl; =head2 hsl_to_rgb =for sig Signature: (hsl(c=3); [o]rgb(c=3)) Types: (double) =pod =for ref Converts an HSL color triple to an RGB color triple The first dimension of the ndarrays holding the hsl and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $rgb = hsl_to_rgb( $hsl ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the H, S, or V quantities, the output ndarray will be marked as bad and the associated R, G, and B color values will all be marked as bad. =cut *hsl_to_rgb = \&PDL::hsl_to_rgb; =head2 rgb_to_hsv =for sig Signature: (rgb(c=3); [o]hsv(c=3)) Types: (double) =pod =for ref Converts an RGB color triple to an HSV color triple. The first dimension of the ndarrays holding the hsv and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $hsv = rgb_to_hsv( $rgb ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated H, S, and V values will all be marked as bad. =cut *rgb_to_hsv = \&PDL::rgb_to_hsv; =head2 hsv_to_rgb =for sig Signature: (hsv(c=3); [o]rgb(c=3)) Types: (double) =pod =for ref Converts an HSV color triple to an RGB color triple The first dimension of the ndarrays holding the hsv and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $rgb = hsv_to_rgb( $hsv ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the H, S, or V quantities, the output ndarray will be marked as bad and the associated R, G, and B color values will all be marked as bad. =cut *hsv_to_rgb = \&PDL::hsv_to_rgb; =head2 xyY_to_xyz =for sig Signature: (xyY(c=3); [o]xyz(c=3)) Types: (double) =for usage $xyz = xyY_to_xyz($xyY); xyY_to_xyz($xyY, $xyz); # all arguments given $xyz = $xyY->xyY_to_xyz; # method call $xyY->xyY_to_xyz($xyz); $xyY->inplace->xyY_to_xyz; # can be used inplace xyY_to_xyz($xyY->inplace); =for ref Internal function for white point calculation. Use it if you must. =pod Broadcasts over its inputs. =for bad C processes bad values. It will set the bad-value flag of all output ndarrays if the flag is set for any of the input ndarrays. =cut *xyY_to_xyz = \&PDL::xyY_to_xyz; *_rgb_to_xyz = \&PDL::_rgb_to_xyz; *_xyz_to_rgb = \&PDL::_xyz_to_rgb; *_xyz_to_lab = \&PDL::_xyz_to_lab; *_lab_to_xyz = \&PDL::_lab_to_xyz; =head2 lab_to_lch =for sig Signature: (lab(c=3); [o]lch(c=3)) Types: (double) =pod =for ref Converts an Lab color triple to an LCH color triple. The first dimension of the ndarrays holding the lab values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $lch = lab_to_lch( $lab ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the L, a, or b values the output ndarray will be marked as bad and the associated L, C, and H values will all be marked as bad. =cut *lab_to_lch = \&PDL::lab_to_lch; =head2 lch_to_lab =for sig Signature: (lch(c=3); [o]lab(c=3)) Types: (double) =pod =for ref Converts an LCH color triple to an Lab color triple. The first dimension of the ndarrays holding the lch values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for usage Usage: my $lab = lch_to_lab( $lch ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the L, C, or H values the output ndarray will be marked as bad and the associated L, a, and b values will all be marked as bad. =cut *lch_to_lab = \&PDL::lch_to_lab; =head2 rgb_to_linear =for sig Signature: (rgb(c=3); gamma(); [o]out(c=3)) Types: (double) =for ref Converts an RGB color triple (presumably with gamma) to an RGB color triple with linear values. =for usage Usage: my $rgb_linear = rgb_to_linear( $gammaed, 2.2 ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. =cut *rgb_to_linear = \&PDL::rgb_to_linear; =head2 rgb_from_linear =for sig Signature: (rgb(c=3); gamma(); [o]out(c=3)) Types: (double) =for ref Converts an RGB color triple (presumably linear) to an RGB color triple with the specified gamma. =for usage Usage: my $gammaed = rgb_from_linear( $rgb_linear, 2.2 ); =pod Broadcasts over its inputs. =for bad =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. =cut *rgb_from_linear = \&PDL::rgb_from_linear; #line 752 "color_space.pd" =head2 rgb_to_xyz =for ref Converts an RGB color triple to an XYZ color triple. The first dimension of the ndarrays holding the rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated X, Y, and Z values will all be marked as bad. =for usage Usage: my $xyz = rgb_to_xyz( $rgb, 'sRGB' ); my $xyz = rgb_to_xyz( $rgb, \%rgb_spec ); =cut *rgb_to_xyz = \&PDL::rgb_to_xyz; sub PDL::rgb_to_xyz { my ($rgb, $space) = @_; my $spec = get_space($space); my $m = PDL->topdl( $spec->{m} ); return _rgb_to_xyz( $rgb, $spec->{gamma}, $m ); } =head2 xyz_to_rgb =for ref Converts an XYZ color triple to an RGB color triple. The first dimension of the ndarrays holding the xyz and rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the X, Y, or Z values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. =for usage Usage: my $rgb = xyz_to_rgb( $xyz, 'sRGB' ); my $rgb = xyz_to_rgb( $xyz, \%rgb_spec ); =cut *xyz_to_rgb = \&PDL::xyz_to_rgb; sub PDL::xyz_to_rgb { my ($xyz, $space) = @_; my $spec = get_space($space); my $mstar = exists $spec->{mstar} ? PDL->topdl( $spec->{mstar} ) : PDL->topdl( $spec->{m} )->inv; return _xyz_to_rgb( $xyz, $spec->{gamma}, $mstar ); } =head2 xyz_to_lab =for ref Converts an XYZ color triple to an Lab color triple. The first dimension of the ndarrays holding the xyz values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the X, Y, or Z values the output ndarray will be marked as bad and the associated L, a, and b values will all be marked as bad. =for usage Usage: my $lab = xyz_to_lab( $xyz, 'sRGB' ); my $lab = xyz_to_lab( $xyz, \%rgb_spec ); =cut *xyz_to_lab = \&PDL::xyz_to_lab; sub PDL::xyz_to_lab { my ($xyz, $space) = @_; my $spec = get_space($space); my $w = PDL->topdl($spec->{white_point}); return _xyz_to_lab( $xyz, $w ); } =head2 lab_to_xyz =for ref Converts an Lab color triple to an XYZ color triple. The first dimension of the ndarrays holding the lab values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the L, a, or b values the output ndarray will be marked as bad and the associated X, Y, and Z values will all be marked as bad. =for usage Usage: my $xyz = lab_to_xyz( $lab, 'sRGB' ); my $xyz = lab_to_xyz( $lab, \%rgb_spec ); =cut *lab_to_xyz = \&PDL::lab_to_xyz; sub PDL::lab_to_xyz { my ($lab, $space) = @_; my $spec = get_space($space); my $w = PDL->topdl($spec->{white_point}); return _lab_to_xyz( $lab, $w ); } =head2 rgb_to_lch =for ref Converts an RGB color triple to an LCH color triple. The first dimension of the ndarrays holding the rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated L, C, and H values will all be marked as bad. =for usage Usage: my $lch = rgb_to_lch( $rgb, 'sRGB' ); my $lch = rgb_to_lch( $rgb, \%rgb_spec ); =cut *rgb_to_lch = \&PDL::rgb_to_lch; sub PDL::rgb_to_lch { my ($rgb, $space) = @_; my $spec = get_space($space); my $lab = xyz_to_lab( rgb_to_xyz( $rgb, $spec ), $spec ); return lab_to_lch( $lab ); } =head2 lch_to_rgb =for ref Converts an LCH color triple to an RGB color triple. The first dimension of the ndarrays holding the lch values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the L, C, or H values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. =for usage Usage: my $rgb = lch_to_rgb( $lch, 'sRGB' ); my $rgb = lch_to_rgb( $lch, \%rgb_spec ); =cut *lch_to_rgb = \&PDL::lch_to_rgb; sub PDL::lch_to_rgb { my ($lch, $space) = @_; my $spec = get_space($space); my $xyz = lab_to_xyz( lch_to_lab( $lch ), $spec ); return xyz_to_rgb( $xyz, $spec ); } =head2 rgb_to_lab =for ref Converts an RGB color triple to an LAB color triple. The first dimension of the ndarrays holding the rgb values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the R, G, or B values the output ndarray will be marked as bad and the associated L, A, and B values will all be marked as bad. =for usage Usage: my $lab = rgb_to_lab( $rgb, 'sRGB' ); my $lab = rgb_to_lab( $rgb, \%rgb_spec ); =cut *rgb_to_lab = \&PDL::rgb_to_lab; sub PDL::rgb_to_lab { my ($rgb, $space) = @_; my $spec = get_space($space); return xyz_to_lab( rgb_to_xyz( $rgb, $spec ), $spec ); } =head2 lab_to_rgb =for ref Converts an LAB color triple to an RGB color triple. The first dimension of the ndarrays holding the lab values must be size 3, i.e. the dimensions must look like (3, m, n, ...). =for bad If C encounters a bad value in any of the L, A, or B values the output ndarray will be marked as bad and the associated R, G, and B values will all be marked as bad. =for usage Usage: my $rgb = lab_to_rgb( $lab, 'sRGB' ); my $rgb = lab_to_rgb( $lab, \%rgb_spec ); =cut *lab_to_rgb = \&PDL::lab_to_rgb; sub PDL::lab_to_rgb { my ($lab, $space) = @_; my $spec = get_space($space); return xyz_to_rgb( lab_to_xyz( $lab, $spec ), $spec ); } =head2 add_rgb_space Supports adding custom RGB space definitions. The C and C can be supplied as PDL ndarrays if desired. As of 0.202, you don't need to provide an C since the inverse of the C will be calculated (once) as a default. Usage: my %custom_space = ( custom_1 => { 'gamma' => '2.2', 'm' => [ [ '0.467384242424242', '0.240995', '0.0219086363636363' ], [ '0.294454030769231', '0.683554', '0.0736135076923076' ], [ '0.18863', '0.075452', '0.993451333333334' ] ], 'white_point' => [ '0.312713', '0.329016' ], }, custom_2 => { ... }, ); add_rgb_space( \%custom_space ); my $rgb = lch_to_rgb( $lch, 'custom_1' ); =cut *add_rgb_space = \&PDL::Graphics::ColorSpace::RGBSpace::add_rgb_space; *get_space = \&PDL::Graphics::ColorSpace::RGBSpace::get_space; =head1 SEE ALSO Graphics::ColorObject =head1 AUTHOR ~~~~~~~~~~~~ ~~~~~ ~~~~~~~~ ~~~~~ ~~~ `` ><((("> Copyright (C) 2012 Maggie J. Xiong Original work sponsored by Shutterstock, LLC L All rights reserved. There is no warranty. You are allowed to redistribute this software / documentation as described in the file COPYING in the PDL distribution. =cut #line 974 "ColorSpace.pm" # Exit with OK status 1; PDL-Graphics-ColorSpace-0.206/META.yml0000644000175000017500000000135714743227146017145 0ustar osboxesosboxes--- abstract: unknown author: - 'Maggie J. Xiong ' build_requires: ExtUtils::MakeMaker: '0' Test::More: '0.88' configure_requires: PDL: '2.094' dynamic_config: 1 generated_by: 'ExtUtils::MakeMaker version 7.44, CPAN::Meta::Converter version 2.150010' license: perl meta-spec: url: http://module-build.sourceforge.net/META-spec-v1.4.html version: '1.4' name: PDL-Graphics-ColorSpace no_index: directory: - t - inc requires: PDL: '2.094' resources: IRC: irc://irc.perl.org/#pdl bugtracker: https://github.com/PDLPorters/PDL-Graphics-ColorSpace/issues repository: git://github.com/PDLPorters/PDL-Graphics-ColorSpace.git version: '0.206' x_serialization_backend: 'CPAN::Meta::YAML version 0.018' PDL-Graphics-ColorSpace-0.206/Changes0000644000175000017500000000170514743227034017160 0ustar osboxesosboxes0.205 2025-01-19 - add M_PI #define - thanks @navoj 0.205 2025-01-16 - fix NAME section of docs 0.204 2025-01-06 - add licence information 0.203 2023-01-12 - fix xyY_to_xyz to have Y of 1 0.202 2023-01-12 - no need to supply mstar for add_rgb_space - can supply add_rgb_space-format hash-ref to the relevant functions - add rgb_to_lab, lab_to_rgb, rgb_{to,from}_linear - add lsRGB colour space - operations can work inplace 0.201 2022-02-14 - more accurate dependencies - adjust so docs get generated for MetaCPAN benefit 0.200 2022-02-14 - opt in to PDL 2.058+ multi-C - add GitHub repo metadata 0.1.0 01-04-2013 Added support for custom RGB space. 0.0.9 02-11-2012 Fixed pp and perl 5.8* version format issue. 0.0.8 02-09-2012 Added CMYK and RGB conversions. Better handling of versions. 0.0.7 02-07-2012 Fixed double * warnings. Thanks Sisypus! Added README and Changes. 0.0.6 02-01-2012 First version released on an unsuspecting world. PDL-Graphics-ColorSpace-0.206/color_space.c0000644000175000017500000001534114743217244020325 0ustar osboxesosboxes#include #include /* Needed this to compile on strawberry-perl-5.40.0.1-64bit-PDL */ #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #define epsilon 0.008856 #define kappa 903.3 struct pixel { double a; double b; double c; }; #include "color_space.h" /* Local decs */ /*** util functions ***/ double _apow (double a, double p) { return pow(a >= 0.0 ? a : -a, p); } #define EXTREME(var, a, b, c, cmp) \ double var = a; \ if (b cmp var) var = b; \ if (c cmp var) var = c #define BOUNDED(v, min, max) while (v < min) v += max;while (v >= max) v -= max #define CALC_HS(h, s, max, delta, r, g, b, satscale) \ /* set up a greyscale if rgb values are identical */ \ /* Note: automatically includes max = 0 */ \ if (delta <= 0.0) { \ h = s = 0; \ return; \ } \ s = delta / satscale; \ h = (r == max) ? (g - b) / delta : \ (g == max) ? 2 + (b - r) / delta : \ 4 + (r - g) / delta; \ h *= 60.0; \ BOUNDED(h, 0, 360) double _rad2deg( double rad ) { return 180.0 * rad / M_PI; } double _deg2rad( double deg ) { return deg * (M_PI / 180.0); } void _mult_v3_m33( struct pixel *p, double *m0, double *m1, double *m2, double *result ) { result[0] = p->a * m0[0] + p->b * m1[0] + p->c * m2[0]; result[1] = p->a * m0[1] + p->b * m1[1] + p->c * m2[1]; result[2] = p->a * m0[2] + p->b * m1[2] + p->c * m2[2]; } /* ~~~~~~~~~~:> */ double rgb_quant( double p, double q, double h ) { BOUNDED(h, 0, 360); if (h < 60) { return p + (q-p)*h/60; } else if (h < 180) { return q; } else if (h < 240) { return p + (q-p)*(240-h)/60; } else { return p; } } void rgb2cmyk( double *rgb, double *cmyk ) { struct pixel cmy = { 1.0-rgb[0], 1.0-rgb[1], 1.0-rgb[2] }; EXTREME(k, cmy.a, cmy.b, cmy.c, <); cmyk[0] = cmy.a - k; cmyk[1] = cmy.b - k; cmyk[2] = cmy.c - k; cmyk[3] = k; } void cmyk2rgb( double *cmyk, double *rgb ) { double k = cmyk[3]; rgb[0] = 1.0 - cmyk[0] - k; rgb[1] = 1.0 - cmyk[1] - k; rgb[2] = 1.0 - cmyk[2] - k; } void hsl2rgb( double *hsl, double *rgb ) { double h = hsl[0]; double s = hsl[1]; double l = hsl[2]; double p, q; if ( l <= 0.5) { p = l*(1 - s); q = 2*l - p; } else { q = l + s - (l*s); p = 2*l - q; } rgb[0] = rgb_quant(p, q, h+120); rgb[1] = rgb_quant(p, q, h); rgb[2] = rgb_quant(p, q, h-120); } void rgb2hsl( double *rgb, double *hsl ) { double r = rgb[0]; double g = rgb[1]; double b = rgb[2]; /* compute the min and max */ EXTREME(max, r, g, b, >); EXTREME(min, r, g, b, <); double delta = max - min; double sum = max + min; /* luminance */ hsl[2] = sum / 2.0; CALC_HS(hsl[0], hsl[1], max, delta, r, g, b, (hsl[2] <= 0.5 ? sum : (2.0 - sum))); } void rgb2hsv( double *rgb, double *hsv ) { double r = rgb[0]; double g = rgb[1]; double b = rgb[2]; /* compute the min and max */ EXTREME(max, r, g, b, >); EXTREME(min, r, g, b, <); /* got V */ hsv[2] = max; double delta = max - min; CALC_HS(hsv[0], hsv[1], max, delta, r, g, b, max); } void hsv2rgb( double *hsv, double *rgb ) { double h = hsv[0]; double s = hsv[1]; double v = hsv[2]; h /= 60.0; double i = floor( h ); double f = h - i; double p = v * (1 - s); double q = v * (1 - s * f); double t = v * (1 - s * (1 - f)); switch( (int) i ) { case 0: rgb[0] = v; rgb[1] = t; rgb[2] = p; break; case 1: rgb[0] = q; rgb[1] = v; rgb[2] = p; break; case 2: rgb[0] = p; rgb[1] = v; rgb[2] = t; break; case 3: rgb[0] = p; rgb[1] = q; rgb[2] = v; break; case 4: rgb[0] = t; rgb[1] = p; rgb[2] = v; break; default: rgb[0] = v; rgb[1] = p; rgb[2] = q; break; } } void rgb2xyz( double *rgb, double gamma, double *m0, double *m1, double *m2, double *xyz ) { rgb2linear(rgb, gamma, xyz); struct pixel p = { xyz[0], xyz[1], xyz[2] }; _mult_v3_m33( &p, m0, m1, m2, xyz ); } void rgb2linear( double *rgb, double gamma, double *out ) { int i; if (gamma < 0) { /* special case for sRGB gamma curve */ for (i = 0; i < 3; i++) out[i] = fabs(rgb[i]) <= 0.04045 ? rgb[i] / 12.92 : _apow( (rgb[i] + 0.055)/1.055, 2.4 ); } else if (gamma == 1.0) { /* copy if different locations */ if (rgb != out) for (i = 0; i < 3; i++) out[i] = rgb[i]; } else { for (i = 0; i < 3; i++) out[i] = _apow(rgb[i], gamma); } } void xyz2rgb( double *xyz, double gamma, double *m0, double *m1, double *m2, double *rgb ) { struct pixel p = { xyz[0], xyz[1], xyz[2] }; _mult_v3_m33( &p, m0, m1, m2, rgb ); rgb2gamma(rgb, gamma, rgb); } void rgb2gamma( double *rgb, double gamma, double *out ) { int i; if (gamma < 0) { /* special case for sRGB gamma curve */ for (i = 0; i < 3; i++) out[i] = (fabs(rgb[i]) <= 0.0031308) ? 12.92 * rgb[i] : 1.055 * _apow(rgb[i], 1.0/2.4) - 0.055; } else if (gamma == 1.0) { /* copy if different locations */ if (rgb != out) for (i = 0; i < 3; i++) out[i] = rgb[i]; } else { for (i = 0; i < 3; i++) out[i] = _apow(rgb[i], 1.0 / gamma); } } void xyY2xyz( double *xyY, double *xyz ) { if ( xyY[1] == 0.0 ) { xyz[0] = xyz[1] = xyz[2] = 0.0; return; } xyz[0] = xyY[0] / xyY[1]; xyz[1] = 1.0; xyz[2] = (1.0 - xyY[0] - xyY[1]) / xyY[1]; } void xyz2lab( double *xyz, double *w, double *lab ) { double xr = xyz[0] / w[0]; double yr = xyz[1] / w[1]; double zr = xyz[2] / w[2]; double fx = (xr > epsilon)? pow(xr, 1.0/3.0) : (kappa * xr + 16.0) / 116.0; double fy = (yr > epsilon)? pow(yr, 1.0/3.0) : (kappa * yr + 16.0) / 116.0; double fz = (zr > epsilon)? pow(zr, 1.0/3.0) : (kappa * zr + 16.0) / 116.0; lab[0] = 116.0 * fy - 16.0; lab[1] = 500.0 * (fx - fy); lab[2] = 200.0 * (fy - fz); } void lab2lch( double *lab, double *lch ) { lch[0] = lab[0]; lch[1] = sqrt( pow(lab[1], 2) + pow(lab[2], 2) ); lch[2] = _rad2deg( atan2( lab[2], lab[1] ) ); BOUNDED(lch[2], 0.0, 360.0); } void lch2lab( double *lch, double *lab ) { /* l is set */ lab[0] = lch[0]; double c = lch[1]; double h = _deg2rad( lch[2] ); double th = tan(h); double *a = lab+1; double *b = lab+2; *a = c / sqrt( pow(th,2) + 1 ); *b = sqrt( pow(c, 2) - pow(*a, 2) ); if (h < 0.0) h += 2*M_PI; if (h > M_PI/2 && h < M_PI*3/2) *a = -*a; if (h > M_PI) *b = -*b; } void lab2xyz( double *lab, double *w, double *xyz ) { double yr = (lab[0] > kappa * epsilon) ? pow( (lab[0] + 16.0)/116.0, 3 ) : lab[0] / kappa; double fy = (yr > epsilon) ? (lab[0] + 16.0)/116.0 : (kappa * yr + 16.0)/116.0; double fx = fy + lab[1] / 500.0; double fz = fy - lab[2] / 200.0; double xr = (pow(fx, 3) > epsilon) ? pow(fx, 3) : (fx * 116.0 - 16.0) / kappa; double zr = (pow(fz, 3) > epsilon) ? pow(fz, 3) : (fz * 116.0 - 16.0) / kappa; xyz[0] = xr * w[0]; xyz[1] = yr * w[1]; xyz[2] = zr * w[2]; } PDL-Graphics-ColorSpace-0.206/README0000644000175000017500000000302614202367673016547 0ustar osboxesosboxesPDL-Graphics-ColorSpace Does color space conversions such as RGB to XYZ and Lab to LCH. Derived from Graphics::ColorObject but since it's implemented in C and PDL, it runs *much* faster. DEPENDENCIES PDL INSTALLATION To install this module, run the following commands: perl Makefile.PL make make test make install SUPPORT AND DOCUMENTATION After installing, you can find documentation for this module with the perldoc command. perldoc PDL::Graphics::ColorSpace Please email author for questions regarding color conversions in this module. Please email PDL mailing list for general questions regarding PDL http://pdl.perl.org/?page=mailing-lists Please file bugs or requests at https://github.com/maggiexyz/PDL-Graphics-ColorSpace You can also look for information at: RT, CPAN's request tracker (report bugs here) http://rt.cpan.org/NoAuth/Bugs.html?Dist=PDL-Graphics-ColorSpace AnnoCPAN, Annotated CPAN documentation http://annocpan.org/dist/PDL-Graphics-ColorSpace CPAN Ratings http://cpanratings.perl.org/d/PDL-Graphics-ColorSpace Search CPAN http://search.cpan.org/dist/PDL-Graphics-ColorSpace/ LICENSE AND COPYRIGHT ~~~~~~~~~~~~ ~~~~~ ~~~~~~~~ ~~~~~ ~~~ `` ><((("> Copyright (C) 2012 Maggie J. Xiong Original work sponsored by Shutterstock, LLC L All rights reserved. There is no warranty. You are allowed to redistribute this software / documentation as described in the file COPYING in the PDL distribution. PDL-Graphics-ColorSpace-0.206/MANIFEST0000644000175000017500000000061614743227146017022 0ustar osboxesosboxesMANIFEST Changes README Makefile.PL ColorSpace/RGBSpace.pm color_space.pd color_space.c color_space.h t/color_space.t META.yml Module YAML meta-data (added by MakeMaker) META.json Module JSON meta-data (added by MakeMaker) GENERATED/PDL/Graphics/ColorSpace.pm mod=PDL::Graphics::ColorSpace pd=color_space.pd (added by pdlpp_mkgen) PDL-Graphics-ColorSpace-0.206/Makefile.PL0000644000175000017500000000237214743217247017645 0ustar osboxesosboxesuse strict; use warnings; use PDL::Core::Dev; use ExtUtils::MakeMaker; my $pack = [qw(color_space.pd ColorSpace PDL::Graphics::ColorSpace), undef, 1]; my %hash = pdlpp_stdargs($pack); $hash{OBJECT} .= ' color_space$(OBJ_EXT)'; $hash{clean}{FILES} .= ' color_space$(OBJ_EXT)'; WriteMakefile( %hash, AUTHOR => 'Maggie J. Xiong ', LICENSE=> 'perl', PM => { 'ColorSpace.pm' => '$(INST_LIBDIR)/ColorSpace.pm', 'ColorSpace/RGBSpace.pm' => '$(INST_LIBDIR)/ColorSpace/RGBSpace.pm', }, CONFIGURE_REQUIRES => { 'PDL' => '2.094', # Test::PDL }, TEST_REQUIRES => { 'Test::More' => '0.88', }, PREREQ_PM => { 'PDL' => '2.094', }, META_MERGE => { "meta-spec" => { version => 2 }, resources => { bugtracker => {web=>'https://github.com/PDLPorters/PDL-Graphics-ColorSpace/issues'}, repository => { url => 'git://github.com/PDLPorters/PDL-Graphics-ColorSpace.git', type => 'git', web => 'https://github.com/PDLPorters/PDL-Graphics-ColorSpace', }, x_IRC => 'irc://irc.perl.org/#pdl', }, }, ); sub MY::postamble {pdlpp_postamble($pack)};