PHP 中的 RGB 到 HSV

问题描述 投票:0回答:6

在 PHP 中,将 RGB 三元组转换为 HSV 值的最直接方法是什么?

php image colors rgb hsv
6个回答
35
投票

这是一个简单、直接的方法,它以度数和百分比的形式返回 HSV 值,这就是 Photoshop 的颜色选择器所使用的。

请注意,返回值不会四舍五入,如果需要,您可以自行四舍五入。请记住

H(360) == H(0)
,因此
H
及更大值的
359.5
值应四舍五入为
0

大量记录用于学习目的。

/**
 * Licensed under the terms of the BSD License.
 * (Basically, this means you can do whatever you like with it,
 *   but if you just copy and paste my code into your app, you
 *   should give me a shout-out/credit :)
 */

<?php

function RGBtoHSV($R, $G, $B)    // RGB values:    0-255, 0-255, 0-255
{                                // HSV values:    0-360, 0-100, 0-100
    // Convert the RGB byte-values to percentages
    $R = ($R / 255);
    $G = ($G / 255);
    $B = ($B / 255);

    // Calculate a few basic values, the maximum value of R,G,B, the
    //   minimum value, and the difference of the two (chroma).
    $maxRGB = max($R, $G, $B);
    $minRGB = min($R, $G, $B);
    $chroma = $maxRGB - $minRGB;

    // Value (also called Brightness) is the easiest component to calculate,
    //   and is simply the highest value among the R,G,B components.
    // We multiply by 100 to turn the decimal into a readable percent value.
    $computedV = 100 * $maxRGB;

    // Special case if hueless (equal parts RGB make black, white, or grays)
    // Note that Hue is technically undefined when chroma is zero, as
    //   attempting to calculate it would cause division by zero (see
    //   below), so most applications simply substitute a Hue of zero.
    // Saturation will always be zero in this case, see below for details.
    if ($chroma == 0)
        return array(0, 0, $computedV);

    // Saturation is also simple to compute, and is simply the chroma
    //   over the Value (or Brightness)
    // Again, multiplied by 100 to get a percentage.
    $computedS = 100 * ($chroma / $maxRGB);

    // Calculate Hue component
    // Hue is calculated on the "chromacity plane", which is represented
    //   as a 2D hexagon, divided into six 60-degree sectors. We calculate
    //   the bisecting angle as a value 0 <= x < 6, that represents which
    //   portion of which sector the line falls on.
    if ($R == $minRGB)
        $h = 3 - (($G - $B) / $chroma);
    elseif ($B == $minRGB)
        $h = 1 - (($R - $G) / $chroma);
    else // $G == $minRGB
        $h = 5 - (($B - $R) / $chroma);

    // After we have the sector position, we multiply it by the size of
    //   each sector's arc (60 degrees) to obtain the angle in degrees.
    $computedH = 60 * $h;

    return array($computedH, $computedS, $computedV);
}

?>

