Перейти к содержимому
Поиск
Php Разработка

PHP: полный мануал с примерами, функциями и ООП

Практический мануал PHP: от переменных и читаемого кода до безопасных форм, интеграций, базы данных и ООП на примере магазина. Учебный маршрут, поиск функций, подробные примеры, разбор ошибок и исходники для скачивания.

Александр НекрасовИзменено:
Практический мануал PHP: от переменных и функций к объектной модели интернет-магазина
PHP 8.5.11468 глав6256 карточек справочника
Дерево руководства

Глава 374 · После переменных, объектов, файлов, RGB и alpha

Imagick: цветовые группы, общая палитра и настоящие индексированные варианты

От плакатной версии товара к общей палитре разных кадров, корректному извлечению, реальным PNG3/PLTE и художественному cycle с явными alpha-границами.

Получить реальные плакатные и цветовые варианты

Для каталога нужны плакатная версия иллюстрации и несколько намеренно необычных цветовых вариантов. Сначала посмотрим настоящие результаты: предмет на белом фоне, группировка близких цветов, ограниченная палитра и сдвиг выбора цвета. Это разные операции. Группировка не распознаёт предмет, общий набор цветов не гарантирует меньший файл, а циклическая раскраска не обещает естественных оттенков.

Настоящий исходник → группировка гистограмм → joint palette → native cycle+1
Настоящий исходник → группировка гистограмм → joint palette → native cycle+1
Собственный прозрачный вход 400×320 показан OVERwhite
Собственный прозрачный вход 400×320 показан OVERwhite

Растр — сетка пикселей. RGB — три цветовые компоненты, alpha — прозрачность. Палитра — небольшая пронумерованная таблица цветов; индексированный пиксель хранит номер ячейки. $image — переменная с объектом; = присваивает, -> вызывает метод, () содержит аргументы через запятую, ; заканчивает инструкцию. clone создаёт рабочую копию. true/false — логические значения. Переменные, объекты и методы, фон и alpha помогут повторить предпосылки.

Палитру можно представить как подписанные ячейки коробки с красками: пиксель выбирает номер. Это модель индексированного растра, а не описание всех изображений или памяти PHP. Segment группирует сходные цветовые значения; quantizeImages анализирует выбранную часть списка, чтобы ограничить набор; cycle меняет выбор ячейки. Чтобы процесс был понятным, сначала готовим непрозрачную копию: свежий белый фон плюс OVER, затем TRUECOLOR. Исходный прозрачный source.png не перезаписывается.

Понятие и самый маленький вызов

Растр — сетка пикселей. RGB — три цветовые компоненты, alpha — прозрачность. Палитра — небольшая пронумерованная таблица цветов; индексированный пиксель хранит номер ячейки. $image — переменная с объектом; = присваивает, -> вызывает метод, () содержит аргументы через запятую, ; заканчивает инструкцию. clone создаёт рабочую копию. true/false — логические значения. Переменные, объекты и методы, фон и alpha помогут повторить предпосылки.

$ok = $image->segmentImage(Imagick::COLORSPACE_RGB, 5, 5, false);

Один метод получает пространство анализа и два порога. Два числа 5 относятся к разным стадиям; это не 5 цветов и не 5 пикселей. false запрещает подробную печать классов. $ok — статус true; изображение меняется внутри $image. Чтобы получить файл, дальше отдельно выбирают формат и вызывают writeImage. Кружка остаётся узнаваемой, но алгоритм не знает, что это кружка.

Формула 1-u здесь только создаёт второй контролируемый вариант иллюстрации: u — нормированное значение текущего RGB-канала в языке FX, не PHP. Маска RED|GREEN|BLUE ограничивает каналы; | объединяет флаги. Предварительные темы: FX и реальные преобразования цвета. Весь новый метод виден в основном коде; helper занимается только файлами, измерениями и расположением готовых изображений.

Сделать стилизованную иллюстрацию и два цветовых варианта

Исходный предмет остаётся узнаваемым. Отдельная копия группирует близкие цвета; последовательность двух иллюстраций получает совместно рассчитанную палитру; cycle создаёт намеренно необычный цветовой вариант. Вы получаете настоящие PNG, превью и полоски фактических палитр.

Сделать стилизованную иллюстрацию и два цветовых вариантаПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$segmented=clone $sourceWhite;$segmentOK=$segmented->segmentImage(Imagick::COLORSPACE_RGB,5,5,false);
savePaletteView(palettePreview($segmented),'segment-preview.png');savePaletteView($sourceWhite,'source-preview8.png');
$negative=$sourceWhite->fxImage('1-u',$rgb);
$sequence=new Imagick();$sequence->addImage($sourceWhite);$sequence->addImage($negative);$sequence->setFirstIterator();
$quantizeOK=$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$states=paletteSequenceStates($sequence);
$sequence->setFirstIterator();foreach($sequence as $index=>$frame){$current=$sequence->getImage();$assets['first-frame'.$index]=paletteAsset($current,'first-frame'.$index);$checks['detached_frame'.$index]=$assets['first-frame'.$index]['native']['state']===$states[$index]['state'];}
$sequence->setFirstIterator();$cycled=$sequence->getImage();$paletteBefore=paletteEntries($cycled);$cycleOK=$cycled->cycleColormapImage(1);$assets['first-cycle']=paletteAsset($cycled,'first-cycle');
$checks['operations_return_true']=$segmentOK&&$quantizeOK&&$cycleOK;$checks['pair_has_distinct_rasters']=$states[0]['state']['RGBA8_hash']!==$states[1]['state']['RGBA8_hash'];$checks['saved_pair_distinct']=$assets['first-frame0']['reread']['state']['RGBA8_hash']!==$assets['first-frame1']['reread']['state']['RGBA8_hash'];$checks['cycle_keeps_native_palette']=$paletteBefore===paletteEntries($cycled);

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,new Imagick(__DIR__.'/segment-preview.png'),paletteAssetView($assets['first-frame0']),paletteAssetView($assets['first-cycle'])], 'first-palette-sequence.png');
savePaletteDiagnostic('palette-sequence-first', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['segment_return'=>$segmentOK,'quantize_return'=>$quantizeOK,'cycle_return'=>$cycleOK,'sequence_states'=>$states,'segmented_state'=>paletteState($segmented)]);
echo "first-palette-sequence.png\nsource-preview8.png\nsegment-preview.png\nfirst-frame0-indexed.png\nfirst-frame0-preview.png\nfirst-frame0-strip.png\nfirst-frame1-indexed.png\nfirst-frame1-preview.png\nfirst-frame1-strip.png\nfirst-cycle-indexed.png\nfirst-cycle-preview.png\nfirst-cycle-strip.png\nmetrics.json\n";
echo "segment=1 sequence=2 cycle=1 source-unchanged=1\n";

Исходный предмет остаётся узнаваемым. Отдельная копия группирует близкие цвета; последовательность двух иллюстраций получает совместно рассчитанную палитру; cycle создаёт намеренно необычный цветовой вариант. Вы получаете настоящие PNG, превью и полоски фактических палитр. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
first-palette-sequence.png
source-preview8.png
segment-preview.png
first-frame0-indexed.png
first-frame0-preview.png
first-frame0-strip.png
first-frame1-indexed.png
first-frame1-preview.png
first-frame1-strip.png
first-cycle-indexed.png
first-cycle-preview.png
first-cycle-strip.png
metrics.json
segment=1 sequence=2 cycle=1 source-unchanged=1

Разделить палитру, индекс, alpha и реально записанный файл

У процесса две границы. Native Imagick хранит текущий растр, класс/палитру и состояние итератора. Код записи затем создаёт конкретный PNG с IHDR, таблицей PLTE и, если она записана, tRNS. После чтения именно этого файла Imagick создаёт отдельное RGB8-превью. Полоска цветов рисуется native из прочитанных palette entries; белые пустые ячейки полоски не считаются дополнительными цветами файла.

Изображение → выбранные кадры → native палитра/индексы → настоящий PNG → его native превью
Изображение → выбранные кадры → native палитра/индексы → настоящий PNG → его native превью
СтадияЧто хранится/происходитЧего не следует предполагать
Direct RGB/RGBAКомпоненты каждого пикселяЧто это уже палитровый растр
Native PaletteTypeИндексы и colormap; цвет/alpha из ячейкиЧто unique_colors равно числу всех слотов
quantizeImages от текущегоТекущее изображение и все NEXTЧто предыдущие кадры тоже обработаны
getImage()Отдельное текущее изображениеЧто clone многостраничного объекта извлечёт нужный кадр
PNG3 файлSaved индекс→PLTE RGB, при наличии tRNS alphaЧто saved PLTE сохранит native номера
RGB8 previewNative чтение файла и OVERwhiteЧто preview является исходным indexed storage

Отдельный контроль сохраняет native прозрачный результат как PNG8/RGBA и сравнивает с намеренно разрешённой forcedPNG3-политикой. В этой сборке принудительный color-type=3 при таком вводе отбросил alpha. PNG3 в принципе поддерживает tRNS, поэтому вывод относится к показанной записи, а не всему формату. Практические opaque-доставки требуют заранее белый фон; helper по умолчанию откажется молча отбрасывать прозрачность. Маленькая четырёхцветная полоса отдельно показывает, что cycle выбирает и alpha новой ячейки.

Увидеть отдельно native alpha, принудительную запись и ошибки

Этот контроль показывает причину ограничений: quantize может изменить исходную alpha; cycle выбирает другую RGBA-ячейку, поэтому меняется и прозрачность. Принудительная PNG3-политика этой сборки даёт непрозрачное повторное чтение. PNG3 поддерживает tRNS; это не доказательство запрета прозрачности форматом.

