【问题标题】:RGB to HSV in PHPPHP中的RGB到HSV
【发布时间】:2012-10-15 13:03:50
【问题描述】:

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

【问题讨论】:

标签: php image colors rgb hsv


【解决方案1】:

这是一个简单直接的方法,它以度数和百分比的形式返回 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);
}

?>

【讨论】:

  • 很棒的答案。正是我需要学习如何计算 HSV 的价值部分。泰。
  • 有没有办法扭转这个过程,把它转换回来?
  • @JacobTheDev 当然。 This question 有例子。
  • 为什么$G == $minRGB被注释掉了?重要吗?出错了?
  • @ashleedawg 只有三种可能性。给定 [R, B, G] 的集合,如果 R 和 B 已被排除,则无需显式检查 G。评论只是提供信息。
【解决方案2】:
<?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;
}

【讨论】:

  • 我整理了这段代码中的一些错误,根据已知结果对其进行了测试,它运行良好。谢谢!
  • 我正在尝试了解 HSV 的工作原理。在返回值之前,您是否不必将 $H 乘以 360,并将 $S 和 $V 乘以 100?
  • @JackHumphries - 请参阅我的答案以获得更容易遵循的版本,它还返回度数和百分比。
  • 我已经将这个和它的姊妹函数合并到一个更有用的例子中:gist.github.com/zyphlar/55dea0fae7914ff8eb4a
  • 第一个链接坏了。
【解决方案3】:

经过彻底测试和压缩,这是我将要坚持使用的将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
)

【讨论】:

    【解决方案4】:

    我是这样做的

    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;
    }
    

    参考资料链接here

    【讨论】:

      【解决方案5】:

      这是我对它的看法,以及一个单元测试。由于 SV 值是百分比,因此此代码将它们返回为整数 (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})");
          }
      }
      

      【讨论】:

      • 我不明白,只是浪费时间试图弄清楚你想用这段代码返回什么。您是否暗示 all 其他答案不正确?这不会返回 低于 60 或高于 300 的色调值。此外,虽然整数可能更容易查看,但百分比normally 表示为01 之间的值,如果需要对百分比进行进一步计算,这也可以节省步骤,并且通过不四舍五入来保持准确性值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多