PHP中的颜色排序

对颜色进行排序是一种您在需要之前从未真正考虑过的事情。按颜色对一堆项目进行排序在许多应用程序中都很有用,但最简单的只是以更可控的方式向用户显示项目。碰巧用颜色排序是一个比我原先想象的要复杂得多的话题,需要深入研究比我预期更多的数学。

顺便说一句,在我开始研究这个之前,我不知道存在整个色彩数学世界。不过值得学习。

配置

首先,我创建了一个小的 Color 类,以便我可以有一种存储颜色的标准方法。这仅采用颜色的红色、绿色和蓝色值,并允许以简单的方式访问这些值。

class Color {

    public $red = 0;

    public $green = 0;

    public $blue = 0;

 

    public function __construct($red, $green, $blue)

    {

        $this->red = $red;

        $this->green = $green;

        $this->blue = $blue;

    }

}

接下来,我创建了一个数组并用许多 Color 对象填充它。这是我将在本文的其余部分进行排序的基础。

$colors = [];

 

for ($i = 0; $i < 750; $i++) {

  $red = ceil(mt_rand(0, 255));

  $green = ceil(mt_rand(0, 255));

  $blue = ceil(mt_rand(0, 255));

 

  $colors[] = new Color($red, $blue, $green);

}

在我们开始对这些 Color 对象进行排序之前,我需要某种方式来显示它们。与其只是打印出对象的 RBG 值,重要的是实际查看已创建的颜色。否则,实际上很难看到颜色排序是否有效。最简单的方法是使用内置的 PHP 图像创建函数来生成图像。以下函数采用在上述示例中创建的 Color 对象数组,并生成一个图像,其中每种颜色由 1 像素宽的带表示。第二个参数是我们使用的排序类型。

function renderColors($colors, $sortedBy) {

    $color_width = 1;

 

    $width = count($colors) * $color_width;

    $height = 50;

 

    $im = imagecreatetruecolor($width, $height);

    $background_color = imagecolorallocate($im, 255, 255, 255);

 

    $x = 0;

 

    foreach ($colors as $colorObject) {

      $color = imagecolorallocate($im, $colorObject->red, $colorObject->green, $colorObject->blue);

      imagefilledrectangle($im, $x, 0, $x + $color_width,$height, $color);

 

      $x = $x + $color_width;

    }

 

    imagepng($im, 'colors-' . $sortedBy . '.png');

    imagedestroy($im);

}

首先要做的是查看一个未排序的颜色数组,这可以使用打印出来。

renderColors($colors, 'none');

这会在名为“colors-none.png”的文件中生成包含随机颜色的以下图像。

现在我们有了一种渲染颜色的方法,我们可以开始对它们进行排序。

RGB 排序

也许最容易排序的是原始 RGB 值。这可以通过将红色、绿色和蓝色这三种不同的成分相加得到一个可以与其他颜色进行比较的数字来实现。

usort($colors, function ($a, $b) {

  return ($a->red + $a->green + $a->blue) <=> ($b->red + $b->green + $b->blue);

});

从表面上看,这感觉应该可以正常工作,因为较深和较浅的颜色将组合在一起。事实上,如果我们对灰色或单色值进行排序,这种类型的排序确实有效,因为颜色之间的差异不会导致重叠。以下是仅使用灰色和单色进行排序的几个示例。

通过随机收集颜色,最终结果或按 RGB 排序非常混乱。

虽然颜色分类很清楚。较深的颜色被推向底部,而较浅的颜色被推向顶部。不过还有很长的路要走。

十六进制排序

一种稍微不同的方法是按十六进制值排序。这是一个在整个 Web 开发中非常常用的值,所以我想看看它给出了什么样的结果。

usort($colors, function ($a, $b) {

      $aValue['red'] = str_pad(dechex($a->red), 2, '0', STR_PAD_LEFT);

      $aValue['green'] = str_pad(dechex($a->green), 2, '0', STR_PAD_LEFT);

      $aValue['blue'] = str_pad(dechex($a->blue), 2, '0', STR_PAD_LEFT);

 

      $bValue['red'] = str_pad(dechex($b->red), 2, '0', STR_PAD_LEFT);

      $bValue['green'] = str_pad(dechex($b->green), 2, '0', STR_PAD_LEFT);

      $bValue['blue'] = str_pad(dechex($b->blue), 2, '0', STR_PAD_LEFT);

 

      $aValue = implode($aValue);

      $bValue = implode($bValue);

 

      return $aValue <=> $bValue;

});