Увидеть отдельно native alpha, принудительную запись и ошибкиПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$rgbaSequence=new Imagick();$rgbaSequence->addImage($source);$rgbaSequence->setFirstIterator();$quantizeOK=$rgbaSequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$nativeRGBA=$rgbaSequence->getImage();
savePaletteView($nativeRGBA,'native-RGBA.png',true);$nativeRead=new Imagick(__DIR__.'/native-RGBA.png');savePaletteView(palettePreview($nativeRead),'native-white.png');savePaletteView(palettePreview($nativeRead,'#18333c'),'native-dark.png');
// Deliberately allow this labelled lossy policy control; normal deliveries reject translucent input.
$assets['forced-indexed']=paletteAsset($nativeRGBA,'forced-indexed',true);
$pattern=makePaletteAlphaPattern();$patternSequence=new Imagick();$patternSequence->addImage($pattern);$patternSequence->setFirstIterator();$patternSequence->quantizeImages(4,Imagick::COLORSPACE_SRGB,0,true,false);$patternBase=$patternSequence->getImage();$patternCycle=clone $patternBase;$cycleOK=$patternCycle->cycleColormapImage(1);
savePaletteView($patternBase,'pattern-base-RGBA.png',true);savePaletteView($patternCycle,'pattern-cycle-RGBA.png',true);savePaletteView(palettePreview(new Imagick(__DIR__.'/pattern-base-RGBA.png')),'pattern-base-white.png');savePaletteView(palettePreview(new Imagick(__DIR__.'/pattern-cycle-RGBA.png')),'pattern-cycle-white.png');
$errors=[];foreach(['segment'=>fn()=>(new Imagick())->segmentImage(Imagick::COLORSPACE_RGB,5,5,false),'quantize'=>fn()=>(new Imagick())->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false),'cycle'=>fn()=>(new Imagick())->cycleColormapImage(1)] as $name=>$call){try{$call();$errors[$name]=['thrown'=>false];}catch(ImagickException $e){$errors[$name]=['thrown'=>true,'class'=>get_class($e),'message'=>$e->getMessage()];}}
$checks['operations_return_true']=$quantizeOK&&$cycleOK;$checks['all_empty_errors_caught']=count(array_filter($errors,fn($e)=>$e['thrown']))===3;

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,new Imagick(__DIR__.'/native-white.png'),paletteAssetView($assets['forced-indexed']),new Imagick(__DIR__.'/pattern-base-white.png'),new Imagick(__DIR__.'/pattern-cycle-white.png')], 'palette-boundaries.png');
savePaletteDiagnostic('palette-sequence-controls', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['native_RGBA_state'=>paletteState($nativeRGBA),'native_RGBA_read_state'=>paletteState($nativeRead),'native_quantize_preserved_source_alpha'=>$before['alpha8_hash']===paletteHash($nativeRGBA,'A'),'pattern_source'=>paletteState($pattern),'pattern_base'=>paletteState($patternBase),'pattern_cycle'=>paletteState($patternCycle),'cycle_preserved_alpha'=>paletteHash($patternBase,'A')===paletteHash($patternCycle,'A'),'errors'=>$errors]);
echo "palette-boundaries.png\nnative-RGBA.png\nnative-white.png\nnative-dark.png\nforced-indexed-indexed.png\nforced-indexed-preview.png\nforced-indexed-strip.png\npattern-base-RGBA.png\npattern-cycle-RGBA.png\npattern-base-white.png\npattern-cycle-white.png\nmetrics.json\n";
echo "empty-errors=3 native-and-encoded-domains=separate source-unchanged=1\n";

Этот контроль показывает причину ограничений: quantize может изменить исходную alpha; cycle выбирает другую RGBA-ячейку, поэтому меняется и прозрачность. Принудительная PNG3-политика этой сборки даёт непрозрачное повторное чтение. PNG3 поддерживает tRNS; это не доказательство запрета прозрачности форматом. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
palette-boundaries.png
native-RGBA.png
native-white.png
native-dark.png
forced-indexed-indexed.png
forced-indexed-preview.png
forced-indexed-strip.png
pattern-base-RGBA.png
pattern-cycle-RGBA.png
pattern-base-white.png
pattern-cycle-white.png
metrics.json
empty-errors=3 native-and-encoded-domains=separate source-unchanged=1

Построить цветовую группировку и выбрать видимый стиль

Понятие и самый маленький вызов

Растр — сетка пикселей. RGB — три цветовые компоненты, alpha — прозрачность. Палитра — небольшая пронумерованная таблица цветов; индексированный пиксель хранит номер ячейки. $image — переменная с объектом; = присваивает, -> вызывает метод, () содержит аргументы через запятую, ; заканчивает инструкцию. clone создаёт рабочую копию. true/false — логические значения. Переменные, объекты и методы, фон и alpha помогут повторить предпосылки.

$ok = $image->segmentImage(Imagick::COLORSPACE_RGB, 5, 5, false);

Один метод получает пространство анализа и два порога. Два числа 5 относятся к разным стадиям; это не 5 цветов и не 5 пикселей. false запрещает подробную печать классов. $ok — статус true; изображение меняется внутри $image. Чтобы получить файл, дальше отдельно выбирают формат и вызывают writeImage. Кружка остаётся узнаваемой, но алгоритм не знает, что это кружка.

Настоящие native версии 0.5/0.5 и 5/5, затем отдельные PNG3-доставки
Настоящие native версии 0.5/0.5 и 5/5, затем отдельные PNG3-доставки

Сгруппировать цвета для плакатной версии товара

Один явный вызов создаёт плакатный вид без изменения ширины и высоты. Он ищет цветовые группы гистограммы, а не распознаёт предметы и не выдаёт маску кружки.

Сгруппировать цвета для плакатной версии товараПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$poster=clone $sourceWhite;
$ok=$poster->segmentImage(Imagick::COLORSPACE_RGB,5,5,false);
savePaletteView(palettePreview($poster),'poster-preview.png');
$checks['return_true']=$ok===true;$checks['same_geometry']=$poster->getImageWidth()===400&&$poster->getImageHeight()===320;

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,new Imagick(__DIR__.'/poster-preview.png')], 'segment-minimal.png');
savePaletteDiagnostic('palette-sequence-segmentimage-minimal', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['return_type'=>get_debug_type($ok),'return_value'=>$ok,'parameters'=>['colorspace'=>Imagick::COLORSPACE_RGB,'cluster_threshold'=>5.0,'smooth_threshold'=>5.0,'verbose'=>false],'before_state'=>paletteState($sourceWhite),'after_state'=>paletteState($poster)]);
echo "segment-minimal.png\nposter-preview.png\nmetrics.json\n";
echo "segment=1 geometry-kept=1 source-unchanged=1\n";

Один явный вызов создаёт плакатный вид без изменения ширины и высоты. Он ищет цветовые группы гистограммы, а не распознаёт предметы и не выдаёт маску кружки. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
segment-minimal.png
poster-preview.png
metrics.json
segment=1 geometry-kept=1 source-unchanged=1

Порог не обязан совпадать с желаемым количеством цветов. На нашем 400×320 входе контроль 0.5/0.5 дал 28 используемых native цветов,5/5 —7. Это наблюдение конкретного изображения/версии; оно не становится формулой для фотографий. Для выбора дизайна сохраняем обе картинки, затем явно создаём последовательность из этих вариантов и отдельно ограничиваем палитру. foreach перебирает пары имя→двапорога массива, квадратные скобки распределяют пару по переменным. Никакого JSON или регулярных выражений для этой задачи не требуется.

Выбрать стиль группировки и подготовить две индексированные версии

Два настоящих результата позволяют выбрать мягкую или крупную цветовую группировку. Затем отдельная quantizeImages готовит пару индексированных доставок; segmentation и palette quantization остаются разными операциями.

Выбрать стиль группировки и подготовить две индексированные версииПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$presets=['gentle'=>[0.5,0.5],'bold'=>[5.0,5.0]];$views=[];$states=[];$sequence=new Imagick();
foreach($presets as $name=>[$cluster,$smooth]){$poster=clone $sourceWhite;$ok=$poster->segmentImage(Imagick::COLORSPACE_RGB,$cluster,$smooth,false);$checks[$name.'_return']=$ok===true;$states[$name]=paletteState($poster);savePaletteView(palettePreview($poster),$name.'-segmented.png');$views[$name]=new Imagick(__DIR__.'/'.$name.'-segmented.png');$sequence->addImage($poster);}
$sequence->setFirstIterator();$ok=$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$quantizedStates=paletteSequenceStates($sequence);$checks['quantize_return']=$ok===true;
$sequence->setFirstIterator();foreach($sequence as $index=>$frame){$current=$sequence->getImage();$assets['poster-frame'.$index]=paletteAsset($current,'poster-frame'.$index);$checks['detached_frame'.$index]=$assets['poster-frame'.$index]['native']['state']===$quantizedStates[$index]['state'];}

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,$views['gentle'],$views['bold'],paletteAssetView($assets['poster-frame0']),paletteAssetView($assets['poster-frame1'])], 'segment-workflow.png');
savePaletteDiagnostic('palette-sequence-segmentimage-workflow', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['presets'=>$presets,'segmented_states'=>$states,'sequence_states'=>$quantizedStates]);
echo "segment-workflow.png\ngentle-segmented.png\nbold-segmented.png\nposter-frame0-indexed.png\nposter-frame0-preview.png\nposter-frame0-strip.png\nposter-frame1-indexed.png\nposter-frame1-preview.png\nposter-frame1-strip.png\nmetrics.json\n";
echo "presets=2 indexed-deliveries=2 source-unchanged=1\n";

Два настоящих результата позволяют выбрать мягкую или крупную цветовую группировку. Затем отдельная quantizeImages готовит пару индексированных доставок; segmentation и palette quantization остаются разными операциями. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
segment-workflow.png
gentle-segmented.png
bold-segmented.png
poster-frame0-indexed.png
poster-frame0-preview.png
poster-frame0-strip.png
poster-frame1-indexed.png
poster-frame1-preview.png
poster-frame1-strip.png
metrics.json
presets=2 indexed-deliveries=2 source-unchanged=1

Подготовить последовательность и сохранить каждый собственный кадр

Понятие и самый маленький вызов

Растр — сетка пикселей. RGB — три цветовые компоненты, alpha — прозрачность. Палитра — небольшая пронумерованная таблица цветов; индексированный пиксель хранит номер ячейки. $image — переменная с объектом; = присваивает, -> вызывает метод, () содержит аргументы через запятую, ; заканчивает инструкцию. clone создаёт рабочую копию. true/false — логические значения. Переменные, объекты и методы, фон и alpha помогут повторить предпосылки.

$sequence = new Imagick();
$sequence->addImage($first);
$sequence->addImage($second);
$sequence->setFirstIterator();
$ok = $sequence->quantizeImages(16, Imagick::COLORSPACE_SRGB, 0, true, false);

$first и $second здесь уже прочитанные и подготовленные изображения. addImage добавляет их в список, setFirstIterator выбирает начало этого списка. 16 ограничивает размер рассчитываемой палитры,0 просит автоматическую глубину. В этой IM7-сборке true даёт NoDither; последний false отключает расчёт ошибки. После операции foreach выбирает кадры по очереди; $sequence->getImage() извлекает отдельный текущий растр для сохранения. Это важно: clone объекта, содержащего несколько кадров, нельзя выдавать за извлечение выбранного кадра.