17
投票
<?php
function RGB_TO_HSV ($R, $G, $B)  // RGB Values:Number 0-255
{                                 // HSV Results:Number 0-1
   $HSL = array();

   $var_R = ($R / 255);
   $var_G = ($G / 255);
   $var_B = ($B / 255);

   $var_Min = min($var_R, $var_G, $var_B);
   $var_Max = max($var_R, $var_G, $var_B);
   $del_Max = $var_Max - $var_Min;

   $V = $var_Max;

   if ($del_Max == 0)
   {
      $H = 0;
      $S = 0;
   }
   else
   {
      $S = $del_Max / $var_Max;

      $del_R = ( ( ( $var_Max - $var_R ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
      $del_G = ( ( ( $var_Max - $var_G ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;
      $del_B = ( ( ( $var_Max - $var_B ) / 6 ) + ( $del_Max / 2 ) ) / $del_Max;

      if      ($var_R == $var_Max) $H = $del_B - $del_G;
      else if ($var_G == $var_Max) $H = ( 1 / 3 ) + $del_R - $del_B;
      else if ($var_B == $var_Max) $H = ( 2 / 3 ) + $del_G - $del_R;

      if ($H<0) $H++;
      if ($H>1) $H--;
   }

   $HSL['H'] = $H;
   $HSL['S'] = $S;
   $HSL['V'] = $V;

   return $HSL;
}

2
投票

经过彻底测试和压缩,这是我将坚持使用的将 RGB 转换为 HSV 的函数:

function RGBtoHSV($r,$g,$b) {
  $r=($r/255); $g=($g/255); $b=($b/255);
  $maxRGB=max($r,$g,$b); $minRGB=min($r,$g,$b); $chroma=$maxRGB-$minRGB;
  if($chroma==0) return array('h'=>0,'s'=>0,'v'=>$maxRGB);
  if($r==$minRGB)$h=3-(($g-$b)/$chroma);
  elseif($b==$minRGB)$h=1-(($r-$g)/$chroma); else $h=5-(($b-$r)/$chroma);
  return array('h'=>60*$h,'s'=>$chroma/$maxRGB,'v'=>$maxRGB);
} 

示例:

使用颜色“DarkSalmon”的示例:

echo '<pre><code>'. print_r( RGBtoHSV(233,150,122), true ) .'</code></pre>';

...返回:

Array
(
    [h] => 15.135135135135
    [s] => 0.47639484978541
    [v] => 0.91372549019608
)

0
投票

我就是这样做的

function convertRgbToHsv($rgb)
{

    $r = (int)substr($rgb, 0, 3) / 255;
    $g = (int)substr($rgb, 3, 3) / 255;
    $b = (int)substr($rgb, 6, 3) / 255;

    $max = max($r, $g, $b);
    $min = min($r, $g, $b);
    $delta = $max - $min;

    if (!$delta) {
        $h = 0;
    } else if ($r === $max) {
        $h = 60 * ((($g - $b) / $delta) % 6);
    } else if ($g === $max) {
        $h = 60 * ((($b - $r) / $delta) + 2);
    } else {
        $h = 60 * ((($r - $g) / $delta) + 4);
    }

    $s = !!$max ? $delta / $max : 0;

    $v = $max;

    $hsv = array("h" => $h, "s" => $s, "v" => $v);

    return $hsv;
}

参考资料链接此处


0
投票

代码

function RGB_to_HSV($r, $g, $b) {
    $result = [];
    $min = min($r, $g, $b);
    $max = max($r, $g, $b);
    $delta_min_max = $max - $min;
    $result_h = 0;
    if     ($delta_min_max !== 0 && $max === $r && $g >= $b) $result_h = 60 * (($g - $b) / $delta_min_max) +   0;
    elseif ($delta_min_max !== 0 && $max === $r && $g <  $b) $result_h = 60 * (($g - $b) / $delta_min_max) + 360;
    elseif ($delta_min_max !== 0 && $max === $g            ) $result_h = 60 * (($b - $r) / $delta_min_max) + 120;
    elseif ($delta_min_max !== 0 && $max === $b            ) $result_h = 60 * (($r - $g) / $delta_min_max) + 240;
    $result_s = $max === 0 ? 0 : (1 - ($min / $max));
    $result_v = $max;
    $result['h'] = (int)(round($result_h));
    $result['s'] = (int)($result_s * 100);
    $result['v'] = (int)($result_v / 2.55);
    return $result;
}

测试

var_dump( RGB_to_HSV(  0,   0,   0)  ===  ['h' =>   0, 's' =>   0, 'v' =>   0] );
var_dump( RGB_to_HSV(  0,   0, 255)  ===  ['h' => 240, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(  0, 127, 255)  ===  ['h' => 210, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(  0, 255,   0)  ===  ['h' => 120, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(  0, 255, 127)  ===  ['h' => 150, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(  0, 255, 255)  ===  ['h' => 180, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(127,   0, 255)  ===  ['h' => 270, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(127, 127, 255)  ===  ['h' => 240, 's' =>  50, 'v' => 100] );
var_dump( RGB_to_HSV(127, 255,   0)  ===  ['h' =>  90, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(127, 255, 127)  ===  ['h' => 120, 's' =>  50, 'v' => 100] );
var_dump( RGB_to_HSV(255,   0,   0)  ===  ['h' =>   0, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(255,   0, 127)  ===  ['h' => 330, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(255,   0, 255)  ===  ['h' => 300, 's' => 100, 'v' => 100] );
var_dump( RGB_to_HSV(255, 127,   0)  ===  ['h' =>  30, 's' =>  50, 'v' => 100] );
var_dump( RGB_to_HSV(255, 127, 127)  ===  ['h' =>   0, 's' =>  50, 'v' => 100] );
var_dump( RGB_to_HSV(255, 255, 255)  ===  ['h' =>   0, 's' =>   0, 'v' => 100] );
var_dump( RGB_to_HSV(900, 900, 900)  ===  ['h' =>   0, 's' =>   0, 'v' => 100] );

# trying all possible values to try to raise an error
for ($r = -10; $r <= 260; $r++) {
    for ($g = -10; $g <= 260; $g++) {
        for ($b = -10; $b <= 260; $b++) {
            RGB_to_HSV($r, $g, $g);
        }
    }
}

我在Effcore CMS中实现了这个示例,并做了一个测试,可以通过系统界面或通过命令行界面运行。


-2
投票

这是我的看法,以及单元测试。由于

S
V
值是百分比,因此此代码将它们返回为整数 (0, 100),而不是 (0, 1) - 例如,使用
75
而不是
0.75

final class MathService
{    
    /**
     * Converts an RGB point into HSV
     *
     * @param int $r
     * @param int $g
     * @param int $b
     * @return array
     */
    public function rgbToHsv(int $r, int $g, int $b): array
    {
        $rPrime = $r / 255;
        $gPrime = $g / 255;
        $bPrime = $b / 255;

        $max = max([$rPrime, $gPrime, $bPrime]);
        $min = min([$rPrime, $gPrime, $bPrime]);

        $delta = $max - $min;

        // Calculate H
        if ($delta == 0) {
            $h = 0;
        } else {
            if ($max === $rPrime) {
                $h = 60 * ((($gPrime - $bPrime) / $delta) % 6);
            }
            if ($max === $gPrime) {
                $h = 60 * ((($bPrime - $rPrime) / $delta) + 2);
            }
            if ($max === $bPrime) {
                $h = 60 * ((($rPrime - $gPrime) / $delta) + 4);
            }
        }

        // Calculate S
        if ($max == 0) {
            $s = 0;
        } else {
            $s = $delta / $max;
        }

        // Calculate V
        $v = $max;

        return [$h, (int)($s * 100), (int)($v * 100)];
    }
}

使用 PHP 7.2 的 PHPUnit 测试用例

/**
 * @test
 */
public function rgbToHsv_ComputesCorrectValues(): void
{
    $service = new MathService();
    $samples = [
        // [R, G, B, H, S, V]
        [0, 0, 0, 0, 0, 0],
        [255, 255, 255, 0, 0, 100],
        [255, 0, 0, 0, 100, 100],
        [0, 255, 0, 120, 100, 100],
        [0, 0, 255, 240, 100, 100],
        [255, 255, 0, 60, 100, 100],
        [0, 255, 255, 180, 100, 100],
        [255, 0, 255, 300, 100, 100],
        [192, 192, 192, 0, 0, 75],
        [128, 128, 128, 0, 0, 50],
        [128, 0, 0, 0, 100, 50],
        [128, 128, 0, 60, 100, 50],
        [0, 128, 0, 120, 100, 50],
        [128, 0, 128, 300, 100, 50],
        [0, 128, 128, 180, 100, 50],
        [0, 0, 128, 240, 100, 50],
    ];

    foreach ($samples as $sample) {
        list($r, $g, $b) = array_slice($sample, 0, 3);
        $expected = array_slice($sample, 3);
        $hsv = $service->rgbToHsv($r, $g, $b);
        list($h, $s, $v) = $hsv;

        self::assertEquals($expected, $hsv, "Error converting ({$r}, ${g}, ${b}). Got ({$h}, {$s}, {$v})");
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.