不幸的是,这种类型的颜色分类会产生糟糕的结果,蓝色/绿色被推低而红色被推高。

这更多是十六进制值中颜色排序的副作用,而不是所涉及的实际颜色的表示。

亮度排序

我没有按照基本值进行排序,而是查看颜色具有的其他值。我首先看到的是轻盈。亮度是相对于类似照明的白色亮度的亮度。这是通过查看最小和最大颜色值来计算的。

function calculateLightness($color) {

  $red = $color->red / 255;

  $green = $color->green / 255;

  $blue = $color->blue / 255;

 

  $chroma_min = min($red, $green, $blue);

  $chroma_max = max($red, $green, $blue);

 

  return ($chroma_max + $chroma_min) / 2;

}

 

usort($colors, function ($a, $b) {

  return calculateLightness($a) <=> calculateLightness($b);

});

这会产生以下排序颜色。

这显然是按亮度排序的,较浅的颜色逐渐推向一端,但颜色看起来很混乱。

亮度排序

亮度描述颜色的感知亮度,而不是测量的亮度。有几种不同的标准可用于计算亮度,但我们在这里采用的是光度/数字 ITU BT.709。这是使用计算

(0.2126 x R) + (0.7152 x G) + (0.0722 x B)

要按亮度排序,我们使用以下代码。

function calculateLuminance($color) {

  return (0.2126 * $color->red) + (0.7152 * $color->green) + (0.0722 * $color->blue);

}

 

usort($colors, function ($a, $b) {

  return calculateLuminance($a) <=> calculateLuminance($b);

});

这会产生以下排序颜色。

这类似于亮度排序,但显然对被排序的颜色有不同的偏差。

HSV 分选

我没有依赖 RGB 颜色进行排序,而是想知道如果我将 RGB 转换为不同的颜色空间,排序会是什么样子。HSV 或色调、饱和度、值颜色空间的定义方式更接近人类对颜色的感知方式。色调是 0 到 360 之间的值(即圆上的度数),表示实际颜色。饱和度是颜色中灰色的数量,范围从 0% 到 100%,其中 0 表示完全灰色,100 表示完全原色。该值(或亮度)是对 0% 到 100% 之间颜色暗度的度量,其中 0 表示全黑,100 表示颜色最多。在计算机科学中,饱和度和值通常存储为 0 到 1 之间的值。

这是一个将我们的 RGB 颜色转换为 HSV 的函数。它只返回一个包含三个值的关联数组。

function rgbTohsv($color) {

  $red = $color->red / 255;

  $green = $color->green / 255;

  $blue = $color->blue / 255;

 

  $min = min($red, $green, $blue);

  $max = max($red, $green, $blue);

 

  switch ($max) {

    case 0:

      // 如果最大值为 0。

      $hue = 0;

      $saturation = 0;

      $value = 0;

      break;

    case $min:

      // 如果最大值和最小值相同。

      $hue = 0;

      $saturation = 0;

      $value = round($max, 4);

      break;

    default:

      $delta = $max - $min;

      if ($red == $max) {

        $hue = 0 + ($green - $blue) / $delta;

      } elseif ($green == $max) {

        $hue = 2 + ($blue - $red) / $delta;

      } else {

        $hue = 4 + ($red - $green) / $delta;

      }

      $hue *= 60;

      if ($hue < 0) {

        $hue += 360;

      }

      $saturation = $delta / $max;

      $value = round($max, 4);

  }

 

  return ['hue' => $hue, 'saturation' => $saturation, 'value' => $value];

}

此函数可用于按以下方式对颜色数组进行排序。

usort($colors, function ($a, $b) {

  $hsv1 = rgbTohsv($a);

  $hsv2 = rgbTohsv($b);

 

  return ($hsv1['hue'] + $hsv1['saturation'] + $hsv1['value']) <=> ($hsv2['hue'] + $hsv2['saturation'] + $hsv2['value']);

});

这会产生以下色带,这实际上非常接近我们想要的。