Оригинал/негатив: общая native palette против отдельных палитр; каждый saved кадр действительно свой
Оригинал/негатив: общая native palette против отдельных палитр; каждый saved кадр действительно свой

Рассчитать палитру для двух разных иллюстраций

В объект добавлены два действительно разных изображения. setFirstIterator выбирает начало перед совместным анализом, getImage извлекает именно текущий кадр для записи. Оба сохранённых результата должны различаться и соответствовать своим native состояниям.

Рассчитать палитру для двух разных иллюстрацийПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$negative=$sourceWhite->fxImage('1-u',$rgb);savePaletteView(palettePreview($negative),'negative-input.png');
$sequence=new Imagick();$sequence->addImage($sourceWhite);$sequence->addImage($negative);
$sequence->setFirstIterator();
$ok=$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);
$states=paletteSequenceStates($sequence);$sequence->setFirstIterator();
foreach($sequence as $index=>$frame){$current=$sequence->getImage();$assets['joint-frame'.$index]=paletteAsset($current,'joint-frame'.$index);$checks['detached_frame'.$index]=$assets['joint-frame'.$index]['native']['state']===$states[$index]['state'];}
$checks['return_true']=$ok===true;$checks['two_frames']=$sequence->getNumberImages()===2;$checks['native_pair_distinct']=$states[0]['state']['RGBA8_hash']!==$states[1]['state']['RGBA8_hash'];$checks['saved_pair_distinct']=$assets['joint-frame0']['reread']['state']['RGBA8_hash']!==$assets['joint-frame1']['reread']['state']['RGBA8_hash'];

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,new Imagick(__DIR__.'/negative-input.png'),paletteAssetView($assets['joint-frame0']),paletteAssetView($assets['joint-frame1'])], 'quantize-minimal.png');
savePaletteDiagnostic('palette-sequence-quantizeimages-minimal', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['return_type'=>get_debug_type($ok),'return_value'=>$ok,'sequence_states'=>$states,'parameters'=>['number_colors'=>16,'colorspace'=>Imagick::COLORSPACE_SRGB,'tree_depth'=>0,'dither'=>true,'measure_error'=>false]]);
echo "quantize-minimal.png\nnegative-input.png\njoint-frame0-indexed.png\njoint-frame0-preview.png\njoint-frame0-strip.png\njoint-frame1-indexed.png\njoint-frame1-preview.png\njoint-frame1-strip.png\nmetrics.json\n";
echo "sequence=2 detached-files=2 source-unchanged=1\n";

В объект добавлены два действительно разных изображения. setFirstIterator выбирает начало перед совместным анализом, getImage извлекает именно текущий кадр для записи. Оба сохранённых результата должны различаться и соответствовать своим native состояниям. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
quantize-minimal.png
negative-input.png
joint-frame0-indexed.png
joint-frame0-preview.png
joint-frame0-strip.png
joint-frame1-indexed.png
joint-frame1-preview.png
joint-frame1-strip.png
metrics.json
sequence=2 detached-files=2 source-unchanged=1

Когда нужно подготовить набор иллюстраций одной темы, совместный расчёт принимает решение с учётом обоих входов. Отдельные quantizeImage строят решение для каждого. Никакой вариант не объявлен универсально лучшим: выбирайте глазами и по требованиям приложения. В native контроле joint палитра обоих кадров имела 16 одинаковых слотов, но использовалось 14 цветов; writer может сохранять иной PLTE. После записи сравниваем каждый результат со своим текущим кадром, а не удовлетворяемся фактом существования двух файлов.

Итератор — выбранная позиция списка. Если перед вызовом выбрать индекс 1, exact Wand передаст Core именно второй кадр; предыдущий останется прежним. Это полезно при намеренной обработке хвоста, но часто является причиной пропущенного первого изображения. В примере целая последовательность и намеренный suffix показаны отдельно. getImage возвращает текущий отдельный кадр; clone хранителя списка не является надёжным извлечением кадра.

Сравнить общую и отдельные палитры и не пропустить первый кадр

Набор баннеров можно анализировать совместно либо отдельно. Сравниваются настоящие результаты обеих политик. Отдельный контроль начинает с индекса 1: кадр 0 остаётся прежним. measureError не превращает возвращаемый bool в число ошибки.

Сравнить общую и отдельные палитры и не пропустить первый кадрПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$negative=$sourceWhite->fxImage('1-u',$rgb);$inputs=[$sourceWhite,$negative];
$whole=new Imagick();foreach($inputs as $input){$whole->addImage($input);}$whole->setFirstIterator();$ok=$whole->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,true);$wholeStates=paletteSequenceStates($whole);$checks['whole_return']=$ok===true;
$whole->setFirstIterator();foreach($whole as $index=>$frame){$current=$whole->getImage();$assets['whole-frame'.$index]=paletteAsset($current,'whole-frame'.$index);$checks['detached_whole'.$index]=$assets['whole-frame'.$index]['native']['state']===$wholeStates[$index]['state'];}
$independentStates=[];foreach($inputs as $index=>$input){$single=clone $input;$ok=$single->quantizeImage(16,Imagick::COLORSPACE_SRGB,0,true,true);$checks['single_return'.$index]=$ok===true;$independentStates[]=paletteState($single);$assets['single-frame'.$index]=paletteAsset($single,'single-frame'.$index);}
$suffix=new Imagick();foreach($inputs as $input){$suffix->addImage($input);}$suffix->setIteratorIndex(1);$suffixBefore=paletteSequenceStates($suffix,false);$ok=$suffix->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$suffixAfter=paletteSequenceStates($suffix);$checks['suffix_return']=$ok===true;$checks['previous_frame_unchanged']=$suffixBefore[0]['state']===$suffixAfter[0]['state'];
$suffix->setFirstIterator();$untouched=$suffix->getImage();savePaletteView(palettePreview($untouched),'suffix-frame0.png');$suffix->setIteratorIndex(1);$current=$suffix->getImage();$assets['suffix-frame1']=paletteAsset($current,'suffix-frame1');$checks['detached_suffix1']=$assets['suffix-frame1']['native']['state']===$suffixAfter[1]['state'];
$checks['saved_whole_pair_distinct']=$assets['whole-frame0']['reread']['state']['RGBA8_hash']!==$assets['whole-frame1']['reread']['state']['RGBA8_hash'];

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,paletteAssetView($assets['whole-frame0']),paletteAssetView($assets['whole-frame1']),paletteAssetView($assets['single-frame0']),paletteAssetView($assets['single-frame1'])], 'quantize-workflow.png');
savePaletteDiagnostic('palette-sequence-quantizeimages-workflow', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['whole_states'=>$wholeStates,'independent_states'=>$independentStates,'suffix_before'=>$suffixBefore,'suffix_after'=>$suffixAfter,'measure_error_return_is_bool'=>get_debug_type($ok),'whole_native_palettes_equal'=>$wholeStates[0]['palette']===$wholeStates[1]['palette']]);
echo "quantize-workflow.png\nwhole-frame0-indexed.png\nwhole-frame0-preview.png\nwhole-frame0-strip.png\nwhole-frame1-indexed.png\nwhole-frame1-preview.png\nwhole-frame1-strip.png\nsingle-frame0-indexed.png\nsingle-frame0-preview.png\nsingle-frame0-strip.png\nsingle-frame1-indexed.png\nsingle-frame1-preview.png\nsingle-frame1-strip.png\nsuffix-frame0.png\nsuffix-frame1-indexed.png\nsuffix-frame1-preview.png\nsuffix-frame1-strip.png\nmetrics.json\n";
echo "whole=2 independent=2 suffix-start=1 earlier-frame-unchanged=1 source-unchanged=1\n";

Набор баннеров можно анализировать совместно либо отдельно. Сравниваются настоящие результаты обеих политик. Отдельный контроль начинает с индекса 1: кадр 0 остаётся прежним. measureError не превращает возвращаемый bool в число ошибки. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
quantize-workflow.png
whole-frame0-indexed.png
whole-frame0-preview.png
whole-frame0-strip.png
whole-frame1-indexed.png
whole-frame1-preview.png
whole-frame1-strip.png
single-frame0-indexed.png
single-frame0-preview.png
single-frame0-strip.png
single-frame1-indexed.png
single-frame1-preview.png
single-frame1-strip.png
suffix-frame0.png
suffix-frame1-indexed.png
suffix-frame1-preview.png
suffix-frame1-strip.png
metrics.json
whole=2 independent=2 suffix-start=1 earlier-frame-unchanged=1 source-unchanged=1

Сделать художественные цветовые варианты и осмысленно проверить период

Понятие и самый маленький вызов

Растр — сетка пикселей. RGB — три цветовые компоненты, alpha — прозрачность. Палитра — небольшая пронумерованная таблица цветов; индексированный пиксель хранит номер ячейки. $image — переменная с объектом; = присваивает, -> вызывает метод, () содержит аргументы через запятую, ; заканчивает инструкцию. clone создаёт рабочую копию. true/false — логические значения. Переменные, объекты и методы, фон и alpha помогут повторить предпосылки.

$ok = $paletted->cycleColormapImage(1);

$paletted уже подготовлен как палитровое изображение. +1 сдвигает native индекс пикселя к следующей ячейке; после последней выбирается первая. Рисунок получает другую раскраску. Сама таблица не вращается. До вызова на произвольном DirectClass Core может неявно перевести его в PaletteType; поэтому учебный сценарий явно квантирует копию заранее. Даже displacement=0 не обязан быть нейтральным, если перед ним требуется такое преобразование.

Настоящая подготовленная палитра,−1/+1 варианты и native период без подмены savedPLTE
Настоящая подготовленная палитра,−1/+1 варианты и native период без подмены savedPLTE

Получить необычный цветовой вариант из явной палитры

Новый вариант использует следующий цвет таблицы для каждого пикселя. Native таблица цветов остаётся прежней; меняется выбор ячейки. Это художественная перестановка, а не осмысленная замена заданного красного на заданный синий.

