使用PHP检测图像中的主要颜色

41
我正在尝试复制Dribbble.com检测图像中主要颜色的功能。在下面的图片中,您可以看到来自Dribbble.com的截图,显示了左侧图像中8种主要颜色。这是图像的实际页面http://dribbble.com/shots/528033-Fresh-Easy?list=following
我需要能够在PHP中执行此操作,一旦获得所需的颜色,我将将它们保存到数据库中,以便不需要在每次页面加载时运行处理。
经过一些关于如何从图像中获取这些颜色的研究,有些人说,你只需逐像素地检查图像,然后保存出现最多的颜色。其他人说还有更多内容,而且仅获取存在最频繁的颜色无法产生所需的效果。他们说,您需要对图像/颜色进行量化(在这一点上,我迷失了)。
在下面的图像中,Dribble shot下面是一个执行相同操作的Javascript库,该页面可以在此处查看http://lokeshdhakar.com/projects/color-thief/

查看该页面的源代码,我可以看到一个名为quantize.js的Javascript文件,其结果非常好。因此,我希望能够使用PHP和GD/ImageMagick做到与该Javascript库相同的功能。

enter image description here


我找到了一个用PHP返回图像颜色和数量的函数,但结果与上面的Javascript版本和Dribble结果不同。
/**
 * Returns the colors of the image in an array, ordered in descending order, where the keys are the colors, and the values are the count of the color.
 *
 * @return array
 */
function Get_Color()
{
    if (isset($this->image))
    {
        $PREVIEW_WIDTH    = 150;  //WE HAVE TO RESIZE THE IMAGE, BECAUSE WE ONLY NEED THE MOST SIGNIFICANT COLORS.
        $PREVIEW_HEIGHT   = 150;
        $size = GetImageSize($this->image);
        $scale=1;
        if ($size[0]>0)
        $scale = min($PREVIEW_WIDTH/$size[0], $PREVIEW_HEIGHT/$size[1]);
        if ($scale < 1)
        {
            $width = floor($scale*$size[0]);
            $height = floor($scale*$size[1]);
        }
        else
        {
            $width = $size[0];
            $height = $size[1];
        }
        $image_resized = imagecreatetruecolor($width, $height);
        if ($size[2]==1)
        $image_orig=imagecreatefromgif($this->image);
        if ($size[2]==2)
        $image_orig=imagecreatefromjpeg($this->image);
        if ($size[2]==3)
        $image_orig=imagecreatefrompng($this->image);
        imagecopyresampled($image_resized, $image_orig, 0, 0, 0, 0, $width, $height, $size[0], $size[1]); //WE NEED NEAREST NEIGHBOR RESIZING, BECAUSE IT DOESN'T ALTER THE COLORS
        $im = $image_resized;
        $imgWidth = imagesx($im);
        $imgHeight = imagesy($im);
        for ($y=0; $y < $imgHeight; $y++)
        {
            for ($x=0; $x < $imgWidth; $x++)
            {
                $index = imagecolorat($im,$x,$y);
                $Colors = imagecolorsforindex($im,$index);
                $Colors['red']=intval((($Colors['red'])+15)/32)*32;    //ROUND THE COLORS, TO REDUCE THE NUMBER OF COLORS, SO THE WON'T BE ANY NEARLY DUPLICATE COLORS!
                $Colors['green']=intval((($Colors['green'])+15)/32)*32;
                $Colors['blue']=intval((($Colors['blue'])+15)/32)*32;
                if ($Colors['red']>=256)
                $Colors['red']=240;
                if ($Colors['green']>=256)
                $Colors['green']=240;
                if ($Colors['blue']>=256)
                $Colors['blue']=240;
                $hexarray[]=substr("0".dechex($Colors['red']),-2).substr("0".dechex($Colors['green']),-2).substr("0".dechex($Colors['blue']),-2);
            }
        }
        $hexarray=array_count_values($hexarray);
        natsort($hexarray);
        $hexarray=array_reverse($hexarray,true);
        return $hexarray;

    }
    else die("You must enter a filename! (\$image parameter)");
}

