PHP 中的颜色排序:第 3 部分

上次我查看颜色分类时,我产生了一个漂亮的色带或分类颜色,但为此我基本上从颜色信息中删除了三分之一的数据。这意味着在排序的颜色带中没有白色或黑色。

在思考如何解决这个问题后,我想到了一种使用二维颜色数组将颜色过滤成块的方法。这将允许正确渲染丢失的颜色信息,并且仅意味着少量工作使其能够与前面示例中使用的渲染函数一起使用。

生成数据

我可以轻松地生成每种可用的颜色并将其用作数据。我想要生成的是随机分类的颜色,代表系统或其他输入生成的数据类型。

为了生成数据,我创建了一个包含 360 个空白数组的数组。360 表示颜色的色调值,因此所需要做的就是创建颜色的饱和度和值,然后将它们存储在正确的数组中。

以下代码初始化颜色数组,然后用一些随机数据填充它们。该hsvToRgb()函数可以在第 2 部分中找到。

$colorRanges = array_fill(0, 360, []);
 
foreach ($colorRanges as $hue => $range) {
  for ($i = 0; $i < 300; ++$i) {
    // 为饱和度和值生成随机值。
    $saturation = mt_rand(0, 100) / 100;
    $value = mt_rand(0, 100) / 100;
 
    // 将颜色从 hsv 转换为 rgb。
    $rgb = hsvToRbg($hue, $saturation, $value);
 
    // 创建颜色对象。
    $colorRanges[$hue][] = new Color($rgb['red'], $rgb['blue'], $rgb['green']);
  }
}

300 的循环只是一个任意值,用于在每个色调数组中生成相当数量的数据。

渲染

渲染函数只需要一点点改动,就可以渲染一个多维的 Color 对象数组,而不仅仅是一个平面数组。

function renderColors($colorRanges, $sortedBy) {
  $pixelSize = 1;
 
  // 锻炼宽度。
  $width = count($colorRanges) * $pixelSize;
 
  // 确保计算出正确的高度。
  $height = 0;
  foreach ($colorRanges as $colorRange) {
    $potentialHeight = count($colorRange) * $pixelSize;
    if ($potentialHeight > $height) {
      $height = $potentialHeight;
    }
  }
 
  // 初始化图像。
  $im = imagecreatetruecolor($width, $height);
 
  // 用彩色像素填充图像。
  $x = 0;
  foreach ($colorRanges as $colorRange) {
    $y = 0;
    foreach ($colorRange as $yColor => $color) {
      $pixelColour = imagecolorallocate($im, $color->red, $color->green, $color->blue);
      imagefilledrectangle($im, $x, $y, $x + $pixelSize, $y + $pixelSize, $pixelColour);
      $y = $y + $pixelSize;
    }
    $x = $x + $pixelSize;
  }
 
  // 保存图片。
  imagepng($im, 'colors-' . $sortedBy . '.png');
  imagedestroy($im);
}

不进行排序会生成以下图像。

这表明色调已经被分类为颜色带,所以我们需要做的就是遍历每个带并按其他值对其进行排序。

排序

有了这个,我们需要对每个数组进行排序,看看我们是否可以通过只对颜色带进行排序来获得任何不错的结果。这与其他帖子中完成的排序不同,不同之处在于我们遍历数组并对每个颜色带进行排序。

RGB

按 RGB 排序就像听起来一样,只需将 reg、green 和 blue 值加在一起并进行比较。

foreach ($colorRanges as $id => $colorRange) {
  usort($colorRange, function ($a, $b) {
    return ($a->red + $a->green + $a->blue) <=> ($b->red + $b->green + $b->blue);
  });
  $colorRanges[$id] = $colorRange;
}
 
renderColors($colorRanges, 'rgb' . time());

这将生成以下图像。

很明显,这里进行了一些排序。较白的颜色被推向底部,而较暗的颜色被推向顶部。格雷仍然有点问题,因为它似乎没有太大变化。

亮度

为了轻松起见,我重新使用了calculateLightness()第 1 部分中的功能。

foreach ($colorRanges as $id => $colorRange) {
  usort($colorRange, function ($a, $b) {
    return calculateLightness($a) <=> calculateLightness($b);
  });
  $colorRanges[$id] = $colorRange;
}
 
renderColors($colorRanges, 'lightness' . time());

这会产生以下输出。

类似于 RGB 排序,这适用于较浅和较深的颜色,但在很大程度上忽略了灰色。我认为这比将 RGB 值加在一起会产生更好的结果,但事实并非如此。

亮度

颜色的亮度是通过计算红色、绿色和蓝色的比例来计算的,以创建感知的亮度水平。重用calculateLuminance()第 1 部分中的函数。

foreach ($colorRanges as $id => $colorRange) {
  usort($colorRange, function ($a, $b) {
    return calculateLuminance($a) <=> calculateLuminance($b);
  });
  $colorRanges[$id] = $colorRange;
}
 
renderColors($colorRanges, 'luminance' . time());

这将产生以下图像。

这会产生比亮度或 RGB 排序更好的结果,但灰色仍然会导致问题。

HSV

没有必要按色调、饱和度和值进行排序,因为我们在技术上已经按色调进行了排序。因此,我们只需要按饱和度和值的总和进行排序。

foreach ($colorRanges as $id => $colorRange) {
  usort($colorRange, function ($a, $b) {
    $hsv1 = rgbTohsv($a);
    $hsv2 = rgbTohsv($b);
 
    return ($hsv1['saturation'] + $hsv1['value']) <=> ($hsv2['saturation'] + $hsv2['value']);
  });
  $colorRanges[$id] = $colorRange;
}
 
renderColors($colorRanges, 'hsv' . time());

这将创建以下图像。

这将高饱和度的颜色推到了底部,将低饱和度的颜色推到了顶部。这会导致白色和灰色分布在整个光谱中,并且没有分类到正确的结果中。

十六进制

出于兴趣(并且因为在使用单个颜色带进行排序时,HEX 排序非常好)我决定尝试按颜色的 HEX 值进行排序。

foreach ($colorRanges as $id => $colorRange) {
  usort($colorRange, 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;
  });
  $colorRanges[$id] = $colorRange;
}
 
renderColors($colorRanges, 'hex' . time());

这将生成以下图像。

是的,相当可怕。奇怪的是,这让我想起了叶绿素的颜色吸收光谱。这种模式是由于创建十六进制值的方式而创建的。由于十六进制值相加的方式,我基本上创建了一个过程,该过程赋予红色更高的优先级,因此在此示例中这些颜色的排序稍好一些。

不够?

这种对多维数组排序的方法比对一维数组排序更接近,但仍然存在一些问题。我之前说过,创建色相带会创建单独的颜色块。尽管这在技术上是正确的,但我们仍然无法排序,因为我们本质上必须组合一个或多个值才能生成排序算法。虽然我们可以对每种颜色进行分离和排序,但仍有很多灰度值不能很好地适应排序算法,因为我们本质上是试图将一个三维数据项(即颜色)强制转换为二维数组.