Получить необычный цветовой вариант из явной палитрыПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$sequence=new Imagick();$sequence->addImage($sourceWhite);$sequence->setFirstIterator();$ok=$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$base=$sequence->getImage();$assets['base']=paletteAsset($base,'base');
$variant=clone $base;$beforePalette=paletteEntries($variant);$cycleOK=$variant->cycleColormapImage(1);$assets['shift-one']=paletteAsset($variant,'shift-one');
$checks['operations_return_true']=$ok&&$cycleOK;$checks['native_palette_unchanged']=$beforePalette===paletteEntries($variant);$checks['visible_raster_changed']=paletteHash($base)!==paletteHash($variant);

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,paletteAssetView($assets['base']),paletteAssetView($assets['shift-one'])], 'cycle-minimal.png');
savePaletteDiagnostic('palette-sequence-cyclecolormapimage-minimal', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['cycle_return_type'=>get_debug_type($cycleOK),'cycle_return_value'=>$cycleOK,'displace'=>1,'native_before'=>paletteState($base),'native_after'=>paletteState($variant),'palette_before'=>$beforePalette,'palette_after'=>paletteEntries($variant)]);
echo "cycle-minimal.png\nbase-indexed.png\nbase-preview.png\nbase-strip.png\nshift-one-indexed.png\nshift-one-preview.png\nshift-one-strip.png\nmetrics.json\n";
echo "displace=1 native-colormap-unchanged=1 source-unchanged=1\n";

Новый вариант использует следующий цвет таблицы для каждого пикселя. Native таблица цветов остаётся прежней; меняется выбор ячейки. Это художественная перестановка, а не осмысленная замена заданного красного на заданный синий. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
cycle-minimal.png
base-indexed.png
base-preview.png
base-strip.png
shift-one-indexed.png
shift-one-preview.png
shift-one-strip.png
metrics.json
displace=1 native-colormap-unchanged=1 source-unchanged=1

Для набора стилизованных баннеров удобно получить варианты с одинаковой геометрией и проверить обратимость. Мы читаем число всех native ячеек и сдвигаем на него целиком. Не берём period из getImageColors: среди slots могут быть неиспользуемые или повторяющиеся цвета. Native +1 затем−1 возвращает подготовленную RGBA-картину; это не доказательство байтового равенства PNG, метаданных или гарантии одинаковой PLTE-сортировки writer. При наличии alpha другой индекс означает и другую прозрачность: это уже показано на собственной полосе.

Собрать обратимые цветовые варианты и вычислить полный период

Коллекция вариантов строится из одной detached-копии. Отрицательный сдвиг идёт назад; полный период считается по числу native ячеек, а не числу различных видимых цветов. +1 и−1 возвращают native RGBA этого подготовленного растра. Порядок saved PLTE проверяется отдельно.

Собрать обратимые цветовые варианты и вычислить полный периодПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$sequence=new Imagick();$sequence->addImage($sourceWhite);$sequence->setFirstIterator();$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$base=$sequence->getImage();$palette=paletteEntries($base);$period=count($palette);$assets['variant-base']=paletteAsset($base,'variant-base');$states=[];
foreach(['minus-one'=>-1,'plus-one'=>1,'period'=>$period] as $name=>$displace){$variant=clone $base;$ok=$variant->cycleColormapImage($displace);$checks[$name.'_return']=$ok===true;$checks[$name.'_palette']=$palette===paletteEntries($variant);$states[$name]=['displace'=>$displace,'state'=>paletteState($variant)];$assets[$name]=paletteAsset($variant,$name);}
$inverse=clone $base;$inverse->cycleColormapImage(1);$inverse->cycleColormapImage(-1);$checks['inverse_native_RGBA_exact']=paletteHash($inverse)===paletteHash($base);$checks['period_native_RGBA_exact']=$states['period']['state']['RGBA8_hash']===paletteHash($base);

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,paletteAssetView($assets['variant-base']),paletteAssetView($assets['minus-one']),paletteAssetView($assets['plus-one'])], 'cycle-variants.png');
savePaletteDiagnostic('palette-sequence-cyclecolormapimage-workflow', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['native_base'=>paletteState($base),'native_palette'=>$palette,'native_palette_slots'=>$period,'variants'=>$states,'inverse_state'=>paletteState($inverse)]);
echo "cycle-variants.png\nvariant-base-indexed.png\nvariant-base-preview.png\nvariant-base-strip.png\nminus-one-indexed.png\nminus-one-preview.png\nminus-one-strip.png\nplus-one-indexed.png\nplus-one-preview.png\nplus-one-strip.png\nperiod-indexed.png\nperiod-preview.png\nperiod-strip.png\nmetrics.json\n";
echo "variants=3 inverse-exact=1 period-exact=1 source-unchanged=1\n";

Коллекция вариантов строится из одной detached-копии. Отрицательный сдвиг идёт назад; полный период считается по числу native ячеек, а не числу различных видимых цветов. +1 и−1 возвращают native RGBA этого подготовленного растра. Порядок saved PLTE проверяется отдельно. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
cycle-variants.png
variant-base-indexed.png
variant-base-preview.png
variant-base-strip.png
minus-one-indexed.png
minus-one-preview.png
minus-one-strip.png
plus-one-indexed.png
plus-one-preview.png
plus-one-strip.png
period-indexed.png
period-preview.png
period-strip.png
metrics.json
variants=3 inverse-exact=1 period-exact=1 source-unchanged=1

Проверить реальные индексы, coder и ошибки на правильной стадии

Реальные savedPNG3 индексы → PLTE/tRNS, native RGB8 strip и полноразмерный результат
Реальные savedPNG3 индексы → PLTE/tRNS, native RGB8 strip и полноразмерный результат

В PNG IHDR color-type=3 означает indexed raster, а bit_depth=8 —8 бит на индекс в данном файле, не 8 бит суммарного RGB цвета. PLTE хранит RGB тройки, tRNS при наличии задаёт alpha для начальных ячеек, остальные непрозрачны. Настоящие сохранённые индексы здесь берутся из IDAT после распаковки zlib и отмены PNGfilters, а не из mapI: I у exportImagePixels означает интенсивность. getImagePixelColor()->getIndex() тоже нельзя считать доказательством индекса: exact PixelSetQuantumPixel этого Wand не переносит index в PixelWand.

ПроблемаПричина/границаРабочее действие
Два файла неожиданно одинаковыСохранена копия хранителя списка вместо текущего изображенияgetImage перед сохранением; сопоставить каждый файл со своим native состоянием и проверить различие файлов
Первый кадр не изменилсяquantizeImages началась на индексе 1setFirstIterator перед целым списком
Прозрачность измениласьКвантизация, выбор другой RGBA-ячейки либо политика записи coderРазделить native/saved, выбрать opaqueOVER или сохранять осознанный RGBA путь
При false появился точечный узорPHPbool напрямую стал IM7enumСверить binding; здесь true даёт NoDither
Сдвиг 0 неожиданно изменил Direct inputCore сначала преобразует DirectClass в PaletteTypeЯвно подготовить palette и отдельно считать исходное преобразование
Порог 5 не дал 5 цветовПорог цветовой классификации не число цветовСравнить картинки/гистограммы; для colorlimit отдельно quantize
measure_error не дал числоВозвращается boolНе читать bool как RMSE; отдельный метод сравнения — другая задача

Числа nativeRGBA8hash относятся к экспортированному CHAR представлению и отдельно обозначены. Запись PNG8 может округлить Q16 компоненты; до-записи hash не подменяет hash повторно прочитанного файла. Размеры всех кадров известны; более крупные списки требуют собственной политики ресурсов. Размер сохранённого файла в bytes — наблюдение данного PNG, а не обещание, что 16 цветов всегда меньше полноцветного файла. Примеры включают собственный вход, чтобы результат можно было воспроизвести.

Полный helper каждого архива

<?php
declare(strict_types=1);
function restorePaletteInput():void {
    $text=file_get_contents(__DIR__.'/source.png.b64');$raw=$text===false?false:base64_decode($text,true);
    if($raw===false||hash('sha256',$raw)!=='f51e5780445e0f5130a7f23112b2a00618035afd5545b8e3042c7ccff09b617c'||file_put_contents(__DIR__.'/source.png',$raw)===false){throw new RuntimeException('Own source missing or changed');}
}
function paletteHash(Imagick $image,string $channels='RGBA'):string {
    return hash('sha256',json_encode($image->exportImagePixels(0,0,$image->getImageWidth(),$image->getImageHeight(),$channels,Imagick::PIXEL_CHAR),JSON_THROW_ON_ERROR));
}
function paletteState(Imagick $image):array {
    $a=$image->exportImagePixels(0,0,$image->getImageWidth(),$image->getImageHeight(),'A',Imagick::PIXEL_CHAR);
    return ['width'=>$image->getImageWidth(),'height'=>$image->getImageHeight(),'type'=>$image->getImageType(),'colorspace'=>$image->getImageColorspace(),'unique_colors'=>$image->getImageColors(),'alpha'=>$image->getImageAlphaChannel(),'alpha_range'=>[min($a),max($a)],'RGBA8_hash'=>paletteHash($image),'alpha8_hash'=>paletteHash($image,'A')];
}
function paletteEntries(Imagick $image):array {
    $entries=[];
    for($i=0;$i<256;$i++){
        try{$pixel=$image->getImageColormapColor($i);}catch(ImagickException $e){break;}
        $entries[]=['index'=>$i,'normalized_RGBA'=>$pixel->getColor(1),'string'=>$pixel->getColorAsString()];
    }
    return $entries;
}
function paletteSamples(Imagick $image):array {
    $w=$image->getImageWidth();$h=$image->getImageHeight();$rows=[];
    foreach([[0,0],[intdiv($w,4),intdiv($h,2)],[intdiv($w,2),intdiv($h,2)],[intdiv(3*$w,4),intdiv($h,2)],[$w-1,$h-1]] as [$x,$y]){
        $p=$image->getImagePixelColor($x,$y);$rows[]=['xy'=>[$x,$y],'normalized_RGBA'=>$p->getColor(1)];
    }
    return $rows;
}
function paletteSequenceStates(Imagick $sequence,bool $includePalette=true):array {
    $index=$sequence->getIteratorIndex();$sequence->setFirstIterator();$rows=[];
    foreach($sequence as $frame){$row=['state'=>paletteState($frame),'samples'=>paletteSamples($frame)];if($includePalette){$row['palette']=paletteEntries($frame);}$rows[]=$row;}
    $sequence->setIteratorIndex($index);return $rows;
}
function palettePreview(Imagick $image,string $background='white'):Imagick {
    $view=new Imagick();$view->newImage($image->getImageWidth(),$image->getImageHeight(),$background);$view->compositeImage($image,Imagick::COMPOSITE_OVER,0,0);$view->setImageType(Imagick::IMGTYPE_TRUECOLOR);return $view;
}
function savePaletteView(Imagick $image,string $filename,bool $alpha=false):void {
    $copy=clone $image;$copy->setImageFormat('png');$copy->setImageDepth(8);$copy->setOption('png:bit-depth','8');$copy->setOption('png:color-type',$alpha?'6':'2');
    if(!$copy->writeImage(__DIR__.'/'.$filename)){throw new RuntimeException('PNG8 view write failed');}
}
function savePaletteIndexed(Imagick $image,string $filename,bool $acceptDiscardedAlpha=false):void {
    $state=paletteState($image);if(!$acceptDiscardedAlpha&&$state['alpha_range']!==[255,255]){throw new RuntimeException('Indexed delivery requires explicit opaque input; alpha discard is allowed only by the labelled control');}
    $copy=clone $image;$copy->setImageFormat('png');$copy->setImageDepth(8);$copy->setOption('png:bit-depth','8');$copy->setOption('png:color-type','3');
    if(!$copy->writeImage(__DIR__.'/'.$filename)){throw new RuntimeException('Indexed PNG write failed');}
}
function savePaletteStrip(Imagick $image,string $filename,int $limit=16):array {
    $entries=paletteEntries($image);$count=min(count($entries),$limit);if($count===0){throw new RuntimeException('Palette strip requires a real colormap');}
    $strip=new Imagick();$strip->newImage($limit*24,48,'white');$draw=new ImagickDraw();$draw->setStrokeColor('none');
    for($i=0;$i<$count;$i++){$draw->setFillColor($image->getImageColormapColor($i));$draw->rectangle($i*24,0,($i+1)*24-1,47);}
    $strip->drawImage($draw);savePaletteView($strip,$filename);
    return ['filename'=>$filename,'display_domain'=>'native palette entries composited OVER white into RGB8','entry_count_shown'=>$count,'palette_entries_total'=>count($entries),'cell_width'=>24,'cell_height'=>48,'display_slots'=>$limit,'unused_display_slots_are_white'=>true];
}
function makePaletteAlphaPattern():Imagick {
    $im=new Imagick();$im->newImage(64,32,'transparent');$draw=new ImagickDraw();$draw->setStrokeColor('none');
    foreach(['rgba(255,40,40,0.25)','rgba(40,210,80,1)','rgba(40,80,255,0.5)','rgba(245,200,40,0.75)'] as $i=>$color){$draw->setFillColor($color);$draw->rectangle($i*16,0,($i+1)*16-1,31);}
    $im->drawImage($draw);return $im;
}
function savePaletteComparison(array $panels,string $filename):array {
    $width=array_sum(array_map(fn($im)=>$im->getImageWidth(),$panels));$height=max(array_map(fn($im)=>$im->getImageHeight(),$panels));$board=new Imagick();$board->newImage($width,$height,'white');$x=0;$rects=[];
    foreach($panels as $p){$y=intdiv($height-$p->getImageHeight(),2);$board->compositeImage($p,Imagick::COMPOSITE_OVER,$x,$y);$rects[]=['x'=>$x,'y'=>$y,'width'=>$p->getImageWidth(),'height'=>$p->getImageHeight()];$x+=$p->getImageWidth();}
    savePaletteView($board,$filename);return $rects;
}
function savePaletteDiagnostic(string $id,array $data):void {
    $doc=['schema'=>'aen-palette-sequence-program-diagnostics-v1','example_id'=>$id]+$data;
    if(file_put_contents(__DIR__.'/metrics.json',json_encode($doc,JSON_PRETTY_PRINT|JSON_PRESERVE_ZERO_FRACTION|JSON_THROW_ON_ERROR)."\n")===false){throw new RuntimeException('Diagnostic write failed');}
}