我想问一下,有没有人知道如何用PHP完成这样的任务?或者你知道已经存在的东西,或者有任何提示可以让我更接近完成这个任务,将不胜感激。


https://dev59.com/WnA75IYBdhLWcg3wGlIa - Luca Filosofi
@aSeptik,它看起来做的与我已经发布的代码相同。 - JasonDavis
你尝试过在谷歌上搜索“php从图像中获取调色板”吗?我找到了很多结果,只是问一下... - Luca Filosofi
3
是的,我已经尝试过了,其中95%都是我之前发布过的颜色计数方法。我正在寻找更好的量化图像的方法,以获得类似于Dribble和我之前发布的Javascript库中使用的颜色。 - JasonDavis
量化也不能解决问题(如Alex在他的回答中所写的聚类)。你不能将JS代码1:1地移植到PHP的原因是它们使用画布元素 - 因此评估发生在前端... - Chris
7个回答

49

以下是您在PHP中寻找的准确答案:https://github.com/thephpleague/color-extractor

示例:

use League\ColorExtractor\Palette;

$palette = Palette::fromFilename('some/image.png');

$topEightColors = $palette->getMostUsedColors(8);

1
他们代码库中的当前版本没有Client类或loadPng()方法,因此您应该使用新代码更新您的答案,否则我将使用我制作的可行代码编辑您的答案。 - AwesomeGuy

19

这是我获取一张图片主要颜色的简单方法。

$image=imagecreatefromjpeg('image.jpg');
$thumb=imagecreatetruecolor(1,1);
imagecopyresampled($thumb,$image,0,0,0,0,1,1,imagesx($image),imagesy($image));
$mainColor=strtoupper(dechex(imagecolorat($thumb,0,0)));
echo $mainColor;

1
该算法不提供主要颜色,而是混合颜色。 - jspit
通过这种方式,您只能获得图像颜色的平均值,通常是灰色。 - Omiod

9
你需要缩小图片,就可以得到图片的主要颜色。如果你需要在调色板上使用4种颜色,则将其缩小到约8x8,6种颜色则缩小到约12x8等等...
使用imagecopyresized来缩小图像,然后检查每个像素,并将它们存储在数组imagecolorat($image,px,py)中。
试试这个。
<?php

// EXAMPLE PICTURE
$url='https://www.nordoff-robbins.org.uk/sites/default/files/google.jpg';

//var_dump(getColorPallet($url));

echoColors(getColorPallet($url));


function echoColors($pallet){ // OUTPUT COLORSBAR
    foreach ($pallet as $key=>$val)
        echo '<div style="display:inline-block;width:50px;height:20px;background:#'.$val.'"> </div>';
}

function getColorPallet($imageURL, $palletSize=[16,8]){ // GET PALLET FROM IMAGE PLAY WITH INPUT PALLET SIZE
    // SIMPLE CHECK INPUT VALUES
    if(!$imageURL) return false;

    // IN THIS EXEMPLE WE CREATE PALLET FROM JPG IMAGE
    $img = imagecreatefromjpeg($imageURL);

    // SCALE DOWN IMAGE
    $imgSizes=getimagesize($imageURL);

    $resizedImg=imagecreatetruecolor($palletSize[0],$palletSize[1]);

    imagecopyresized($resizedImg, $img , 0, 0 , 0, 0, $palletSize[0], $palletSize[1], $imgSizes[0], $imgSizes[1]);

    imagedestroy($img);

    //CHECK IMAGE
    /*header("Content-type: image/png");
    imagepng($resizedImg);
    die();*/

    //GET COLORS IN ARRAY
    $colors=[];

    for($i=0;$i<$palletSize[1];$i++)
        for($j=0;$j<$palletSize[0];$j++)
            $colors[]=dechex(imagecolorat($resizedImg,$j,$i));

    imagedestroy($resizedImg);

    //REMOVE DUPLICATES
    $colors= array_unique($colors);

    return $colors;

}
?>