这里有一点点噪点,是因为整个地方都点缀着较浅的颜色,但已经很接近了。

色调排序

因为 HSV 排序中的饱和度和价值量非常小,我们可能可以忽略它们并得到类似的结果。所以这里有一个函数可以计算出 RGB 颜色的色调。

function calcualteHue($color) {

  $red = $color->red / 255;

  $green = $color->green / 255;

  $blue = $color->blue / 255;

 

  $min = min($red, $green, $blue);

  $max = max($red, $green, $blue);

 

  switch ($max) {

      case 0:

          // 如果最大值为 0。

          $hue = 0;

          break;

      case $min:

          // 如果最大值和最小值相同。

          $hue = 0;

          break;

      default:

          $delta = $max - $min;

          if ($red == $max) {

              $hue = 0 + ($green - $blue) / $delta;

          } elseif ($green == $max) {

              $hue = 2 + ($blue - $red) / $delta;

          } else {

              $hue = 4 + ($red - $green) / $delta;

          }

          $hue *= 60;

          if ($hue < 0) {

              $hue += 360;

          }

  }

  return $hue;

}

通过我们使用以下函数的函数对颜色进行排序。

usort($colors, function ($a, $b) {

  return calcualteHue($a) <=> calcualteHue($b);

});

这将产生以下排序的彩色图像。

这非常接近于完整的 HSV 排序,并且不需要我们计算饱和度和价值量。尽管如此,它仍然远非完美。

分而治之的色调排序

我们非常接近色调排序,但它仍然需要一些额外的东西来进行更好的排序。更好的方法是将颜色分成单独的色调,然后分别对每个色调进行排序。色调值中大约有 6 种颜色(红色、黄色、绿色、青色、蓝色、洋红色),因此通过将其加倍,我们可以通过将色调除以 30 将色调分成 12 个单独的部分。

// Set up the ranges array.

$ranges = [];

 

foreach ($colors as $color) {

  // 得到色调。

  $hue = calcualteHue($color);

 

  // 简化色调以创建 12 个段。

  $simplifiedHue = floor($hue / 30);

 

  if (!isset($ranges[$simplifiedHue])) {

    // 如果不存在,则将简化的色调添加到范围数组中。

    $ranges[$simplifiedHue] = [];

  }

 

  // 将颜色添加到正确的部分。

  $ranges[$simplifiedHue][] = $color;

}

 

// 按键(简化的色调)对范围进行排序。

ksort($ranges);

有了这个,我们就有了属于特定色调的大致颜色范围。因为 RGB 排序实际上非常适合单色排序,所以我们可以通过它们的 RGB 值对颜色范围内的每个项目进行排序。

$newColors = [];

foreach ($ranges as $id => $colorRange) {

  usort($colorRange, function ($a, $b) {

     return ($a->red + $a->green + $a->blue) <=> ($b->red + $b->green + $b->blue);

  });

  $newColors = array_merge($newColors, $colorRange);

}

渲染 $newColors 颜色数组后,我们得到以下图像

这与我们使用这个随机颜色数据所能得到的差不多,但由于灰色未能正确排序,它仍然有点混乱。其中一些部分确实看起来有点不对,但这是由于我们用来排序的随机颜色。

不可能的?

我在这里尝试以几种不同的方式进行排序,但是每次我接近时,总会有一些东西导致颜色看起来有点乱。

你们中的一些人(特别是如果您以前见过这个问题)现在可能已经发现这实际上是不可能的。问题在于,尝试将包含多维数据的颜色信息排序到二维平面中意味着在排序过程中会遗漏某些数据。这就是为什么您永远不会在任何应用程序或网站中看到线性二维线中的颜色选择器。您实际上倾向于看到 HSV 立方体,您可以在其中选择颜色然后更改饱和度。

我可能会写一篇关于这个主题的后续文章,探讨将颜色分类到 3 维网格中的问题。

如果您有兴趣在单个类中获取本文中看到的所有代码,请查看我为网站进化引擎创建的 Color 类,我一直在研究该类。该 Color 类允许您使用 RGB、十六进制甚至 HSV 创建对象,然后从那里计算出颜色几何。

以上是 PHP中的颜色排序 的全部内容, 来源链接: utcz.com/z/341363.html

回到顶部