function paletteAsset(Imagick $image,string $stem,bool $acceptDiscardedAlpha=false):array {
    if($image->getNumberImages()!==1){throw new RuntimeException('Extract the current frame with getImage() before saving an asset');}
    $native=['state'=>paletteState($image),'palette'=>paletteEntries($image),'samples'=>paletteSamples($image)];
    $encoded=$stem.'-indexed.png';$preview=$stem.'-preview.png';$strip=$stem.'-strip.png';
    savePaletteIndexed($image,$encoded,$acceptDiscardedAlpha);$read=new Imagick(__DIR__.'/'.$encoded);
    savePaletteView(palettePreview($read),$preview);$stripInfo=savePaletteStrip($read,$strip);
    return ['encoded_filename'=>$encoded,'decoded_preview_filename'=>$preview,'strip_filename'=>$strip,'accept_discarded_alpha'=>$acceptDiscardedAlpha,'native'=>$native,'reread'=>['state'=>paletteState($read),'palette'=>paletteEntries($read),'samples'=>paletteSamples($read)],'strip'=>$stripInfo];
}
function paletteAssetView(array $asset):Imagick {return new Imagick(__DIR__.'/'.$asset['decoded_preview_filename']);}

restorePaletteInput восстанавливает source.png с SHA-проверкой. palettePreview делает явный OVER фон и TRUECOLOR. paletteAsset принимает один getImage-кадр, сохраняет настоящий PNG3, читает его снова, создаёт RGB8preview и native полоску первых до 16 palette entries. Незаполненные display ячейки полоски белые и не относятся к PLTE. Для прозрачного ввода обычная доставка откажется; только явно подписанный контроль разрешает forcedalpha-loss. Изучаемые три метода в helper не спрятаны.

Применить понятный цветовой сценарий в своём проекте

Выбор задачи: группы цветов, общая палитра, художественный сдвиг или явное RGBA сохранение
Выбор задачи: группы цветов, общая палитра, художественный сдвиг или явное RGBA сохранение
Нужный результатВыборСледующее действие
Плакатные цветовые областиsegmentImageСравнить пресеты на своём входе; это не маска предмета
Набор иллюстраций с общим native наборомquantizeImages от firstiteratorИзвлечь каждый getImage, проверить отдельные savedfiles
Независимое решение на изображениеquantizeImageСравнить с совместной политикой, не обещать лучшее качество
Необычные циклические оттенкиcycleColormapImage наявной paletteУчитывать порядок/alpha/полный nativeperiod
Важна исходная прозрачностьЯвный RGBA путь и сравнение alphaНе принуждать color-type=3 без провереннойполитики

Упражнение: подготовьте три версии своей иллюстрации: исходная на белом, сегментация 5/5 и выбранный native palette сдвиг. Сохраните раздельные файлы. Для набора добавьте второй действительно отличный кадр, явно вернитесь к firstiterator и извлеките каждый getImage. Решение уже заложено в первом и sequence сценариях; менять число цветов можно только с последующей проверкой видимого результата/alpha и фактического PLTE.

Для сайта эти файлы передаются отдельным HTTP ответом: Content-Type соответствует формату, до бинарного содержимого не выводится echo. CLI имена файлов и metrics.json помогают воспроизвести результат локально. Дальше: палитры и remap, типы растра и PNG, фон/alpha, цветовые FX и границы сравнения, работа с файлами. Все runtime особенности здесь привязаны к PHP8.5.11, Imagick3.8.1 и ImageMagick7.1.1-46; обещаний одинаковой скорости для всех изображений нет.

Функции и методы этой темы

Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.

Imagick::segmentImageВозвращает true при успехе и меняет текущее изображение; при native отказе выбрасывается ImagickException.

PECL imagick 2 >= 2.3.0, PECL imagick 3 · imagick

Imagick::segmentImage(int $colorspace, float $cluster_threshold, float $smooth_threshold, bool $verbose=false): bool

Понятие и самый маленький вызов

Растр — сетка пикселей. RGB — три цветовые компоненты, alpha — прозрачность. Палитра — небольшая пронумерованная таблица цветов; индексированный пиксель хранит номер ячейки. $image — переменная с объектом; = присваивает, -> вызывает метод, () содержит аргументы через запятую, ; заканчивает инструкцию. clone создаёт рабочую копию. true/false — логические значения. Переменные, объекты и методы, фон и alpha помогут повторить предпосылки.

Объединить близкие цвета по цветовым гистограммам: настоящие изображения соответствующих программ
Объединить близкие цвета по цветовым гистограммам: настоящие изображения соответствующих программ
$ok = $image->segmentImage(Imagick::COLORSPACE_RGB, 5, 5, false);

Один метод получает пространство анализа и два порога. Два числа 5 относятся к разным стадиям; это не 5 цветов и не 5 пикселей. false запрещает подробную печать классов. $ok — статус true; изображение меняется внутри $image. Чтобы получить файл, дальше отдельно выбирают формат и вызывают writeImage. Кружка остаётся узнаваемой, но алгоритм не знает, что это кружка.

Полный базовый контракт

Объединить близкие цвета по цветовым гистограммам

Imagick::segmentImage(int $colorspace, float $cluster_threshold, float $smooth_threshold, bool $verbose=false): bool
АргументНазначение и границы
$colorspace: intПространство анализа из Imagick::COLORSPACE_*. Вызов использует RGB; после обработки Core возвращает исходное colorspace.
$cluster_threshold: floatВ документации — процент минимального числа пикселей в допустимой цветовой области hexahedra. Это не число классов и не простое правило «убрать все участки меньше 5% картинки».
$smooth_threshold: floatСглаживание шума во второй производной цветовых гистограмм. Это не пространственный blur-радиус. В Core0 заменяется на 1 для этой стадии.
$verbose: bool=falseПечатать подробности распознанных цветовых классов. false оставляет stdout программы под контролем.

Возвращает true при успехе и меняет текущее изображение; при native отказе выбрасывается ImagickException.

Доступность официальной записи: PECL imagick 2 >= 2.3.0, PECL imagick 3. Все аргументы примеров передаются позиционно. Исходные XML-имена сохранены ниже; актуальные имена параметров binding важны, если используются named arguments.

Полное официальное описание: метод анализирует изображение и находит сходные единицы; требует сборку против ImageMagick6.4.5 или новее. В XML имя COLORSPACE записано заглавными, в stub/Reflection этой Imagick3.8.1 — colorspace. Returnvalues в XML пуст, но stub и C-обёртка возвращают bool true. Цветовые классы вычисляются из гистограмм методом fuzzy c-Means. Официальное процентное описание cluster_threshold не превращаем в обещание удаления каждой пространственной области меньше заданного процента: exact Classify имеет собственный механизм кластеров. Два пресета ниже наблюдаются на одном входе; число классов не обещано монотонно зависимым от этих чисел.

Проверяйте ошибку на нужной стадии: чтение файла, выполнение метода и запись имеют отдельные зависимости. try/catch(ImagickException $e) перехватывает отказ Imagick, а неверный PHP-тип может быть TypeError. Пустые объекты во всех трёх реальных контролях дали ImagickException: Can not process empty Imagick object. Не передавайте произвольные огромные размеры/списки; квантование анализирует пиксели всех выбранных кадров и требует памяти. Универсального обещания скорости, меньшего файла или сохранения alpha здесь нет.

Полный исходный официальный пример 1