对我来说完美运作。


这是一个很好的轻量级获取颜色的示例。有没有办法将其限制为最常用的颜色。例如,图像中使用的前8种颜色? - JasonDavis

4
您所链接的页面有一个指向GitHub源代码的链接,因此如果您想确切了解他们是如何做的,您可以在PHP中复制他们的源代码。
他们做法与您的做法的最大区别在于,他们使用聚类来查找颜色。他们在存储颜色时不会四舍五入,而是将所有原始颜色存储在数组中。然后,他们循环遍历该数组,直到找到具有最高点数比集群颜色数更高比率的集群。该集群的中心点是最常见的颜色。调色板由下一个最高集群组成,并加入一些逻辑以防止集群几乎完全重叠。

3

试一下这个:http://www.coolphptools.com/color_extract

适用于JPEG和PNG格式的图片。

最棒的是:无需使用composer,只需要require_once即可。

require_once 'colorextract/colors.inc.php';
$ex=new GetMostCommonColors();
$num_results=20;
$reduce_brightness=1;
$reduce_gradients=1;
$delta=24;
$colors=$ex->Get_Color( 'image.png', $num_results, $reduce_brightness, $reduce_gradients, $delta);
print_r($colors);

给你类似这样的东西:

数组 ( [3060a8] => 0.55827380952381 [f0a848] => 0.19791666666667 [000000] => 0.069642857142857 [483018] => 0.02047619047619 [786018] => 0.01827380952381 [183060] => 0.01797619047619 [4878a8] => 0.016011904761905 [181800] => 0.015119047619048 [a87830] => 0.014345238095238 [a8c0d8] => 0.011904761904762 [6090c0] => 0.01172619047619 [d89030] => 0.011011904761905 [90a8d8] => 0.0071428571428571 [ffffff] => 0.0070238095238095 [604830] => 0.006547619047619 [f0f0f0] => 0.0063095238095238 [d8d8f0] => 0.005297619047619 [c0d8d8] => 0.0044047619047619 [f0f0ff] => 0.00041666666666667 [181830] => 0.00011904761904762 )

我用不同的图片试了一下,似乎是可靠的。


1
我有一个使用ImageMagick的Unix bash shell脚本,名为dominantcolor,可能可以满足你的需求。请查看我的脚本网站http://www.fmwconcepts.com/imagemagick/index.php。您可以从PHP exec()中运行它。请在我的主页上查看如何使用的提示。

enter image description here

dominantcolor -n 6 -p all -s save plate.png

count,hexcolor
586,#5ECADC
520,#AFA85D
469,#3C3126
462,#B9C8BB
258,#488A70
205,#B06928


-n 6是颜色量化中所需的颜色数。-p all表示打印所有计数和结果中6种颜色的颜色。-s save表示保存调色板图像。

下面显示的颜色以左侧为主导颜色,并根据上面的列表向右递减计数颜色。

enter image description here


使用 PHP exec()。 - fmw42

1
获取图像的主要颜色的想法有点棘手,因为例如最常见的像素颜色可能在图像中分散得如此广泛,以至于根本不被视为主导颜色。我认为像Color coherence vector这样的算法足以克服这个问题,因为它将颜色聚类成连贯和不连贯的(这相当直观),然后您可以使用它们来丢弃那些错误的主要颜色。我发现这是一个很容易实现的算法,Image Retrieval: Color Coherence Vector describes描述了它的步骤,并提供了它如何工作的示例,甚至在最后提到了matlab实现。

欢迎来到SO。感谢您关于主要颜色检测算法的回答以及分享这些链接。请注意,在SO上,直接发布代码/算法而不是通过链接分享更受欢迎。 - CKE

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接