提取圖片主色調是圖片搜索、推薦系統、設計工具的核心需求。傳統遍歷像素統計 RGB 頻率效率極低 (O(像素數))。Imagick 的 quantizeImage() 基於中位切分/八叉樹量化算法,能在毫秒級將圖片減色至 N 色並輸出調色板,是生產環境提取主色調的標準方案。
quantizeImage() 將圖片顏色空間從數百萬色壓縮到指定數量 (如 10-256 色),通過聚類相似顏色,保留視覺上最具代表性的顏色組合。返回的調色板即為圖片主色調集合。
<?php
/**
* 使用 Imagick 量化算法提取圖片主色調
* @param string $imagePath 圖片路徑或 URL
* @param int $colorCount 目標顏色數 (建議 8-16)
* @param int $colorSpace 色彩空間常量
* @return array [[r,g,b,a], ...] 主色調陣列
*/
function getDominantColors(string $imagePath, int $colorCount = 10, int $colorSpace = Imagick::COLORSPACE_RGB): array
{
try {
$imagick = new Imagick($imagePath);
// 1. 縮略圖預處理:加速量化,不影響主色調準確性
// 最大邊 200px 足夠,大圖量化極慢
$imagick->thumbnailImage(200, 200, true, true);
// 2. 顏色量化:核心步驟
// $colorCount: 目標顏色數
// $colorSpace: COLORSPACE_RGB / COLORSPACE_LAB / COLORSPACE_HSV
// 0: 無樹深度限制
// false: 不測量誤差
// false: 不使用 Floyd-Steinberg 抖動
$imagick->quantizeImage($colorCount, $colorSpace, 0, false, false);
// 3. 合併相似顏色 (去重)
$imagick->uniqueImageColors();
// 4. 提取調色板
$colors = [];
$iterator = $imagick->getPixelIterator();
$iterator->resetIterator();
while ($row = $iterator->getNextIteratorRow()) {
foreach ($row as $pixel) {
$color = $pixel->getColor(); // ['r'=>..., 'g'=>..., 'b'=>..., 'a'=>...]
// 過濾透明度極低的顏色
if (($color['a'] ?? 1) > 0.1) {
$colors[] = [
'r' => (int)$color['r'],
'g' => (int)$color['g'],
'b' => (int)$color['b'],
'a' => round(($color['a'] ?? 1), 2),
'hex' => sprintf('#%02x%02x%02x', $color['r'], $color['g'], $color['b']),
];
}
}
}
$imagick->destroy();
return $colors;
} catch (ImagickException $e) {
error_log("Imagick 色調提取失敗: {$e->getMessage()}");
return [];
}
}
// 使用示例
$colors = getDominantColors('photo.jpg', 10);
echo "主色調:\n";
foreach ($colors as $c) {
echo "RGB({$c['r']}, {$c['g']}, {$c['b']}) {$c['hex']} Alpha:{$c['a']}\n";
}
$colorCount | 8-16 | 過少丟失細節,過多計算慢且含噪聲色 |
| $colorSpace | Imagick::COLORSPACE_LAB | **LAB 空間更符合人眼視覺感知**,聚類效果優於 RGB |
| thumbnailImage | 200x200 | 平衡速度與精度,原圖量化可能耗秒級 |
| uniqueImageColors | 必須調用 | 合併量化後的相似顏色,減少輸出數量 |
<?php
// 對比不同色彩空間效果
$spaces = [
'RGB' => Imagick::COLORSPACE_RGB,
'LAB' => Imagick::COLORSPACE_LAB,
'HSV' => Imagick::COLORSPACE_HSV,
'YUV' => Imagick::COLORSPACE_YUV,
];
foreach ($spaces as $name => $space) {
$colors = getDominantColors('test.jpg', 8, $space);
echo "=== $name 空間 ===\n";
foreach ($colors as $c) echo "{$c['hex']} ";
echo "\n\n";
}
quantizeImage 不直接返回顏色佔比。需結合直方圖或像素統計:
<?php
function getDominantColorsWithWeight(string $imagePath, int $colorCount = 10): array
{
$imagick = new Imagick($imagePath);
$imagick->thumbnailImage(150, 150, true, true);
$imagick->quantizeImage($colorCount, Imagick::COLORSPACE_LAB, 0, false, false);
$imagick->uniqueImageColors();
// 獲取調色板顏色
$palette = [];
$iterator = $imagick->getPixelIterator();
while ($row = $iterator->getNextIteratorRow()) {
foreach ($row as $pixel) {
$c = $pixel->getColor();
$palette[] = sprintf('#%02x%02x%02x', $c['r'], $c['g'], $c['b']);
}
}
// 統計每個調色板顏色的像素佔比
$histogram = $imagick->getImageHistogram(); // 返回 [Pixel => count]
$totalPixels = $imagick->getImageWidth() * $imagick->getImageHeight();
$results = [];
foreach ($histogram as $pixel => $count) {
$c = $pixel->getColor();
$hex = sprintf('#%02x%02x%02x', $c['r'], $c['g'], $c['b']);
$results[] = [
'hex' => $hex,
'rgb' => [$c['r'], $c['g'], $c['b']],
'count' => $count,
'percentage' => round($count / $totalPixels * 100, 2),
];
}
// 按佔比降序
usort($results, fn($a, $b) => $b['percentage'] <=> $a['percentage']);
$imagick->destroy();
return $results;
}
/* 輸出示例:
[
['hex' => '#2c3e50', 'percentage' => 35.2],
['hex' => '#ecf0f1', 'percentage' => 28.7],
['hex' => '#e74c3c', 'percentage' => 12.1],
...
]
*/
<?php
// 併發處理:使用並行或隊列
function processBatch(array $imagePaths): Generator
{
foreach ($imagePaths as $path) {
yield $path => getDominantColors($path);
// 釋放內存
gc_collect_cycles();
}
}
// 異步隊列 (Redis/RabbitMQ) 處理海量圖片
// 生產者:推送圖片路徑
// 消費者:調用 getDominantColors → 存入數據庫/ES
COLORSPACE_RGB |
| 內存溢出 | 大圖未縮略直接量化 | **強制 thumbnailImage 先縮放** |
| 透明背景干擾 | PNG 透明區被量化為黑/白 | 量化前合成白背景:$im->setImageBackgroundColor('white'); $im->flattenImages(); |
| 速度太慢 | 未縮略圖 / 色彩數過多 / 開啟抖動 | 縮略圖 200px、色彩數 10、dither=false |
| 中文路徑失敗 | Imagick 內部編碼問題 | 使用 realpath() 或 file:// 協議 |
quantizeImage(LAB, 10) → uniqueImageColors() → 遍歷 PixelIteratorgetImageHistogram() 獲取像素佔比,輸出真正主色調掌握 Imagick 量化算法,能以 100ms 級延遲 穩定提取圖片主色調,為「以圖搜圖」、「智能配色」、「內容標簽」等 AI 應用提供高質量色彩特徵向量。