<?php
function segmentImage($imagePath, $colorSpace, $clusterThreshold, $smoothThreshold) {
    $imagick = new \Imagick(realpath($imagePath));
    $imagick->segmentImage($colorSpace, $clusterThreshold, $smoothThreshold);
    header("Content-Type: image/jpg");
    echo $imagick->getImageBlob();
}

segmentImage($imagePath, \Imagick::COLORSPACE_RGB, 5, 5);

?>

В оригинальном segmentImage $imagePath должен быть определён вызывающим кодом, файл должен существовать; realpath при неудаче не даёт корректный путь. header("Content-Type: image/jpg") сам не преобразует формат getImageBlob. Для нашего исходного PNG выбирайте настоящий PNG MIME и формат; CLI ниже сохраняет файл и печатает диагностику отдельно от бинарного HTTP-ответа. Полный исходный пример оставлен без исправления задним числом.

Полный исходный XML: описание, параметры, возврат, ошибки и пример
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->

<refentry xml:id="imagick.segmentimage" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::segmentImage</refname>
  <refpurpose>Segments an image</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::segmentImage</methodname>
   <methodparam><type>int</type><parameter>COLORSPACE</parameter></methodparam>
   <methodparam><type>float</type><parameter>cluster_threshold</parameter></methodparam>
   <methodparam><type>float</type><parameter>smooth_threshold</parameter></methodparam>
   <methodparam choice="opt"><type>bool</type><parameter>verbose</parameter><initializer>&false;</initializer></methodparam>
  </methodsynopsis>
  <para>
   Analyses the image and identifies units that are similar. &imagick.method.available.0x645;
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>COLORSPACE</parameter></term>
     <listitem>
      <para>
       One of the <link linkend="imagick.constants.colorspace">COLORSPACE constants</link>.
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>cluster_threshold</parameter></term>
     <listitem>
      <para>
       A percentage describing minimum number of pixels 
       contained in hexedra before it is considered valid.
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>smooth_threshold</parameter></term>
     <listitem>
      <para>
       Eliminates noise from the histogram.
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>verbose</parameter></term>
     <listitem>
      <para>
       Whether to output detailed information about recognised classes.
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
  </para>
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>

  </para>
 </refsect1>




 <refsect1 role="examples">
  &reftitle.examples;
  <para>
    <example>
      <title> <function>Imagick::segmentImage</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
function segmentImage($imagePath, $colorSpace, $clusterThreshold, $smoothThreshold) {
    $imagick = new \Imagick(realpath($imagePath));
    $imagick->segmentImage($colorSpace, $clusterThreshold, $smoothThreshold);
    header("Content-Type: image/jpg");
    echo $imagick->getImageBlob();
}

segmentImage($imagePath, \Imagick::COLORSPACE_RGB, 5, 5);

?>
]]>
      </programlisting>
    </example>
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

Сгруппировать цвета для плакатной версии товара

Один явный вызов создаёт плакатный вид без изменения ширины и высоты. Он ищет цветовые группы гистограммы, а не распознаёт предметы и не выдаёт маску кружки.

Сгруппировать цвета для плакатной версии товараПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$poster=clone $sourceWhite;
$ok=$poster->segmentImage(Imagick::COLORSPACE_RGB,5,5,false);
savePaletteView(palettePreview($poster),'poster-preview.png');
$checks['return_true']=$ok===true;$checks['same_geometry']=$poster->getImageWidth()===400&&$poster->getImageHeight()===320;

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,new Imagick(__DIR__.'/poster-preview.png')], 'segment-minimal.png');
savePaletteDiagnostic('palette-sequence-segmentimage-minimal', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['return_type'=>get_debug_type($ok),'return_value'=>$ok,'parameters'=>['colorspace'=>Imagick::COLORSPACE_RGB,'cluster_threshold'=>5.0,'smooth_threshold'=>5.0,'verbose'=>false],'before_state'=>paletteState($sourceWhite),'after_state'=>paletteState($poster)]);
echo "segment-minimal.png\nposter-preview.png\nmetrics.json\n";
echo "segment=1 geometry-kept=1 source-unchanged=1\n";

Один явный вызов создаёт плакатный вид без изменения ширины и высоты. Он ищет цветовые группы гистограммы, а не распознаёт предметы и не выдаёт маску кружки. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
segment-minimal.png
poster-preview.png
metrics.json
segment=1 geometry-kept=1 source-unchanged=1

Выбрать стиль группировки и подготовить две индексированные версии

Два настоящих результата позволяют выбрать мягкую или крупную цветовую группировку. Затем отдельная quantizeImages готовит пару индексированных доставок; segmentation и palette quantization остаются разными операциями.

Выбрать стиль группировки и подготовить две индексированные версииПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$presets=['gentle'=>[0.5,0.5],'bold'=>[5.0,5.0]];$views=[];$states=[];$sequence=new Imagick();
foreach($presets as $name=>[$cluster,$smooth]){$poster=clone $sourceWhite;$ok=$poster->segmentImage(Imagick::COLORSPACE_RGB,$cluster,$smooth,false);$checks[$name.'_return']=$ok===true;$states[$name]=paletteState($poster);savePaletteView(palettePreview($poster),$name.'-segmented.png');$views[$name]=new Imagick(__DIR__.'/'.$name.'-segmented.png');$sequence->addImage($poster);}
$sequence->setFirstIterator();$ok=$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$quantizedStates=paletteSequenceStates($sequence);$checks['quantize_return']=$ok===true;
$sequence->setFirstIterator();foreach($sequence as $index=>$frame){$current=$sequence->getImage();$assets['poster-frame'.$index]=paletteAsset($current,'poster-frame'.$index);$checks['detached_frame'.$index]=$assets['poster-frame'.$index]['native']['state']===$quantizedStates[$index]['state'];}

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,$views['gentle'],$views['bold'],paletteAssetView($assets['poster-frame0']),paletteAssetView($assets['poster-frame1'])], 'segment-workflow.png');
savePaletteDiagnostic('palette-sequence-segmentimage-workflow', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['presets'=>$presets,'segmented_states'=>$states,'sequence_states'=>$quantizedStates]);
echo "segment-workflow.png\ngentle-segmented.png\nbold-segmented.png\nposter-frame0-indexed.png\nposter-frame0-preview.png\nposter-frame0-strip.png\nposter-frame1-indexed.png\nposter-frame1-preview.png\nposter-frame1-strip.png\nmetrics.json\n";
echo "presets=2 indexed-deliveries=2 source-unchanged=1\n";

Два настоящих результата позволяют выбрать мягкую или крупную цветовую группировку. Затем отдельная quantizeImages готовит пару индексированных доставок; segmentation и palette quantization остаются разными операциями. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
segment-workflow.png
gentle-segmented.png
bold-segmented.png
poster-frame0-indexed.png
poster-frame0-preview.png
poster-frame0-strip.png
poster-frame1-indexed.png
poster-frame1-preview.png
poster-frame1-strip.png
metrics.json
presets=2 indexed-deliveries=2 source-unchanged=1
Imagick::quantizeImagesМеняет кадры начиная с текущего и возвращает true; при отказе ImagickException. Для всего списка сначала явно setFirstIterator().

PECL imagick 2, PECL imagick 3 · imagick

Imagick::quantizeImages(int $number_colors, int $colorspace, int $tree_depth, bool $dither, bool $measure_error): bool

Понятие и самый маленький вызов

Растр — сетка пикселей. RGB — три цветовые компоненты, alpha — прозрачность. Палитра — небольшая пронумерованная таблица цветов; индексированный пиксель хранит номер ячейки. $image — переменная с объектом; = присваивает, -> вызывает метод, () содержит аргументы через запятую, ; заканчивает инструкцию. clone создаёт рабочую копию. true/false — логические значения. Переменные, объекты и методы, фон и alpha помогут повторить предпосылки.

Рассчитать цвета для последовательности от текущего изображения вперёд: настоящие изображения соответствующих программ
Рассчитать цвета для последовательности от текущего изображения вперёд: настоящие изображения соответствующих программ
$sequence = new Imagick();
$sequence->addImage($first);
$sequence->addImage($second);
$sequence->setFirstIterator();
$ok = $sequence->quantizeImages(16, Imagick::COLORSPACE_SRGB, 0, true, false);

$first и $second здесь уже прочитанные и подготовленные изображения. addImage добавляет их в список, setFirstIterator выбирает начало этого списка. 16 ограничивает размер рассчитываемой палитры,0 просит автоматическую глубину. В этой IM7-сборке true даёт NoDither; последний false отключает расчёт ошибки. После операции foreach выбирает кадры по очереди; $sequence->getImage() извлекает отдельный текущий растр для сохранения. Это важно: clone объекта, содержащего несколько кадров, нельзя выдавать за извлечение выбранного кадра.

Полный базовый контракт

Рассчитать цвета для последовательности от текущего изображения вперёд

Imagick::quantizeImages(int $number_colors, int $colorspace, int $tree_depth, bool $dither, bool $measure_error): bool
АргументНазначение и границы
$number_colors: intЖелаемый предел палитры; не обещание ровно столько реально используемых цветов. В exact Core0 означает MaxColormapSize, большие значения ограничиваются максимумом.
$colorspace: intПространство, в котором строится цветовая квантизация. Для уже подготовленного sRGB здесь используется COLORSPACE_SRGB.
$tree_depth: intГлубина дерева квантования. В этой реализации 0 включает автоматический выбор; явная ненулевая глубина ограничивается диапазоном 2..8.
$dither: boolАргумент PHP binding. В проверенной Imagick3.8.1/IM7 значение напрямую превращается в enum: true→NoDither, false→Undefined→Floyd–Steinberg. Это важное отличие от обычного ожидания false отключает dithering.
$measure_error: boolПросить native расчёт ошибки квантизации. Метод по-прежнему возвращает bool, а не значение ошибки; не выдумывайте getter метрики.

Меняет кадры начиная с текущего и возвращает true; при отказе ImagickException. Для всего списка сначала явно setFirstIterator().

Доступность официальной записи: PECL imagick 2, PECL imagick 3. Все аргументы примеров передаются позиционно. Исходные XML-имена сохранены ниже; актуальные имена параметров binding важны, если используются named arguments.

В исходном XML описания всех пяти параметров пусты; значения раскрыты выше по exact C/Wand/Core, а исходная запись сохранена целиком. XML использует numberColors/treedepth/measureError, текущий stub — number_colors/tree_depth/measure_error. Пустой объект даёт ImagickException. Core анализирует текущий кадр и последующие NEXT; предыдущие не входят в эту операцию. Совместное дерево классификации назначает кадрам общую native палитру, но каждый сохранённый PNG может переставить/удалить неиспользуемые ячейки PLTE. Поэтому одинаковые номера saved PNG не считаются договором приложения о цветах. number_colors — предел, actual used colors и slots могут различаться.

