#!/usr/bin/perl # # Copyright (c) 1997-2024 Robert Munafo # License: Creative Commons Attribution-NonCommercial 4.0 International # Source: http://mrob.com/pub/perl/nearest-color-name.txt # $bapropos = q@ nearest-color-name(1r) -- Give a name for an RGB colour based on xkcd survey @; $setup_instructions = q` Getting the colour data on a new computer: cd cd xkcd/color-survey curl 'https://xkcd.com/color/rgb.txt' > rgb-954.txt mkdir z-tars cd !$ # Go to http://blog.xkcd.com/2010/05/03/color-survey-results # Find the link to "the raw data as an SQLite dump file" and download it # (Probably http://xkcd.com/color/colorsurvey.tar.gz ) # Put the tarfile is in the z-tars directory gunzip colorsurvey.tar.gz tar xvf colorsurvey.tar # You should now have "mainsurvey_sqldump.txt" and "satfaces_sqldump.txt" cd .. nearest-color-name -gd ls -l sorted-rgb-954.txt"; `; $hd = $ENV{"HOME"}; # require "$hd/bin/rpmlib.pl"; # Set xkcd_dir to a path that contains xkcd-related stuff $xkcd_dir = "$hd/xkcd"; die "There is no directory '$xkcd_dir'!\n" if (!(-d $xkcd_dir)); # Set data_dir to the path of a directory that this script can use # to put the uncompressed, pre-processed versions of the colour data # files $data_dir = "$xkcd_dir/color-survey"; die "There is no directory '$data_dir'!\n" if (!(-d $data_dir)); # Go into that directory and download "http://xkcd.com/color/rgb.txt" # saving it with the name "rgb-954.txt"; $rgb_954_file = "$data_dir/rgb-954.txt"; # Set data_tar_dir to the path of a directory that will contain the file # "colorsurvey.tar.gz" $data_tar_dir = "$data_dir/z-tars"; # Go to http://blog.xkcd.com/2010/05/03/color-survey-results # Find the link to "the raw data as an SQLite dump file" and download it # (Probably http://xkcd.com/color/colorsurvey.tar.gz ) # Make sure that tarfile is in the data_tar_dir defined above. # Uncompress the tar.gz file # You should now have "mainsurvey_sqldump.txt" and "satfaces_sqldump.txt" # Leave those files in the data_tar_dir # Run the script with the -gd option, and if you have everything set up # as described here, it will generate the file "sorted-rgb-954.txt" $sorted_rgb_file = "$data_dir/sorted-rgb-954.txt"; # Now try the script by typing "nearest-color-name --help" then try some # of the examples, like "nearest-color-name #acc2d9" # Here is a full example, done in a bash shell using the 'curl', 'gunzup' # and 'tar' tools: # # mkdir clr-temp # cd clr-temp # curl 'http://mrob.com/pub/perl/nearest-color-name.txt' > ncn.pl # chmod +x ncn.pl # curl 'http://xkcd.com/color/rgb.txt' > rgb-954.txt # mkdir tars # cd tars # curl 'http://xkcd.com/color/colorsurvey.tar.gz' > colorsurvey.tar.gz # # 1 file, 83.5 MiB # gunzip colorsurvey.tar.gz # # file is now 'colorsurvey.tar', 454.2 MiB # tar xvf colorsurvey.tar # # 2 files 'mainsurvey_sqldump.txt' and 'satfaces_sqldump.txt' # cd .. # # Now edit "ncn.pl" so it says: # # $xkcd_dir = "$hd/clr-temp"; # # $data_dir = "$xkcd_dir"; # # $data_tar_dir = "$data_dir/tars"; # ./ncn.pl -gd # ./ncn.pl acc2d9 $help = qq` NAME nearest-color-name - Give a name for an RGB colour based on xkcd survey DESCRIPTION Supply a colour in one of the forms shown below, and it will search for a matching colour name using the xkcd colour survey results in $sorted_rgb_file OPTIONS -gd Generate data file 'sorted-rgb-954.txt'. This is only done once; read the block header comment of this script for full instructions. #acc2d9 A hex-encoded RGB triple with values in the range [00..ff]. Hex digits may be uppercase. #AC5 A hex-encoded RGB triple with values in the range [0..f]. This will be expanded into the range [00..ff] by multiplying each component by 0x11. acc2d9 You may leave off the '#' for convenience, as it's a comment delimiter in most shells. -d #e00 Give the distance (by whatever metric is chosen) to some other colour, and then just exit. -la Just list all the colours and their RGB values -lab Measure distance in CIE L*a*b* space by just turning colors into L*a*b* coordinates and measuring the Eucliedean distance in those coordinates. This is sometimes called the "CIE76 color-difference formula". -t Terse: show only the closest colour-name matched. (default) -v Verbose: show approximations in descending order of popularity. The most-popular, but least-closely-matching, colour names will be shown first. LICENSE This script is provided under the same license as most of the rest of mrob.com, see mrob.com/cc-license.html for details. `; my $unused_block_header = q` REVISION HISTORY 20131213 First version. 20150208 Make -v the default. 20150217 Make script easier to use by other people. 20150406 Add -lab option, &dist2, and supporting conversion functions 20150409 Fix several bugs in rgb_xyz and xyz_lab; -lab option now seems to work fairly well. 20150603 Add -d option. 20180302 Better instructions for setting up the data files. 20220719 Add -la option BUGS and TTD Given a set of regexes, return colours whose name matches all the regexes Given a colour and a distance, search for a colour name that is exactly that distance from some colour that is "reasonbly close" to the one given. {Algorithm: Given target T and distance d: Find colours close to the given point; sort by how far they are from the given point; For each colour C find the point D such that T, D and C are colinear and distance(D,T)=d; output the two colours for which the value of distance(C,T) is closest to d.} `; $| = 1; $hdig = "[0-9a-fA-F]"; # Hexadecimal digit $hd2 = "$hdig$hdig"; # 2 hex digits sub parse3 { my($s) = @_; if ($s =~ m/^#?($hd2)($hd2)($hd2)$/) { # Something like #170bff or FF00BF $g_r = hex($1); $g_g = hex($2); $g_b = hex($3); return 1; } elsif ($arg =~ m/^#?($hdig)($hdig)($hdig)$/) { # Something like #047 or 3c9 $g_r = hex($1) * 0x11; $g_g = hex($2) * 0x11; $g_b = hex($3) * 0x11; return 1; } return 0; } $pnum = "[0-9]+"; # decimal digit $pname = "[- '/a-z0-9]+"; # colour name # Simple distance function assuming the primary axes are orthogonal, # the transformation to perceptual distance is everywhere linear, and we # can just scale the three components. sub dist1 { my($red1, $grn1, $blu1, $red2, $grn2, $blu2) = @_; my($r, $g, $b, $dist); $r = ($red1 - $red2) * 0.6; $g = $grn1 - $grn2; $b = ($blu1 - $blu2) * 0.3; $dist = sqrt($r*$r + $g*$g + $b*$b); return $dist; } # From en.wikipedia.org/wiki/SRGB: # Convert sRGB to linear RGB: # a = 0.055 # R_lin = R/12.92 (if R <= 0.04045) # ((R+a)/(1+a))^2.4 (if R > 0.04045) # (Same for G_lin and B_lin) sub rgb_linearize { my($r, $g, $b) = @_; my($lr, $lg, $lb); my($a) = 0.055; $lr = ($r <= 0.04045) ? ($r/12.92) : ((($r+$a)/(1.0+$a)) ** 2.4); $lg = ($g <= 0.04045) ? ($g/12.92) : ((($g+$a)/(1.0+$a)) ** 2.4); $lb = ($b <= 0.04045) ? ($b/12.92) : ((($b+$a)/(1.0+$a)) ** 2.4); return(($lr, $lg, $lb)); } # From en.wikipedia.org/wiki/SRGB: # Convert sRGB to CIE XYZ using reference white D65: # [ X ] [0.4124 0.3576 0.1805] [R_lin] # [ Y ] = [0.2126 0.7152 0.0722] [G_lin] # [ Z ] [0.0193 0.1192 0.9505] [B_lin] # 6-digit precision from www.cs.rit.edu/~ncs/color/t_convert.html # Or from www.easyrgb.com/index.php?X=MATH&H=02 : # var_R = ( R / 255 ) //R from 0 to 255 # var_G = ( G / 255 ) //G from 0 to 255 # var_B = ( B / 255 ) //B from 0 to 255 # // gamma-correct # if ( var_R > 0.04045 ) var_R = ( ( var_R + 0.055 ) / 1.055 ) ^ 2.4 # else var_R = var_R / 12.92 # if ( var_G > 0.04045 ) var_G = ( ( var_G + 0.055 ) / 1.055 ) ^ 2.4 # else var_G = var_G / 12.92 # if ( var_B > 0.04045 ) var_B = ( ( var_B + 0.055 ) / 1.055 ) ^ 2.4 # else var_B = var_B / 12.92 # // scale for XYZ # var_R = var_R * 100 # var_G = var_G * 100 # var_B = var_B * 100 # //Observer. = 2°, Illuminant = D65 # X = var_R * 0.4124 + var_G * 0.3576 + var_B * 0.1805 # Y = var_R * 0.2126 + var_G * 0.7152 + var_B * 0.0722 # Z = var_R * 0.0193 + var_G * 0.1192 + var_B * 0.9505 sub rgb_xyz { my($r, $g, $b) = @_; my($x, $y, $z); $r *= 100; $g *= 100; $b *= 100; $x = 0.412453*$r + 0.357580*$g + 0.180423*$b; $y = 0.212671*$r + 0.715160*$g + 0.072169*$b; $z = 0.019334*$r + 0.119193*$g + 0.950227*$b; return (($x, $y, $z)); } # From en.wikipedia.org/wiki/SRGB: # From CIE XYZ to CIE L* a* b* # f(t) = { t^(1/3) if t>(6/29)^3 # { (1/3)*(29/6)^2*t + 4/29 if t <= (6/29)^3 # Xn = Yn = Zn = 1/3 # L* = 116*f(Y/Yn) - 16 # a* = 500(f(X/Xn)-f(Y/Yn)) # b* = 200(f(Y/Yn)-f(Z/Zn)) # Or acto www.cs.rit.edu/~ncs/color/t_convert.html # L* = 116 * (Y/Yn)1/3 - 16 { for Y/Yn > 0.008856 # L* = 903.3 * Y/Yn { otherwise # a* = 500(f(X/Xn)-f(Y/Yn)) # b* = 200(f(Y/Yn)-f(Z/Zn)) # Or from http://www.easyrgb.com/index.php?X=MATH&H=07 # var_X = X / ref_X // ref_X = 95.047 Observer= 2°, Illuminant= D65 # var_Y = Y / ref_Y // ref_Y = 100.000 # var_Z = Z / ref_Z // ref_Z = 108.883 # // Fo the f(n) functions # if ( var_X > 0.008856 ) var_X = var_X ^ ( 1/3 ) # else var_X = ( 7.787 * var_X ) + ( 16 / 116 ) # if ( var_Y > 0.008856 ) var_Y = var_Y ^ ( 1/3 ) # else var_Y = ( 7.787 * var_Y ) + ( 16 / 116 ) # if ( var_Z > 0.008856 ) var_Z = var_Z ^ ( 1/3 ) # else var_Z = ( 7.787 * var_Z ) + ( 16 / 116 ) # // finish the L*a*b* conversion # CIE-L* = ( 116 * var_Y ) - 16 # CIE-a* = 500 * ( var_X - var_Y ) # CIE-b* = 200 * ( var_Y - var_Z ) sub cief { my($t) = @_; my($k6293) = 0.008856451679; # (6/29)^3 my($k132962) = 7.787037037037; # (1/3)*(29/6)^2 my($k429) = 0.1379310344827; # 4/29 if ($t > $k6293) { return ($t ** (1/3.0)); } # else return($k132962*$t + $k429); } sub xyz_lab { my($x, $y, $z) = @_; my($L, $a, $b); $x /= 95.047; $y /= 100.000; $z /= 108.883; $L = 116.0 * &cief($y) - 16.0; $a = 500.0 * (&cief($x) - &cief($y)); $b = 200.0 * (&cief($y) - &cief($z)); return(($L, $a, $b)); } # Convert sRGB values on the scale 0..255 to L*a*b*. This combines the three # steps implemented in the above functions rgb_linearize, rgb_xyz and xyz_lab # We scale the input from 0..255 to 0.0..1.0; we have to scale the output as # well because otherwise the numbers come out way too large. sub rgb255_lab { my($red, $grn, $blu) = @_; my($lr, $lg, $lb); my($x, $y, $z); my($L, $a, $b); $red /= 255.0; $grn /= 255.0; $blu /= 255.0; # print "rgb255_lab($red, $grn, $blu)\n"; ($lr, $lg, $lb) = &rgb_linearize($red, $grn, $blu); # print "(lr,lg,lb) = ($lr, $lg, $lb)\n"; ($x, $y, $z) = &rgb_xyz($lr, $lg, $lb); # print "(x,y,z) = ($x, $y, $z)\n"; ($L, $a, $b) = &xyz_lab($x, $y, $z); # print "(L,a,b) = ($L, $a, $b)\n"; return (($L, $a, $b)); } # &rgb255_lab(255,0,191); exit(0); # From stackoverflow.com/questions/1313/followup-finding-an-accurate-distance-between-colors # Now that we are in L*a*b* space we can get a good approximation just using # a pythagorean distance: # # distance = sqrt((L_1-L_2)^2 + (a_1-a_2)^2 + (b_1-b_2)^2) sub dist2 { my($red1, $grn1, $blu1, $red2, $grn2, $blu2) = @_; my($L1, $a1, $b1, $L2, $a2, $b2); my($L, $a, $b, $dist); ($L1, $a1, $b1) = &rgb255_lab($red1, $grn1, $blu1); ($L2, $a2, $b2) = &rgb255_lab($red2, $grn2, $blu2); # print "(L1,a1,b1) = ($L1, $a1, $b1); (L2,a2,b2) = ($L2, $a2, $b2)"; $L = $L1 - $L2; $a = $a1 - $a2; $b = $b1 - $b2; $dist = sqrt($L*$L + $a*$a + $b*$b); # print "-> dist = $dist\n"; return $dist; } # From stackoverflow.com/questions/1313/followup-finding-an-accurate-distance-between-colors # Or go all-out and use Sharma et al. CIECD2000 (see paper: # "Sharma 2000 Color Difference Formula.pdf" and spreadsheet: # "Sharma 2000 Color Difference spreadsheet.xls" ) # # #define REF_X 95.047; // Observer= 2°, Illuminant= D65 # #define REF_Y 100.000; # #define REF_Z 108.883; # # void bgr2xyz( const Vec3b& BGR, Vec3d& XYZ ) # { # double r = (double)BGR[2] / 255.0; # double g = (double)BGR[1] / 255.0; # double b = (double)BGR[0] / 255.0; # if( r > 0.04045 ) # r = pow( ( r + 0.055 ) / 1.055, 2.4 ); # else # r = r / 12.92; # if( g > 0.04045 ) # g = pow( ( g + 0.055 ) / 1.055, 2.4 ); # else # g = g / 12.92; # if( b > 0.04045 ) # b = pow( ( b + 0.055 ) / 1.055, 2.4 ); # else # b = b / 12.92; # r *= 100.0; # g *= 100.0; # b *= 100.0; # XYZ[0] = r * 0.4124 + g * 0.3576 + b * 0.1805; # XYZ[1] = r * 0.2126 + g * 0.7152 + b * 0.0722; # XYZ[2] = r * 0.0193 + g * 0.1192 + b * 0.9505; # } # # void xyz2lab( const Vec3d& XYZ, Vec3d& Lab ) # { # double x = XYZ[0] / REF_X; # double y = XYZ[1] / REF_X; # double z = XYZ[2] / REF_X; # if( x > 0.008856 ) # x = pow( x , .3333333333 ); # else # x = ( 7.787 * x ) + ( 16.0 / 116.0 ); # if( y > 0.008856 ) # y = pow( y , .3333333333 ); # else # y = ( 7.787 * y ) + ( 16.0 / 116.0 ); # if( z > 0.008856 ) # z = pow( z , .3333333333 ); # else # z = ( 7.787 * z ) + ( 16.0 / 116.0 ); # Lab[0] = ( 116.0 * y ) - 16.0; # Lab[1] = 500.0 * ( x - y ); # Lab[2] = 200.0 * ( y - z ); # } # # void lab2lch( const Vec3d& Lab, Vec3d& LCH ) # { # LCH[0] = Lab[0]; # LCH[1] = sqrt( ( Lab[1] * Lab[1] ) + ( Lab[2] * Lab[2] ) ); # LCH[2] = atan2( Lab[2], Lab[1] ); # } # # double deltaE2000( const Vec3b& bgr1, const Vec3b& bgr2 ) # { # Vec3d xyz1, xyz2, lab1, lab2, lch1, lch2; # bgr2xyz( bgr1, xyz1 ); # bgr2xyz( bgr2, xyz2 ); # xyz2lab( xyz1, lab1 ); # xyz2lab( xyz2, lab2 ); # lab2lch( lab1, lch1 ); # lab2lch( lab2, lch2 ); # return deltaE2000( lch1, lch2 ); # } # # double deltaE2000( const Vec3d& lch1, const Vec3d& lch2 ) # { # double avg_L = ( lch1[0] + lch2[0] ) * 0.5; # double delta_L = lch2[0] - lch1[0]; # double avg_C = ( lch1[1] + lch2[1] ) * 0.5; # double delta_C = lch1[1] - lch2[1]; # double avg_H = ( lch1[2] + lch2[2] ) * 0.5; # if( fabs( lch1[2] - lch2[2] ) > CV_PI ) # avg_H += CV_PI; # double delta_H = lch2[2] - lch1[2]; # if( fabs( delta_H ) > CV_PI ) # { # if( lch2[2] <= lch1[2] ) # delta_H += CV_PI * 2.0; # else # delta_H -= CV_PI * 2.0; # } # delta_H = sqrt( lch1[1] * lch2[1] ) * sin( delta_H ) * 2.0; # double T = 1.0 - # 0.17 * cos( avg_H - CV_PI / 6.0 ) + # 0.24 * cos( avg_H * 2.0 ) + # 0.32 * cos( avg_H * 3.0 + CV_PI / 30.0 ) - # 0.20 * cos( avg_H * 4.0 - CV_PI * 7.0 / 20.0 ); # double SL = avg_L - 50.0; # SL *= SL; # SL = SL * 0.015 / sqrt( SL + 20.0 ) + 1.0; # double SC = avg_C * 0.045 + 1.0; # double SH = avg_C * T * 0.015 + 1.0; # double delta_Theta = avg_H / 25.0 - CV_PI * 11.0 / 180.0; # delta_Theta = exp( delta_Theta * -delta_Theta ) * ( CV_PI / 6.0 ); # double RT = pow( avg_C, 7.0 ); # RT = sqrt( RT / ( RT + 6103515625.0 ) ) * sin( delta_Theta ) * -2.0; // 6103515625 = 25^7 # delta_L /= SL; # delta_C /= SC; # delta_H /= SH; # return sqrt( delta_L * delta_L + delta_C * delta_C + delta_H * delta_H + RT * delta_C * delta_H ); # } # # See also: # en.wikipedia.org/wiki/Color_difference # en.wikipedia.org/wiki/Standard_illuminant # en.wikipedia.org/wiki/Lab_color_space # en.wikipedia.org/wiki/CIE_1931_color_space # en.wikipedia.org/wiki/RGB_color_space # en.wikipedia.org/wiki/SRGB # en.wikipedia.org/wiki/Gamma_correction # www.ece.rochester.edu/~gsharma/ciede2000/ sub dist { my($red1, $grn1, $blu1, $red2, $grn2, $blu2) = @_; if ($g_lab) { # Measure distance in CIE L*a*b* space return &dist2($red1, $grn1, $blu1, $red2, $grn2, $blu2); } return &dist1($red1, $grn1, $blu1, $red2, $grn2, $blu2); } sub scan1 { my($listall) = @_; my($l, $pop, $name, $col6, $r, $g, $b, $dist, $bdis, $bname, $bcol); my(@best6); if(!(-f $sorted_rgb_file)) { die "No data file '$sorted_rgb_file'; generate with -gd option.\n"; } $bdis = 9.9e99; $bname = 'no match'; open(my $IN, $sorted_rgb_file); while ($l = <$IN>) { chomp $l; # Data lines look like: # 317 yellow/green #c8fd3d if ($l =~ m|^($pnum)\t($pname)\t(\#$hd2$hd2$hd2)|) { $pop = $1; $name = $2; $col6 = $3; $defined_names{$name} = 1; $name_rgb{$name} = $col6; &parse3($col6); $dist = &dist($g_r, $g_g, $g_b, $g_target_r, $g_target_g, $g_target_b); if ($listall) { print sprintf(" %27s %6d %s\n", $name, $pop, $col6); } elsif ($dist < $bdis) { $bdis = $dist; $bname = $name; $bcol = $col6; push(@best6, sprintf( " ... matches \"%s\" (%s) to within %5.2f (pop %d)\n", $bname, $bcol, $bdis, $pop)); if ($#best6 >= 6) { shift @best6; } } } else { print "scan1: parse error: '$l'\n"; } } close $IN; if(!($listall)) { if ($g_verbose) { foreach $l (@best6) { print $l; } } print ("The closest match to ($g_target_r, $g_target_g, $g_target_b)" . " is \"$bname\" ($bcol)\n"); } } # End of scan.1 # Read one of the raw data files (used by scan.3) sub scan2 { my($fname) = @_; my($l, $nm); print "scan2 $fname\n"; open(my $IN, $fname); while($l = <$IN>) { chomp $l; $g_nl++; $gi++; if ($gi >= 10000) { print " ... $g_nl lines so far... \r"; $gi = 0; } if(!($l =~ m/\~/)) { $l =~ s/\'\'/"/g; $l =~ s/\'/~/g; $l =~ s/\"/'/g; if ($l =~ m|answers.*\~($pname)\~|) { $nm = $1; if ($defined_names{$nm}) { $nam_pop{$nm}++; } } } } close $IN; } # End of scan.2 # Read the defined colour names from a file of the same format as # http://xkcd.com/color/rgb.txt sub scan4 { my($pn) = @_; my($l, $pop, $name, $col6, $r, $g, $b, $dist, $bdis, $bname, $bcol); if(!(-f $pn)) { die "No file '$pn'; read instructions in block header comment.\n"; } open(my $IN, $pn); while ($l = <$IN>) { chomp $l; # Data lines look like: # yellow/green #c8fd3d if ($l =~ m|^($pname)\t(\#$hd2$hd2$hd2)|) { $name = $1; $col6 = $2; $defined_names{$name} = 1; $name_rgb{$name} = $col6; $n_num_colors++; } elsif ($l =~ m/License.+creativecommons.org/) { # Ignore this } else { print "scan4: parse error: '$l'\n"; } } close $IN; } # End of scan.4 # scan3 discovers the 'population' of each colour name. The colour names # are read from $rgb_954_file, then it reads through the huge raw data files # to count how many times each name appears in the raw results. As desribed # above, the raw data files are from xkcd.com/color/colorsurvey.tar.gz sub scan3 { my($d1, $k, $l); $d1 = $data_tar_dir; &scan4($rgb_954_file); if ($n_num_colors < 900) { die "File '$rgb_954_file' does not seem to contain enough colours.\n"; } print "Scanning...\n"; &scan2("$d1/mainsurvey_sqldump.txt"); &scan2("$d1/satfaces_sqldump.txt"); open(my $OUT, "> $sorted_rgb_file"); foreach $k (sort {$nam_pop{$b} <=> $nam_pop{$a}} (keys %defined_names)) { $l = "$nam_pop{$k}\t$k\t$name_rgb{$k}"; print $OUT "$l\n"; } close $OUT; } # End of scan.3 $| = 1; # Set defaults and parse arguments $g_dist2 = ''; $g_listall = 0; $g_target_r = 0; $g_target_g = 0; $g_target_b = 0; $g_verbose = 1; $g_lab = 0; while($arg = shift) { if ($arg =~ m/^-[-]?h(elp)?$/) { print $help; exit(0); } elsif ($arg eq '-d') { # Give the distance to some other colour. $g_dist2 = shift; if (&parse3($g_dist2)) { $g_d2_r = $g_r; $g_d2_g = $g_g; $g_d2_b = $g_b; } else { print STDERR "'$g_dist2' is not the right format for an RGB colour.\n"; exit(-1); } } elsif ($arg eq '-gd') { $g_generate_data = 1; } elsif ($arg eq '-la') { $g_listall = 1; } elsif ($arg eq '-lab') { $g_lab = 1; } elsif ($arg eq '-t') { $g_verbose = 0; } elsif ($arg eq '-v') { $g_verbose = 1; } elsif (&parse3($arg)) { $g_target_r = $g_r; $g_target_g = $g_g; $g_target_b = $g_b; } else { print STDERR "Unrecognized argument '$arg'\n"; exit(-1); } } if ($g_dist2 ne '') { if ($g_lab) { print "Using the CIE76 color-difference metric, the"; } else { print "the"; } print " distance from: ($g_target_r, $g_target_g, $g_target_b)"; print " to: ($g_d2_r, $g_d2_g, $g_d2_b)"; if ($g_lab) { # Measure distance in CIE L*a*b* space $d = &dist2($g_target_r, $g_target_g, $g_target_b, $g_d2_r, $g_d2_g, $g_d2_b); } else { $d = &dist1($g_target_r, $g_target_g, $g_target_b, $g_d2_r, $g_d2_g, $g_d2_b); } print sprintf(" is %5.2f\n", $d); exit(0); } if ($g_generate_data) { &scan3(); exit(0); } # Do requested action(s) if ($g_verbose) { print "RGB values: ($g_target_r, $g_target_g, $g_target_b)\n"; if ($g_lab) { ($L2, $a2, $b2) = &rgb255_lab($g_target_r, $g_target_g, $g_target_b); print "(Using CIE L*a*b* coordinates to compute distances)\n"; print sprintf("L*a*b* values: (%.2f, %.2f, %.2f)\n", $L2, $a2, $b2); } } &scan1($g_listall);