Dithering распространяет ошибку выбора цвета на соседние пиксели, создавая точечный узор вместо некоторых плавных градаций. В Imagick 3.8.1 с IM7 boolean напрямую передан DitherMethod, поэтому true соответствует enum NoDither=1, false — Undefined=0, который Core направляет в Floyd–Steinberg. Это подтверждено exactsource и отдельным реальным контролем обоих значений. В другой версии сверяйте binding; не исправляйте аргумент случайным приведением enum к bool. measure_error не возвращает RMSE и не доказывает видимое качество. Общая палитра полезна для набора иллюстраций, но не является автоматическим улучшением каждого отдельного кадра.

Проверяйте ошибку на нужной стадии: чтение файла, выполнение метода и запись имеют отдельные зависимости. try/catch(ImagickException $e) перехватывает отказ Imagick, а неверный PHP-тип может быть TypeError. Пустые объекты во всех трёх реальных контролях дали ImagickException: Can not process empty Imagick object. Не передавайте произвольные огромные размеры/списки; квантование анализирует пиксели всех выбранных кадров и требует памяти. Универсального обещания скорости, меньшего файла или сохранения alpha здесь нет.

На официальной странице нет собственного примера. Ниже добавлены минимальное самостоятельное применение и развивающий его рабочий сценарий с настоящими файлами.

Полный исходный XML: описание, параметры, возврат, ошибки и пример
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.quantizeimages">
 <refnamediv>
  <refname>Imagick::quantizeImages</refname>
  <refpurpose>Analyzes the colors within a sequence of images</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::quantizeImages</methodname>
   <methodparam><type>int</type><parameter>numberColors</parameter></methodparam>
   <methodparam><type>int</type><parameter>colorspace</parameter></methodparam>
   <methodparam><type>int</type><parameter>treedepth</parameter></methodparam>
   <methodparam><type>bool</type><parameter>dither</parameter></methodparam>
   <methodparam><type>bool</type><parameter>measureError</parameter></methodparam>
  </methodsynopsis>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>numberColors</parameter></term>
     <listitem>
      <para>
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>colorspace</parameter></term>
     <listitem>
      <para>
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>treedepth</parameter></term>
     <listitem>
      <para>
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>dither</parameter></term>
     <listitem>
      <para>
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>measureError</parameter></term>
     <listitem>
      <para>
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
  </para>
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   &imagick.return.success;
  </para>
 </refsect1>

 <refsect1 role="errors">
  &reftitle.errors;
  <para>
   &imagick.imagick.throws;
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

Рассчитать палитру для двух разных иллюстраций

В объект добавлены два действительно разных изображения. setFirstIterator выбирает начало перед совместным анализом, getImage извлекает именно текущий кадр для записи. Оба сохранённых результата должны различаться и соответствовать своим native состояниям.

Рассчитать палитру для двух разных иллюстрацийПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$negative=$sourceWhite->fxImage('1-u',$rgb);savePaletteView(palettePreview($negative),'negative-input.png');
$sequence=new Imagick();$sequence->addImage($sourceWhite);$sequence->addImage($negative);
$sequence->setFirstIterator();
$ok=$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);
$states=paletteSequenceStates($sequence);$sequence->setFirstIterator();
foreach($sequence as $index=>$frame){$current=$sequence->getImage();$assets['joint-frame'.$index]=paletteAsset($current,'joint-frame'.$index);$checks['detached_frame'.$index]=$assets['joint-frame'.$index]['native']['state']===$states[$index]['state'];}
$checks['return_true']=$ok===true;$checks['two_frames']=$sequence->getNumberImages()===2;$checks['native_pair_distinct']=$states[0]['state']['RGBA8_hash']!==$states[1]['state']['RGBA8_hash'];$checks['saved_pair_distinct']=$assets['joint-frame0']['reread']['state']['RGBA8_hash']!==$assets['joint-frame1']['reread']['state']['RGBA8_hash'];

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,new Imagick(__DIR__.'/negative-input.png'),paletteAssetView($assets['joint-frame0']),paletteAssetView($assets['joint-frame1'])], 'quantize-minimal.png');
savePaletteDiagnostic('palette-sequence-quantizeimages-minimal', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['return_type'=>get_debug_type($ok),'return_value'=>$ok,'sequence_states'=>$states,'parameters'=>['number_colors'=>16,'colorspace'=>Imagick::COLORSPACE_SRGB,'tree_depth'=>0,'dither'=>true,'measure_error'=>false]]);
echo "quantize-minimal.png\nnegative-input.png\njoint-frame0-indexed.png\njoint-frame0-preview.png\njoint-frame0-strip.png\njoint-frame1-indexed.png\njoint-frame1-preview.png\njoint-frame1-strip.png\nmetrics.json\n";
echo "sequence=2 detached-files=2 source-unchanged=1\n";

В объект добавлены два действительно разных изображения. setFirstIterator выбирает начало перед совместным анализом, getImage извлекает именно текущий кадр для записи. Оба сохранённых результата должны различаться и соответствовать своим native состояниям. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
quantize-minimal.png
negative-input.png
joint-frame0-indexed.png
joint-frame0-preview.png
joint-frame0-strip.png
joint-frame1-indexed.png
joint-frame1-preview.png
joint-frame1-strip.png
metrics.json
sequence=2 detached-files=2 source-unchanged=1

Сравнить общую и отдельные палитры и не пропустить первый кадр

Набор баннеров можно анализировать совместно либо отдельно. Сравниваются настоящие результаты обеих политик. Отдельный контроль начинает с индекса 1: кадр 0 остаётся прежним. measureError не превращает возвращаемый bool в число ошибки.

Сравнить общую и отдельные палитры и не пропустить первый кадрПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$negative=$sourceWhite->fxImage('1-u',$rgb);$inputs=[$sourceWhite,$negative];
$whole=new Imagick();foreach($inputs as $input){$whole->addImage($input);}$whole->setFirstIterator();$ok=$whole->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,true);$wholeStates=paletteSequenceStates($whole);$checks['whole_return']=$ok===true;
$whole->setFirstIterator();foreach($whole as $index=>$frame){$current=$whole->getImage();$assets['whole-frame'.$index]=paletteAsset($current,'whole-frame'.$index);$checks['detached_whole'.$index]=$assets['whole-frame'.$index]['native']['state']===$wholeStates[$index]['state'];}
$independentStates=[];foreach($inputs as $index=>$input){$single=clone $input;$ok=$single->quantizeImage(16,Imagick::COLORSPACE_SRGB,0,true,true);$checks['single_return'.$index]=$ok===true;$independentStates[]=paletteState($single);$assets['single-frame'.$index]=paletteAsset($single,'single-frame'.$index);}
$suffix=new Imagick();foreach($inputs as $input){$suffix->addImage($input);}$suffix->setIteratorIndex(1);$suffixBefore=paletteSequenceStates($suffix,false);$ok=$suffix->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$suffixAfter=paletteSequenceStates($suffix);$checks['suffix_return']=$ok===true;$checks['previous_frame_unchanged']=$suffixBefore[0]['state']===$suffixAfter[0]['state'];
$suffix->setFirstIterator();$untouched=$suffix->getImage();savePaletteView(palettePreview($untouched),'suffix-frame0.png');$suffix->setIteratorIndex(1);$current=$suffix->getImage();$assets['suffix-frame1']=paletteAsset($current,'suffix-frame1');$checks['detached_suffix1']=$assets['suffix-frame1']['native']['state']===$suffixAfter[1]['state'];
$checks['saved_whole_pair_distinct']=$assets['whole-frame0']['reread']['state']['RGBA8_hash']!==$assets['whole-frame1']['reread']['state']['RGBA8_hash'];

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,paletteAssetView($assets['whole-frame0']),paletteAssetView($assets['whole-frame1']),paletteAssetView($assets['single-frame0']),paletteAssetView($assets['single-frame1'])], 'quantize-workflow.png');
savePaletteDiagnostic('palette-sequence-quantizeimages-workflow', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['whole_states'=>$wholeStates,'independent_states'=>$independentStates,'suffix_before'=>$suffixBefore,'suffix_after'=>$suffixAfter,'measure_error_return_is_bool'=>get_debug_type($ok),'whole_native_palettes_equal'=>$wholeStates[0]['palette']===$wholeStates[1]['palette']]);
echo "quantize-workflow.png\nwhole-frame0-indexed.png\nwhole-frame0-preview.png\nwhole-frame0-strip.png\nwhole-frame1-indexed.png\nwhole-frame1-preview.png\nwhole-frame1-strip.png\nsingle-frame0-indexed.png\nsingle-frame0-preview.png\nsingle-frame0-strip.png\nsingle-frame1-indexed.png\nsingle-frame1-preview.png\nsingle-frame1-strip.png\nsuffix-frame0.png\nsuffix-frame1-indexed.png\nsuffix-frame1-preview.png\nsuffix-frame1-strip.png\nmetrics.json\n";
echo "whole=2 independent=2 suffix-start=1 earlier-frame-unchanged=1 source-unchanged=1\n";

Набор баннеров можно анализировать совместно либо отдельно. Сравниваются настоящие результаты обеих политик. Отдельный контроль начинает с индекса 1: кадр 0 остаётся прежним. measureError не превращает возвращаемый bool в число ошибки. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
quantize-workflow.png
whole-frame0-indexed.png
whole-frame0-preview.png
whole-frame0-strip.png
whole-frame1-indexed.png
whole-frame1-preview.png
whole-frame1-strip.png
single-frame0-indexed.png
single-frame0-preview.png
single-frame0-strip.png
single-frame1-indexed.png
single-frame1-preview.png
single-frame1-strip.png
suffix-frame0.png
suffix-frame1-indexed.png
suffix-frame1-preview.png
suffix-frame1-strip.png
metrics.json
whole=2 independent=2 suffix-start=1 earlier-frame-unchanged=1 source-unchanged=1
Imagick::cycleColormapImageИзменяет текущий растр и возвращает true. Native таблица остаётся прежней; индекс каждого пикселя сдвигается и его RGBA берётся из выбранной ячейки. При отказе ImagickException.

PECL imagick 2, PECL imagick 3 · imagick

Imagick::cycleColormapImage(int $displace): bool

Понятие и самый маленький вызов

Растр — сетка пикселей. RGB — три цветовые компоненты, alpha — прозрачность. Палитра — небольшая пронумерованная таблица цветов; индексированный пиксель хранит номер ячейки. $image — переменная с объектом; = присваивает, -> вызывает метод, () содержит аргументы через запятую, ; заканчивает инструкцию. clone создаёт рабочую копию. true/false — логические значения. Переменные, объекты и методы, фон и alpha помогут повторить предпосылки.

Сдвинуть выбор цвета каждого палитрового пикселя: настоящие изображения соответствующих программ
Сдвинуть выбор цвета каждого палитрового пикселя: настоящие изображения соответствующих программ
$ok = $paletted->cycleColormapImage(1);

$paletted уже подготовлен как палитровое изображение. +1 сдвигает native индекс пикселя к следующей ячейке; после последней выбирается первая. Рисунок получает другую раскраску. Сама таблица не вращается. До вызова на произвольном DirectClass Core может неявно перевести его в PaletteType; поэтому учебный сценарий явно квантирует копию заранее. Даже displacement=0 не обязан быть нейтральным, если перед ним требуется такое преобразование.

Полный базовый контракт

Сдвинуть выбор цвета каждого палитрового пикселя

Imagick::cycleColormapImage(int $displace): bool
АргументНазначение и границы
$displace: intЧисло позиций. +1 выбирает следующую ячейку, −1 предыдущую; остаток по числу native ячеек замыкает обход. Это не градусы hue и не проценты яркости.

Изменяет текущий растр и возвращает true. Native таблица остаётся прежней; индекс каждого пикселя сдвигается и его RGBA берётся из выбранной ячейки. При отказе ImagickException.

Доступность официальной записи: PECL imagick 2, PECL imagick 3. Все аргументы примеров передаются позиционно. Исходные XML-имена сохранены ниже; актуальные имена параметров binding важны, если используются named arguments.

Официальная фраза «сдвигает colormap» описывает эффект, а exact Core делает конкретнее: index=(old_index+displace)%image->colors, отрицательный остаток нормализуется; далее пиксель получает RGBA соответствующей неизменённой colormap-ячейки. Это действует и на alpha в этой ячейке. Native count slots берётся по colormap, а getImageColors сообщает число используемых различных цветов — эти числа нельзя бездумно подставлять друг вместо друга. Цветовой порядок заранее не гарантируется; эффект художественный, а не адресная замена #rrggbb.

Проверяйте ошибку на нужной стадии: чтение файла, выполнение метода и запись имеют отдельные зависимости. try/catch(ImagickException $e) перехватывает отказ Imagick, а неверный PHP-тип может быть TypeError. Пустые объекты во всех трёх реальных контролях дали ImagickException: Can not process empty Imagick object. Не передавайте произвольные огромные размеры/списки; квантование анализирует пиксели всех выбранных кадров и требует памяти. Универсального обещания скорости, меньшего файла или сохранения alpha здесь нет.

На официальной странице нет собственного примера. Ниже добавлены минимальное самостоятельное применение и развивающий его рабочий сценарий с настоящими файлами.

Полный исходный XML: описание, параметры, возврат, ошибки и пример
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.cyclecolormapimage">
 <refnamediv>
  <refname>Imagick::cycleColormapImage</refname>
  <refpurpose>Displaces an image's colormap</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::cycleColormapImage</methodname>
   <methodparam><type>int</type><parameter>displace</parameter></methodparam>
  </methodsynopsis>
  <para>
   Displaces an image's colormap by a given number of positions.  If you
   cycle the colormap a number of times you can produce a psychedelic
   effect.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>displace</parameter></term>
     <listitem>
      <para>
      The amount to displace the colormap.
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
  </para>
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   &imagick.return.success;
  </para>
 </refsect1>

 <refsect1 role="errors">
  &reftitle.errors;
  <para>
   &imagick.imagick.throws;
  </para>
 </refsect1>

</refentry>

<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:t
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
indent-tabs-mode:nil
sgml-parent-document:nil
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:nil
sgml-local-ecat-files:nil
End:
vim600: syn=xml fen fdm=syntax fdl=2 si
vim: et tw=78 syn=sgml
vi: ts=1 sw=1
-->

Получить необычный цветовой вариант из явной палитры

Новый вариант использует следующий цвет таблицы для каждого пикселя. Native таблица цветов остаётся прежней; меняется выбор ячейки. Это художественная перестановка, а не осмысленная замена заданного красного на заданный синий.

Получить необычный цветовой вариант из явной палитрыПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$sequence=new Imagick();$sequence->addImage($sourceWhite);$sequence->setFirstIterator();$ok=$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$base=$sequence->getImage();$assets['base']=paletteAsset($base,'base');
$variant=clone $base;$beforePalette=paletteEntries($variant);$cycleOK=$variant->cycleColormapImage(1);$assets['shift-one']=paletteAsset($variant,'shift-one');
$checks['operations_return_true']=$ok&&$cycleOK;$checks['native_palette_unchanged']=$beforePalette===paletteEntries($variant);$checks['visible_raster_changed']=paletteHash($base)!==paletteHash($variant);

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,paletteAssetView($assets['base']),paletteAssetView($assets['shift-one'])], 'cycle-minimal.png');
savePaletteDiagnostic('palette-sequence-cyclecolormapimage-minimal', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['cycle_return_type'=>get_debug_type($cycleOK),'cycle_return_value'=>$cycleOK,'displace'=>1,'native_before'=>paletteState($base),'native_after'=>paletteState($variant),'palette_before'=>$beforePalette,'palette_after'=>paletteEntries($variant)]);
echo "cycle-minimal.png\nbase-indexed.png\nbase-preview.png\nbase-strip.png\nshift-one-indexed.png\nshift-one-preview.png\nshift-one-strip.png\nmetrics.json\n";
echo "displace=1 native-colormap-unchanged=1 source-unchanged=1\n";

Новый вариант использует следующий цвет таблицы для каждого пикселя. Native таблица цветов остаётся прежней; меняется выбор ячейки. Это художественная перестановка, а не осмысленная замена заданного красного на заданный синий. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
cycle-minimal.png
base-indexed.png
base-preview.png
base-strip.png
shift-one-indexed.png
shift-one-preview.png
shift-one-strip.png
metrics.json
displace=1 native-colormap-unchanged=1 source-unchanged=1

Собрать обратимые цветовые варианты и вычислить полный период

Коллекция вариантов строится из одной detached-копии. Отрицательный сдвиг идёт назад; полный период считается по числу native ячеек, а не числу различных видимых цветов. +1 и−1 возвращают native RGBA этого подготовленного растра. Порядок saved PLTE проверяется отдельно.

Собрать обратимые цветовые варианты и вычислить полный периодПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/palette-sequence-fixture.php';
restorePaletteInput();
$source=new Imagick(__DIR__.'/source.png');
$before=paletteState($source);$sourceWhite=palettePreview($source);
$rgb=Imagick::CHANNEL_RED|Imagick::CHANNEL_GREEN|Imagick::CHANNEL_BLUE;
$assets=[];$checks=[];
$sequence=new Imagick();$sequence->addImage($sourceWhite);$sequence->setFirstIterator();$sequence->quantizeImages(16,Imagick::COLORSPACE_SRGB,0,true,false);$base=$sequence->getImage();$palette=paletteEntries($base);$period=count($palette);$assets['variant-base']=paletteAsset($base,'variant-base');$states=[];
foreach(['minus-one'=>-1,'plus-one'=>1,'period'=>$period] as $name=>$displace){$variant=clone $base;$ok=$variant->cycleColormapImage($displace);$checks[$name.'_return']=$ok===true;$checks[$name.'_palette']=$palette===paletteEntries($variant);$states[$name]=['displace'=>$displace,'state'=>paletteState($variant)];$assets[$name]=paletteAsset($variant,$name);}
$inverse=clone $base;$inverse->cycleColormapImage(1);$inverse->cycleColormapImage(-1);$checks['inverse_native_RGBA_exact']=paletteHash($inverse)===paletteHash($base);$checks['period_native_RGBA_exact']=$states['period']['state']['RGBA8_hash']===paletteHash($base);

$checks['source_unchanged']=$before===paletteState($source);
foreach($checks as $name=>$ok){if(!$ok){throw new RuntimeException('Check failed: '.$name);}}
$rects=savePaletteComparison([$sourceWhite,paletteAssetView($assets['variant-base']),paletteAssetView($assets['minus-one']),paletteAssetView($assets['plus-one'])], 'cycle-variants.png');
savePaletteDiagnostic('palette-sequence-cyclecolormapimage-workflow', ['source_before'=>$before,'source_after'=>paletteState($source),'panel_geometry'=>$rects,'assets'=>$assets,'checks'=>$checks]+['native_base'=>paletteState($base),'native_palette'=>$palette,'native_palette_slots'=>$period,'variants'=>$states,'inverse_state'=>paletteState($inverse)]);
echo "cycle-variants.png\nvariant-base-indexed.png\nvariant-base-preview.png\nvariant-base-strip.png\nminus-one-indexed.png\nminus-one-preview.png\nminus-one-strip.png\nplus-one-indexed.png\nplus-one-preview.png\nplus-one-strip.png\nperiod-indexed.png\nperiod-preview.png\nperiod-strip.png\nmetrics.json\n";
echo "variants=3 inverse-exact=1 period-exact=1 source-unchanged=1\n";

Коллекция вариантов строится из одной detached-копии. Отрицательный сдвиг идёт назад; полный период считается по числу native ячеек, а не числу различных видимых цветов. +1 и−1 возвращают native RGBA этого подготовленного растра. Порядок saved PLTE проверяется отдельно. Сохраните example.php и два дополнительных текстовых файла из архива в одной папке. Запустите php example.php с Imagick и PNG. Helper проверяет и восстанавливает собственный source.png; нужна запись в эту папку. Основные изображения и превью — настоящие PNG8/RGB либо явно названные PNG8/RGBA. Отдельные *-indexed.png — настоящие индексированные PNG3; они читаются снова самим Imagick, после чего сохраняется RGB8-превью. Helper не вызывает segmentImage, quantizeImages, quantizeImage или cycleColormapImage; все эти вызовы видны в программе.

Ожидаемый вывод и созданные файлы
cycle-variants.png
variant-base-indexed.png
variant-base-preview.png
variant-base-strip.png
minus-one-indexed.png
minus-one-preview.png
minus-one-strip.png
plus-one-indexed.png
plus-one-preview.png
plus-one-strip.png
period-indexed.png
period-preview.png
period-strip.png
metrics.json
variants=3 inverse-exact=1 period-exact=1 source-unchanged=1
Источники и обсуждения

Код и руководство с собой

Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.

Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.

Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.