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

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

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

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

Глава 372 · После переменных, функций, массивов, циклов, объектов и пикселей

Imagick: цветовая формула FX, сохранённый Hald look и измеримая разница

Получить реальные изображения, сохранить собственный color→color рецепт, повторно применить LUT и объяснить отличие native Q16 сравнения от PNG8 просмотра.

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

Нужно одинаково осветлить цвет карточек товара, сохранить собственный рецепт отдельным файлом и повторно применить его. Сначала получим три настоящих изображения: исходник, прямой результат формулы и результат из сохранённой Hald-таблицы. Рецепт и его повторное применение могут немного различаться: таблица хранит конечную цветовую сетку, а не бесконечную формулу. Увидеть итог и проверить это отличие — часть задачи.

Собственный исходник → прямой FX → прочитанный с диска Hald16: полные реальные PNG и отдельно измеренная разница
Собственный исходник → прямой FX → прочитанный с диска Hald16: полные реальные PNG и отдельно измеренная разница
Собственный RGBA-исходник 400×320, показанный на белом фоне; прозрачность проверяется отдельно
Собственный RGBA-исходник 400×320, показанный на белом фоне; прозрачность проверяется отдельно

Самое простое действие до полной программы

Растр — сетка пикселей. RGB означает красную, зелёную и синюю компоненты, RGBA добавляет alpha: 0 прозрачно, максимум непрозрачен. $image — переменная с объектом Imagick; $ начинает имя, = присваивает, -> вызывает метод объекта, () содержит аргументы, ; заканчивает инструкцию. Строка в кавычках передаётся как данные. new Imagick($path) читает файл, clone создаёт независимую рабочую копию. Imagick:: обращается к именованной константе класса. Подробности: переменные, параметры и возврат, объекты и методы.

$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$result = $image->fxImage('u^0.8', $rgb);

Здесь u — нормированное значение текущего выбранного канала от 0 до 1. Например, 0.25 в степени 0.8 приблизительно равно 0.33: промежуточная компонента становится выше. Символ ^ означает степень именно в языке FX внутри строки, а в PHP вне строки ^ — побитовый XOR; PHP-степень записывается **. Мы не вычисляем PHP-выражение и не обещаем физическую коррекцию экспозиции в линейном свете. Полученный $result — новый объект; для самостоятельного файла его ещё нужно сохранить. Маска $rgb исключает alpha. Сама короткая запись использует уже прочитанный $image; ниже полный архив включает вход и сохранение.

Чтобы рецепт можно было передать другому скрипту, создадим identity Hald — набор исходных RGB-цветов. Применим к этим цветам ту же фиксированную формулу u^0.8, сохраним таблицу look16.png и заново прочитаем. Затем haldClutImage заменит цвета рабочей копии исходника по этой таблице. Она не содержит предмет с исходной фотографии: она содержит соответствия цвет→цвет. getImageDistortion отдельно измерит, насколько повторное применение отличается от прямой формулы.

$identity = new Imagick();
$identity->newPseudoImage(216, 216, 'hald:6');
$recipe = $identity->fxImage('u^0.8', $rgb);
$copy = clone $image;
$ok = $copy->haldClutImage($recipe, $rgb);

В коротком разборе таблица в памяти. Полная программа ниже сохраняет её и применяет именно заново прочитанный файл. newPseudoImage — создание служебного растра HALD coder; level6 даёт сторону 6³=216. $rgb выбирает три цвета, не alpha. Перед применением проверяется одинаковое цветовое пространство. __DIR__ — папка программы; . соединяет части пути. echo печатает текст, printf подставляет %d для целого; (int) показывает bool как 1/0. !== строго сравнивает значение и тип; if и throw останавливают сценарий при нарушенной договорённости.

Все изображения для экрана сохраняются как PNG8. LUT сохраняется PNG16, чтобы не свести цветовую сетку к 8-битным узлам; это проверяется заголовком и реальным Q16-чтением. Служебный metrics.json содержит полные float-значения и сведения о прочитанной таблице. Его число не вычисляется из уменьшенной иллюстрации. Код восстановления входа и сохранения целиком доступен ниже; запуск из архива не требует чужой фотографии или внешнего LUT.

Сохранить собственный цветовой рецепт и применить его к карточке

Программа создаёт настоящие direct.png, look16.png и reapplied.png. Первый результат вычислен формулой, второй — прочитанной с диска Hald-таблицей. Таблица сохраняет Q16 значения в PNG16; её экранное preview — отдельный PNG8. При явной RGB-маске исходная прозрачность сохранена. Численное отличие приближения записано в metrics.json.

Сохранить собственный цветовой рецепт и применить его к карточкеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$before = fxHaldPixelHash($source);
$direct = $source->fxImage($expression, $rgb);
saveFxHaldPNG($direct, 'direct.png', true);
$identity = new Imagick(); $identity->newPseudoImage(216, 216, 'hald:6');
$look = $identity->fxImage($expression, $rgb);
saveFxHaldPNG($look, 'look16.png', false, 16);
$storedLook = new Imagick(__DIR__ . '/look16.png');
if ($storedLook->getImageColorspace() !== $source->getImageColorspace()) { throw new RuntimeException('LUT color space must match'); }
$result = clone $source; $result->haldClutImage($storedLook, $rgb);
saveFxHaldPNG($result, 'reapplied.png', true);
saveFxHaldPNG($storedLook, 'look-preview8.png');
$readDirect = new Imagick(__DIR__ . '/direct.png'); $readResult = new Imagick(__DIR__ . '/reapplied.png');
[$left, $right] = fxHaldMetricViews($direct, $result);
$rmse = $left->getImageDistortion($right, Imagick::METRIC_ROOTMEANSQUAREDERROR);
saveFxHaldPNG($sourceWhite, 'source-preview8.png');
saveFxHaldPNG(fxHaldPreview($readResult, '#243c4d'), 'reapplied-dark.png');
saveFxHaldComparison([$sourceWhite, fxHaldPreview($readDirect), fxHaldPreview($readResult)], 'fx-hald-first.png');
saveFxHaldDiagnostic('fx-hald-first', ['kind'=>'first','expression'=>$expression,'source'=>fxHaldState($source),'direct'=>fxHaldState($direct),'result'=>fxHaldState($result),'tables'=>['look16.png'=>fxHaldTableInfo($storedLook,6,'look-preview8.png',$look)],'metrics'=>['native_Q16_RMSE'=>$rmse],'metric_views'=>[fxHaldState($left),fxHaldState($right)],'LUT16_generated_native_hash'=>fxHaldPixelHash($look)]);
echo "fx-hald-first.png\ndirect.png\nlook16.png\nlook-preview8.png\nreapplied.png\nsource-preview8.png\nreapplied-dark.png\nmetrics.json\n";
printf("new-object=%d source-unchanged=%d LUT16-exact=%d alpha-preserved=%d\n", (int)($direct !== $source), (int)($before === fxHaldPixelHash($source)), (int)(fxHaldPixelHash($look) === fxHaldPixelHash($storedLook)), (int)(fxHaldPixelHash($source,'A',Imagick::PIXEL_CHAR) === fxHaldPixelHash($readResult,'A',Imagick::PIXEL_CHAR)));

Программа создаёт настоящие direct.png, look16.png и reapplied.png. Первый результат вычислен формулой, второй — прочитанной с диска Hald-таблицей. Таблица сохраняет Q16 значения в PNG16; её экранное preview — отдельный PNG8. При явной RGB-маске исходная прозрачность сохранена. Численное отличие приближения записано в metrics.json. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
fx-hald-first.png
direct.png
look16.png
look-preview8.png
reapplied.png
source-preview8.png
reapplied-dark.png
metrics.json
new-object=1 source-unchanged=1 LUT16-exact=1 alpha-preserved=1

Разделить формулу, плотность LUT, точность хранения и alpha

Формула описывает действие непрерывно, а Hald хранит его в узлах цветового куба. Для RGB каждой оси соответствует компонента. Уровень L задаёт N=L² точек на ось; всего N³ узлов укладываются в квадрат L³×L³. Быстрее всего меняется красная координата, затем зелёная, затем синяя. Цвет между узлами восстанавливается интерполяцией, поэтому перенесённый нелинейный рецепт — приближение.

Пиксель проходит фиксированную формулу непосредственно или через сохранённую цветовую сетку: это две реально разные цепочки
Пиксель проходит фиксированную формулу непосредственно или через сохранённую цветовую сетку: это две реально разные цепочки
Реальные PNG8 previews сохранённых LUT; плотность level3/6 отдельно от PNG8/16 точности файлов
Реальные PNG8 previews сохранённых LUT; плотность level3/6 отдельно от PNG8/16 точности файлов
ВариантЦветовой кубРазмер HaldЧто меняется
level3 / PNG169×9×927×27Редкая сетка, 16-битные сохранённые компоненты
level6 / PNG1636×36×36216×216Более плотная сетка с той же битностью
level6 / PNG836×36×36216×216Та же сетка, но компоненты округлены для PNG8

Адрес узла: index=r+N*g+N²*b, x=index%side, y=intdiv(index,side). Это модель порядка хранения RGB-куба, не цветовая координата пикселя фотографии. При level3 красный край — (8,0), зелёный — (18,2), синий — (0,24). После обработки таблицы значения в этих точках уже описывают рецепт. Окно preview показывает реальный декодированный LUT в PNG8: две такие картинки могут выглядеть одинаково, хотя их Q16 узлы различаются.

Во второй программе foreach перебирает три заранее заданных варианта; [$level,$depth] распаковывает два значения массива. ** — степень PHP при вычислении размера, в отличие от ^ внутри FX-строки. Для каждого варианта создаются таблица, файл, повторное чтение, реальный результат и метрика. Переменная $errors хранит результаты по имени варианта. Отдельный запуск haldClutImage без маски демонстрирует опасное для этой задачи изменение alpha; две тёмные панели делают различие видимым.

Разделить плотность цветовой сетки, битность хранения и выбор alpha

Три настоящих результата разделяют два изменения: level3→level6 и PNG16→PNG8 при том же level6. Для выбранной формулы/исходника dense16 даёт меньшую измеренную ошибку. Это частный результат, не универсальная гарантия для любой таблицы. Дополнительный контроль показывает, что default Hald меняет alpha; RGB-маска в этой задаче её сохраняет.

Разделить плотность цветовой сетки, битность хранения и выбор alphaПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$direct = $source->fxImage($expression, $rgb); $errors = []; $tables = []; $results = [];
foreach (['coarse16'=>[3,16], 'fine16'=>[6,16], 'fine8'=>[6,8]] as $name=>[$level,$depth]) {
    $edge = $level ** 3; $identity = new Imagick(); $identity->newPseudoImage($edge, $edge, 'hald:'.$level);
    $look = $identity->fxImage($expression, $rgb);
    saveFxHaldPNG($look, $name.'-lut.png', false, $depth);
    $lut = new Imagick(__DIR__ . '/'.$name.'-lut.png');
    $result = clone $source; $result->haldClutImage($lut, $rgb);
    saveFxHaldPNG($result, $name.'-result.png', true); saveFxHaldPNG($lut, $name.'-preview8.png');
    [$left,$right] = fxHaldMetricViews($direct,$result);
    $errors[$name] = $left->getImageDistortion($right, Imagick::METRIC_ROOTMEANSQUAREDERROR);
    $tables[$name.'-lut.png'] = fxHaldTableInfo($lut,$level,$name.'-preview8.png',$look);
    $results[$name] = $result;
    if ($name === 'fine16') { $fineLut = $lut; }
}
$default = clone $source; $default->haldClutImage($fineLut);
saveFxHaldPNG($default, 'default-mask.png', true);
saveFxHaldPNG(fxHaldPreview($results['fine16'],'#243c4d'), 'rgb-mask-dark.png');
saveFxHaldPNG(fxHaldPreview($default,'#243c4d'), 'default-mask-dark.png');
saveFxHaldComparison([$sourceWhite, fxHaldPreview($direct), fxHaldPreview(new Imagick(__DIR__.'/coarse16-result.png')), fxHaldPreview(new Imagick(__DIR__.'/fine16-result.png'))], 'fx-hald-controls.png');
$alphaSource=fxHaldPixelHash($source,'A',Imagick::PIXEL_CHAR);
saveFxHaldDiagnostic('fx-hald-controls', ['kind'=>'controls','expression'=>$expression,'tables'=>$tables,'metrics'=>['native_Q16_RMSE'=>$errors],'source'=>fxHaldState($source),'explicit_RGB'=>fxHaldState($results['fine16']),'default_mask'=>fxHaldState($default),'alpha_RGB_same'=>$alphaSource===fxHaldPixelHash($results['fine16'],'A',Imagick::PIXEL_CHAR),'alpha_default_same'=>$alphaSource===fxHaldPixelHash($default,'A',Imagick::PIXEL_CHAR)]);
echo "fx-hald-controls.png\ncoarse16-lut.png\ncoarse16-result.png\ncoarse16-preview8.png\nfine16-lut.png\nfine16-result.png\nfine16-preview8.png\nfine8-lut.png\nfine8-result.png\nfine8-preview8.png\ndefault-mask.png\nrgb-mask-dark.png\ndefault-mask-dark.png\nmetrics.json\n";
printf("fine16-better-grid=%d fine16-better-storage=%d RGB-alpha=%d default-alpha=%d\n", (int)($errors['fine16']<$errors['coarse16']), (int)($errors['fine16']<$errors['fine8']), (int)($alphaSource===fxHaldPixelHash($results['fine16'],'A',Imagick::PIXEL_CHAR)), (int)($alphaSource===fxHaldPixelHash($default,'A',Imagick::PIXEL_CHAR)));

Три настоящих результата разделяют два изменения: level3→level6 и PNG16→PNG8 при том же level6. Для выбранной формулы/исходника dense16 даёт меньшую измеренную ошибку. Это частный результат, не универсальная гарантия для любой таблицы. Дополнительный контроль показывает, что default Hald меняет alpha; RGB-маска в этой задаче её сохраняет. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
fx-hald-controls.png
coarse16-lut.png
coarse16-result.png
coarse16-preview8.png
fine16-lut.png
fine16-result.png
fine16-preview8.png
fine8-lut.png
fine8-result.png
fine8-preview8.png
default-mask.png
rgb-mask-dark.png
default-mask-dark.png
metrics.json
fine16-better-grid=1 fine16-better-storage=1 RGB-alpha=1 default-alpha=0

Вычислять цветовую формулу и отличать её от пространственного FX

Самое простое действие до полной программы

Растр — сетка пикселей. RGB означает красную, зелёную и синюю компоненты, RGBA добавляет alpha: 0 прозрачно, максимум непрозрачен. $image — переменная с объектом Imagick; $ начинает имя, = присваивает, -> вызывает метод объекта, () содержит аргументы, ; заканчивает инструкцию. Строка в кавычках передаётся как данные. new Imagick($path) читает файл, clone создаёт независимую рабочую копию. Imagick:: обращается к именованной константе класса. Подробности: переменные, параметры и возврат, объекты и методы.

$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$result = $image->fxImage('u^0.8', $rgb);

Здесь u — нормированное значение текущего выбранного канала от 0 до 1. Например, 0.25 в степени 0.8 приблизительно равно 0.33: промежуточная компонента становится выше. Символ ^ означает степень именно в языке FX внутри строки, а в PHP вне строки ^ — побитовый XOR; PHP-степень записывается **. Мы не вычисляем PHP-выражение и не обещаем физическую коррекцию экспозиции в линейном свете. Полученный $result — новый объект; для самостоятельного файла его ещё нужно сохранить. Маска $rgb исключает alpha. Сама короткая запись использует уже прочитанный $image; ниже полный архив включает вход и сохранение.

Самый простой новый FX-объект, два доверенных рецепта и реально сгенерированный radial.png
Самый простой новый FX-объект, два доверенных рецепта и реально сгенерированный radial.png

Получить новый объект одной формулой, сохранив исходную карточку

fxImage вернул новый объект Imagick. В формуле u — нормированная компонента выбранного канала; степень 0.8 поднимает промежуточные значения 0<u<1 в текущем представлении. Источник не изменён, alpha исключена явной RGB-маской; самостоятельный PNG можно открыть после запуска.

Получить новый объект одной формулой, сохранив исходную карточкуПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$before = fxHaldPixelHash($source);
$result = $source->fxImage('u^0.8', $rgb);
saveFxHaldPNG($result, 'fx-minimal.png', true);
$read = new Imagick(__DIR__ . '/fx-minimal.png');
saveFxHaldComparison([$sourceWhite, fxHaldPreview($read)], 'fx-minimal-comparison.png');
saveFxHaldDiagnostic('fx-hald-fximage-minimal', ['kind'=>'fx-minimal','expression'=>'u^0.8','source'=>fxHaldState($source),'native_result'=>fxHaldState($result),'saved_result'=>fxHaldState($read),'returns_new_object'=>$result!==$source]);
echo "fx-minimal-comparison.png\nfx-minimal.png\nmetrics.json\n";
printf("new-object=%d source-unchanged=%d alpha-preserved=%d\n", (int)($result!==$source), (int)($before===fxHaldPixelHash($source)), (int)(fxHaldPixelHash($source,'A',Imagick::PIXEL_CHAR)===fxHaldPixelHash($read,'A',Imagick::PIXEL_CHAR)));

fxImage вернул новый объект Imagick. В формуле u — нормированная компонента выбранного канала; степень 0.8 поднимает промежуточные значения 0<u<1 в текущем представлении. Источник не изменён, alpha исключена явной RGB-маской; самостоятельный PNG можно открыть после запуска. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
fx-minimal-comparison.png
fx-minimal.png
metrics.json
new-object=1 source-unchanged=1 alpha-preserved=1

Реальная следующая задача — выбирать из ограниченного списка рецептов. Цветовой lift зависит только от u; затемнение краёв дополнительно зависит от i/j/w/h. i растёт вправо, j вниз; w/h — размеры, hypot(a,b) вычисляет расстояние. max(0,...) ограничивает формулу снизу. В радиальном примере xx/yy/rr — временные имена самого FX; ; внутри строки разделяет его инструкции. Это не PHP-переменные с $.

Два одинаковых входных цвета у пространственной формулы могут получить разный результат в центре и у края. Одна Hald-таблица для всех изображений не сможет выразить такое правило: в неё не входят координаты. Также нельзя обещать перенос размытия, поиска соседей, геометрии или случайного эффекта. Фиксированные выражения задаёт код проекта, а не посетитель сайта.

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

Два доверенных рецепта решают разные задачи: первый зависит только от цвета, второй — ещё и от координат. Отдельный radial.png действительно сгенерирован на пустом холсте по пространственной формуле из официального примера. Пространственные эффекты нельзя переносить на произвольные картинки одной цветовой Hald-таблицей.

Выбрать доверенный рецепт и отдельно создать пространственную маскуПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$recipes = ['lift'=>'u^0.8','soft-edges'=>'u*(0.45+0.55*max(0,1-hypot(i-w/2,j-h/2)/200))'];
$pointwise = $source->fxImage($recipes['lift'], $rgb);
$spatial = $source->fxImage($recipes['soft-edges'], $rgb);
$canvas = new Imagick(); $canvas->newPseudoImage(400, 320, 'xc:white');
$radialExpression = 'xx=i-w/2; yy=j-h/2; rr=hypot(xx,yy); (.5-rr/140)*1.2+.5';
$radial = $canvas->fxImage($radialExpression, $rgb);
saveFxHaldPNG($pointwise, 'recipe-lift.png', true); saveFxHaldPNG($spatial, 'recipe-spatial.png', true); saveFxHaldPNG($radial, 'radial.png');
saveFxHaldComparison([$sourceWhite, fxHaldPreview(new Imagick(__DIR__.'/recipe-lift.png')), fxHaldPreview(new Imagick(__DIR__.'/recipe-spatial.png')), new Imagick(__DIR__.'/radial.png')], 'fx-spatial-comparison.png');
saveFxHaldDiagnostic('fx-hald-fximage-workflow', ['kind'=>'fx-workflow','recipes'=>$recipes,'radial_expression'=>$radialExpression,'source'=>fxHaldState($source),'pointwise'=>fxHaldState($pointwise),'spatial'=>fxHaldState($spatial),'radial'=>fxHaldState($radial),'pointwise_transferable_to_Hald'=>true,'coordinate_recipe_transferable_to_Hald'=>false]);
echo "fx-spatial-comparison.png\nrecipe-lift.png\nrecipe-spatial.png\nradial.png\nmetrics.json\n";
printf("recipes=2 alpha-preserved=%d radial-size=%dx%d\n", (int)(fxHaldPixelHash($source,'A',Imagick::PIXEL_CHAR)===fxHaldPixelHash($spatial,'A',Imagick::PIXEL_CHAR)), $radial->getImageWidth(), $radial->getImageHeight());

Два доверенных рецепта решают разные задачи: первый зависит только от цвета, второй — ещё и от координат. Отдельный radial.png действительно сгенерирован на пустом холсте по пространственной формуле из официального примера. Пространственные эффекты нельзя переносить на произвольные картинки одной цветовой Hald-таблицей. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
fx-spatial-comparison.png
recipe-lift.png
recipe-spatial.png
radial.png
metrics.json
recipes=2 alpha-preserved=1 radial-size=400x320

Применять таблицу и повторно использовать её на понятных входах

Самое простое действие до полной программы

Растр — сетка пикселей. RGB означает красную, зелёную и синюю компоненты, RGBA добавляет alpha: 0 прозрачно, максимум непрозрачен. $image — переменная с объектом Imagick; $ начинает имя, = присваивает, -> вызывает метод объекта, () содержит аргументы, ; заканчивает инструкцию. Строка в кавычках передаётся как данные. new Imagick($path) читает файл, clone создаёт независимую рабочую копию. Imagick:: обращается к именованной константе класса. Подробности: переменные, параметры и возврат, объекты и методы.

$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$copy = clone $image;
$ok = $copy->haldClutImage($table, $rgb);

$table — объект уже прочитанной Hald-таблицы; $rgb явно объединяет красный, зелёный и синий каналы через |, не включает alpha. Таблица отвечает: какой выходной цвет нужен для данного входного цвета. Метод меняет $copy; $ok содержит bool-статус, а не картинку. Между известными узлами цветовой сетки библиотека вычисляет промежуточные значения. Вначале получите identity-таблицу: она сопоставляет цвет ему самому; затем цветовой рецепт можно применить к таблице и сохранить отдельно.

Identity сохраняет цвет; один сохранённый look применён к исходнику и его известной зеркальной перестановке; RGB/default alpha показана на тёмном фоне
Identity сохраняет цвет; один сохранённый look применён к исходнику и его известной зеркальной перестановке; RGB/default alpha показана на тёмном фоне

Применить identity Hald и понять устройство таблицы

Identity Hald отображает цвет в тот же цвет. В проверенной цепочке level3/PNG16 исходные Q16 компоненты и alpha сохранились точно. Таблица 27×27 — развёрнутый куб 9×9×9, не уменьшенная фотография. Метод меняет текущую рабочую копию и возвращает bool, а не новый объект.

Применить identity Hald и понять устройство таблицыПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$identity = new Imagick(); $identity->newPseudoImage(27, 27, 'hald:3');
saveFxHaldPNG($identity, 'identity16.png', false, 16);
$table = new Imagick(__DIR__ . '/identity16.png');
$result = clone $source;
$ok = $result->haldClutImage($table, $rgb);
saveFxHaldPNG($result, 'identity-result.png', true); saveFxHaldPNG($table, 'identity-preview8.png');
saveFxHaldComparison([$sourceWhite, fxHaldPreview(new Imagick(__DIR__.'/identity-result.png'))], 'hald-identity-comparison.png');
saveFxHaldDiagnostic('fx-hald-haldclutimage-minimal', ['kind'=>'hald-minimal','tables'=>['identity16.png'=>fxHaldTableInfo($table,3,'identity-preview8.png',$identity)],'source'=>fxHaldState($source),'native_result'=>fxHaldState($result),'saved_result'=>fxHaldState(new Imagick(__DIR__.'/identity-result.png')),'return_type'=>get_debug_type($ok),'return_value'=>$ok]);
echo "hald-identity-comparison.png\nidentity16.png\nidentity-result.png\nidentity-preview8.png\nmetrics.json\n";
printf("ok=%d table-size=%dx%d native-identity-exact=%d\n", (int)$ok, $table->getImageWidth(), $table->getImageHeight(), (int)(fxHaldPixelHash($source)===fxHaldPixelHash($result)));

Identity Hald отображает цвет в тот же цвет. В проверенной цепочке level3/PNG16 исходные Q16 компоненты и alpha сохранились точно. Таблица 27×27 — развёрнутый куб 9×9×9, не уменьшенная фотография. Метод меняет текущую рабочую копию и возвращает bool, а не новый объект. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
hald-identity-comparison.png
identity16.png
identity-result.png
identity-preview8.png
metrics.json
ok=1 table-size=27x27 native-identity-exact=1

После нейтрального identity-контроля повторно используем один recipe16.png. Вторая карточка намеренно зеркальна первой: flopImage переставляет пиксели, не меняя смысл цветового рецепта. Это понятный контролируемый второй вход. Такой pointwise look должен одинаково отображать каждый встреченный цвет независимо от позиции; собственный результат сравнивается с зеркалом уже обработанного исходника. Таблицу читаем один раз, к каждому входу применяем отдельную clone-копию.

Рецепт из произвольной готовой фотографии не восстанавливается единственным вызовом Hald: по одному результату неизвестны исходные цвета, свет, пространственная обработка и авторские шаги. Identity-таблицу обрабатывают известным цветовым действием, а затем используют как переносимый рецепт. Исходник и таблица должны соответствовать выбранному пространству/диапазону; не обещаем перенос любого look на любые профили без подготовки.

Повторно использовать один сохранённый look независимо от положения пикселей

Один файл recipe16.png прочитан и применён к двум локальным входам. Второй вход — намеренное зеркальное преобразование собственного исходника, а не заявка на тестирование чужих фотографий. Поскольку рецепт зависит только от цвета, перестановка пикселей и применение look дают согласованный результат. Alpha проверена отдельно.

Повторно использовать один сохранённый look независимо от положения пикселейПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$identity = new Imagick(); $identity->newPseudoImage(216,216,'hald:6');
$recipe = $identity->fxImage($expression,$rgb);
saveFxHaldPNG($recipe,'recipe16.png',false,16);
$stored = new Imagick(__DIR__.'/recipe16.png');
saveFxHaldPNG($stored,'recipe-preview8.png');
$mirror = clone $source; $mirror->flopImage(); saveFxHaldPNG($mirror,'mirror-input.png',true);
$inputs=['original'=>$source,'mirror'=>$mirror]; $outputs=[];
foreach ($inputs as $name=>$input) {
    if ($input->getImageColorspace()!==$stored->getImageColorspace()) { throw new RuntimeException('LUT color space must match'); }
    $output=clone $input; $output->haldClutImage($stored,$rgb);
    saveFxHaldPNG($output,$name.'-graded.png',true); $outputs[$name]=$output;
}
$mirrorOfGraded=clone $outputs['original']; $mirrorOfGraded->flopImage();
saveFxHaldComparison([$sourceWhite,fxHaldPreview(new Imagick(__DIR__.'/original-graded.png')),fxHaldPreview($mirror),fxHaldPreview(new Imagick(__DIR__.'/mirror-graded.png'))],'hald-reuse-comparison.png');
saveFxHaldDiagnostic('fx-hald-haldclutimage-workflow',['kind'=>'hald-workflow','expression'=>$expression,'tables'=>['recipe16.png'=>fxHaldTableInfo($stored,6,'recipe-preview8.png',$recipe)],'source'=>fxHaldState($source),'mirror_input'=>fxHaldState($mirror),'original_result'=>fxHaldState($outputs['original']),'mirror_result'=>fxHaldState($outputs['mirror']),'mirror_commutes'=>fxHaldPixelHash($mirrorOfGraded)===fxHaldPixelHash($outputs['mirror'])]);
echo "hald-reuse-comparison.png\nrecipe16.png\nrecipe-preview8.png\nmirror-input.png\noriginal-graded.png\nmirror-graded.png\nmetrics.json\n";
printf("reused=2 mirror-consistent=%d alpha-preserved=%d\n",(int)(fxHaldPixelHash($mirrorOfGraded)===fxHaldPixelHash($outputs['mirror'])),(int)(fxHaldPixelHash($source,'A',Imagick::PIXEL_CHAR)===fxHaldPixelHash($outputs['original'],'A',Imagick::PIXEL_CHAR)));

Один файл recipe16.png прочитан и применён к двум локальным входам. Второй вход — намеренное зеркальное преобразование собственного исходника, а не заявка на тестирование чужих фотографий. Поскольку рецепт зависит только от цвета, перестановка пикселей и применение look дают согласованный результат. Alpha проверена отдельно. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
hald-reuse-comparison.png
recipe16.png
recipe-preview8.png
mirror-input.png
original-graded.png
mirror-graded.png
metrics.json
reused=2 mirror-consistent=1 alpha-preserved=1

Измерять выбранную разницу и принимать объяснимое решение

Самое простое действие до полной программы

Растр — сетка пикселей. RGB означает красную, зелёную и синюю компоненты, RGBA добавляет alpha: 0 прозрачно, максимум непрозрачен. $image — переменная с объектом Imagick; $ начинает имя, = присваивает, -> вызывает метод объекта, () содержит аргументы, ; заканчивает инструкцию. Строка в кавычках передаётся как данные. new Imagick($path) читает файл, clone создаёт независимую рабочую копию. Imagick:: обращается к именованной константе класса. Подробности: переменные, параметры и возврат, объекты и методы.

$error = $reference->getImageDistortion($candidate, Imagick::METRIC_ROOTMEANSQUAREDERROR);
echo $error;

Оба объекта уже подготовлены по одной политике. Для RMSE 0 означает отсутствие различий в учитываемых компонентах этого сравнения; большее значение означает большую среднеквадратичную разницу. Это не вероятность совпадения и не процент качества. Для других METRIC_* направление и диапазон могут отличаться. float — число с дробной частью. В наших полных примерах сначала проверяются размеры/цветовое пространство и получаются непрозрачные RGB-виды на белом фоне: отдельно от исходной alpha и отдельно от 8-битных PNG для экрана.

Точная копия, изменённый RGB-вид и выбор плотности по фактическим Q16 измерениям; разница усилена native ×32
Точная копия, изменённый RGB-вид и выбор плотности по фактическим Q16 измерениям; разница усилена native ×32

Сравнить точную копию и видимую правку по одной заданной метрике

Точная копия даёт RMSE 0; изменённая картинка — положительное float-значение, записанное полностью в metrics.json. Перед сравнением получены одинаковые непрозрачные RGB-виды OVER-white с TRUECOLOR. Метод сохраняет служебное свойство distortion у сравниваемого объекта, но не меняет его пиксели.

Сравнить точную копию и видимую правку по одной заданной метрикеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$changed = $source->fxImage($expression,$rgb);
[$left,$right] = fxHaldMetricViews($source,$changed);
$exactCopy=clone $left; $before=fxHaldPixelHash($left);
$zero=$left->getImageDistortion($exactCopy,Imagick::METRIC_ROOTMEANSQUAREDERROR);
$error=$left->getImageDistortion($right,Imagick::METRIC_ROOTMEANSQUAREDERROR);
$property=$left->getImageProperty('distortion');
saveFxHaldPNG($left,'metric-reference.png'); saveFxHaldPNG($exactCopy,'metric-copy.png'); saveFxHaldPNG($right,'metric-changed.png');
saveFxHaldComparison([$sourceWhite,new Imagick(__DIR__.'/metric-copy.png'),new Imagick(__DIR__.'/metric-changed.png')],'metric-minimal-comparison.png');
saveFxHaldDiagnostic('fx-hald-getimagedistortion-minimal',['kind'=>'metric-minimal','metrics'=>['copy_Q16_RMSE'=>$zero,'changed_Q16_RMSE'=>$error],'metric_views'=>[fxHaldState($left),fxHaldState($right)],'return_type'=>get_debug_type($error),'distortion_property'=>$property,'metric_source_pixels_unchanged'=>$before===fxHaldPixelHash($left)]);
echo "metric-minimal-comparison.png\nmetric-reference.png\nmetric-copy.png\nmetric-changed.png\nmetrics.json\n";
printf("copy=%.6f changed-positive=%d alpha-free=%d pixels-unchanged=%d\n",$zero,(int)($error>0),(int)(!$left->getImageAlphaChannel()&&!$right->getImageAlphaChannel()),(int)($before===fxHaldPixelHash($left)));

Точная копия даёт RMSE 0; изменённая картинка — положительное float-значение, записанное полностью в metrics.json. Перед сравнением получены одинаковые непрозрачные RGB-виды OVER-white с TRUECOLOR. Метод сохраняет служебное свойство distortion у сравниваемого объекта, но не меняет его пиксели. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
metric-minimal-comparison.png
metric-reference.png
metric-copy.png
metric-changed.png
metrics.json
copy=0.000000 changed-positive=1 alpha-free=1 pixels-unchanged=1

RMSE в этом уроке измеряет среднюю компонентную разницу уже подготовленных непрозрачных RGB-видов, а не красоту картинки. Для W×H пикселей нормированных RGB-компонент это sqrt(sum((a-b)²)/(W*H*3)). Одинаковые размеры и пространство обязательны по нашей политике; OVER-white фиксирует фон, TRUECOLOR исключает alpha из измеряемого вида. Сами исходные RGBA остаются отдельными объектами: их alpha проверена независимо. Если проекту важен иной фон, следует явно изменить политику и интерпретацию результата.

В рабочем сценарии выбираем точность аппроксимации. Измерения до записи используют native Q16; второй набор измеряет уже повторно прочитанные PNG8. Их нельзя смешивать: сохранение также округляет значения. Порог 0.001 принят только для этого учебного проекта. Даже если вариант прошёл порог, визуально оцените результат на своих данных. Последняя картинка вычислена COMPOSITE_DIFFERENCE и умножена на 32 с отсечением перед PNG8; это усиленный вид отличий, не новое значение RMSE и не доказательство нулевой Q16-ошибки по чёрным экранным пикселям.

Выбрать точность приближения и отдельно увидеть усиленную разницу

Для собственной формулы выбирается вариант с меньшей RMSE по фиксированной политике. Порог 0.001 — правило этого проекта, не универсальная граница качества. В metrics.json раздельно сохранены Q16 измерения до PNG8-доставки и измерения повторно прочитанных PNG8. Последняя панель — реальная native разница coarse/direct, умноженная на 32 с отсечением: это не исходная ошибка в натуральном цвете.

Выбрать точность приближения и отдельно увидеть усиленную разницуПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$direct=$source->fxImage($expression,$rgb); saveFxHaldPNG($direct,'metric-direct.png',true);
$errors=[]; $savedErrors=[]; $tables=[]; $results=[]; $best=null; $bestError=INF;
foreach (['coarse16'=>[3,16],'fine16'=>[6,16],'fine8'=>[6,8]] as $name=>[$level,$depth]) {
    $edge=$level ** 3; $identity=new Imagick(); $identity->newPseudoImage($edge,$edge,'hald:'.$level);
    $recipe=$identity->fxImage($expression,$rgb); saveFxHaldPNG($recipe,$name.'-lut.png',false,$depth);
    $table=new Imagick(__DIR__.'/'.$name.'-lut.png');
    $output=clone $source; $output->haldClutImage($table,$rgb);
    saveFxHaldPNG($output,$name.'-result.png',true); saveFxHaldPNG($table,$name.'-preview8.png');
    [$a,$b]=fxHaldMetricViews($direct,$output); $errors[$name]=$a->getImageDistortion($b,Imagick::METRIC_ROOTMEANSQUAREDERROR);
    [$savedA,$savedB]=fxHaldMetricViews(new Imagick(__DIR__.'/metric-direct.png'),new Imagick(__DIR__.'/'.$name.'-result.png'));
    $savedErrors[$name]=$savedA->getImageDistortion($savedB,Imagick::METRIC_ROOTMEANSQUAREDERROR);
    $results[$name]=$output; $tables[$name.'-lut.png']=fxHaldTableInfo($table,$level,$name.'-preview8.png',$recipe);
    if ($errors[$name]<$bestError) {$best=$name; $bestError=$errors[$name];}
}
$threshold=0.001;
[$reference,$coarse]=fxHaldMetricViews($direct,$results['coarse16']);
$difference=clone $reference; $difference->compositeImage($coarse,Imagick::COMPOSITE_DIFFERENCE,0,0);
$difference->evaluateImage(Imagick::EVALUATE_MULTIPLY,32,$rgb); saveFxHaldPNG($difference,'difference32.png');
saveFxHaldComparison([$sourceWhite,fxHaldPreview(new Imagick(__DIR__.'/metric-direct.png')),fxHaldPreview(new Imagick(__DIR__.'/coarse16-result.png')),new Imagick(__DIR__.'/difference32.png')],'metric-choice-comparison.png');
saveFxHaldDiagnostic('fx-hald-getimagedistortion-workflow',['kind'=>'metric-workflow','expression'=>$expression,'tables'=>$tables,'metrics'=>['native_Q16_RMSE'=>$errors,'saved_PNG8_RMSE'=>$savedErrors],'threshold'=>$threshold,'chosen'=>$best,'chosen_pass'=>$bestError<$threshold,'metric_views'=>[fxHaldState($reference),fxHaldState($coarse)],'difference'=>['filename'=>'difference32.png','factor'=>32,'operator'=>'native COMPOSITE_DIFFERENCE then EVALUATE_MULTIPLY; Q16 clamp before PNG8','domain'=>'native coarse16 and direct opaque TRUECOLOR white views']]);
echo "metric-choice-comparison.png\nmetric-direct.png\ncoarse16-lut.png\ncoarse16-result.png\ncoarse16-preview8.png\nfine16-lut.png\nfine16-result.png\nfine16-preview8.png\nfine8-lut.png\nfine8-result.png\nfine8-preview8.png\ndifference32.png\nmetrics.json\n";
printf("chosen=%s pass=%d coarse-pass=%d fine-pass=%d\n",$best,(int)($bestError<$threshold),(int)($errors['coarse16']<$threshold),(int)($errors['fine16']<$threshold));

Для собственной формулы выбирается вариант с меньшей RMSE по фиксированной политике. Порог 0.001 — правило этого проекта, не универсальная граница качества. В metrics.json раздельно сохранены Q16 измерения до PNG8-доставки и измерения повторно прочитанных PNG8. Последняя панель — реальная native разница coarse/direct, умноженная на 32 с отсечением: это не исходная ошибка в натуральном цвете. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
metric-choice-comparison.png
metric-direct.png
coarse16-lut.png
coarse16-result.png
coarse16-preview8.png
fine16-lut.png
fine16-result.png
fine16-preview8.png
fine8-lut.png
fine8-result.png
fine8-preview8.png
difference32.png
metrics.json
chosen=fine16 pass=1 coarse-pass=0 fine-pass=1

Разбирать ошибки, прозрачность и реальные данные сохранённого файла

Метод может успешно выполниться, но результат не соответствовать задаче. Проверяйте отдельные договорённости: исходник не изменён, alpha не потеряна, таблица действительно прочитана с диска, пространства/размеры совпадают, файл содержит нужную битность. getImageDepth — метаданные объекта, а IHDR bit depth — реальное хранение PNG; это не одно обещание о точности промежуточных вычислений.

Ситуация реального запускаНаблюдениеЧто делать в приложении
Недописанная FX-строка u+ImagickExceptionВыбирать фиксированные валидные рецепты, обрабатывать ImagickException
Пустой объект FX / LUT / referenceImagickExceptionПрочитать/создать настоящий растр до вызова
Default Hald на нашем RGBAAlpha измененаЯвно выбрать RGB и отдельно проверить alpha
LUT16 после сохранения/чтенияQ16 узлы сохранены; PNG8 узлы округленыПроверять фактический файл, не только preview
399×320 против 400×320RMSE 0 без исключенияПроверить геометрию в своём коде
getImageDistortion после сравненияПиксели не изменены, distortion property записаноНе путать отсутствие изменения растра с отсутствием состояния

Библиотечная alpha-метка и видимая непрозрачность различаются: ALPHACHANNEL_DEACTIVATE может оставить Copy-трейт, при котором getImageAlphaChannel всё ещё true. Наш helper после реального OVER-white использует IMGTYPE_TRUECOLOR; в этой библиотеке он снимает alpha-трейт, сохраняя Q16 цветовые значения. Это явный способ получить понятный измеряемый RGB-домен, а не попытка объявить любые прозрачные сравнения обычной RGB-формулой. Исходные RGBA не заменяются этим видом.

Полный локальный helper, входящий в каждый архив

<?php
declare(strict_types=1);
function restoreFxHaldInput(): void {
    $text = file_get_contents(__DIR__ . '/source.png.b64');
    if ($text === false) { throw new RuntimeException('Input missing'); }
    $bytes = base64_decode($text, true);
    if ($bytes === false || hash('sha256', $bytes) !== 'f51e5780445e0f5130a7f23112b2a00618035afd5545b8e3042c7ccff09b617c') { throw new RuntimeException('Input mismatch'); }
    if (file_put_contents(__DIR__ . '/source.png', $bytes) === false) { throw new RuntimeException('Restore failed'); }
}
function fxHaldPreview(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 saveFxHaldPNG(Imagick $image, string $filename, bool $alpha = false, int $depth = 8): void {
    if ($depth !== 8 && $depth !== 16) { throw new InvalidArgumentException('PNG depth must be 8 or 16'); }
    $copy = clone $image; $copy->setImageFormat('png'); $copy->setImageDepth($depth);
    $copy->setOption('png:bit-depth', (string)$depth); $copy->setOption('png:color-type', $alpha ? '6' : '2');
    if (!$copy->writeImage(__DIR__ . '/' . $filename)) { throw new RuntimeException('PNG write failed'); }
}
function saveFxHaldComparison(array $panels, string $filename): void {
    $width = 0; $height = 0;
    foreach ($panels as $panel) { $width += $panel->getImageWidth(); $height = max($height, $panel->getImageHeight()); }
    $canvas = new Imagick(); $canvas->newImage($width, $height, 'white'); $x = 0;
    foreach ($panels as $panel) { $canvas->compositeImage($panel, Imagick::COMPOSITE_OVER, $x, intdiv($height - $panel->getImageHeight(), 2)); $x += $panel->getImageWidth(); }
    saveFxHaldPNG($canvas, $filename);
}
function fxHaldPixelHash(Imagick $image, string $channels = 'RGBA', int $storage = Imagick::PIXEL_DOUBLE): string {
    return hash('sha256', json_encode($image->exportImagePixels(0, 0, $image->getImageWidth(), $image->getImageHeight(), $channels, $storage), JSON_THROW_ON_ERROR));
}
function fxHaldState(Imagick $image): array {
    return ['width'=>$image->getImageWidth(),'height'=>$image->getImageHeight(),'reported_depth'=>$image->getImageDepth(),'colorspace'=>$image->getImageColorspace(),'alpha'=>$image->getImageAlphaChannel(),'RGBA_double_hash'=>fxHaldPixelHash($image),'RGBA8_hash'=>fxHaldPixelHash($image,'RGBA',Imagick::PIXEL_CHAR),'alpha8_hash'=>fxHaldPixelHash($image,'A',Imagick::PIXEL_CHAR)];
}
function fxHaldMetricViews(Imagick $left, Imagick $right): array {
    if ($left->getImageWidth() !== $right->getImageWidth() || $left->getImageHeight() !== $right->getImageHeight()) { throw new InvalidArgumentException('Metric dimensions must match'); }
    if ($left->getImageColorspace() !== $right->getImageColorspace()) { throw new InvalidArgumentException('Metric color spaces must match'); }
    $a = fxHaldPreview($left); $b = fxHaldPreview($right);
    if ($a->getImageAlphaChannel() || $b->getImageAlphaChannel()) { throw new RuntimeException('Expected opaque TRUECOLOR metric views'); }
    return [$a, $b];
}
function fxHaldTableInfo(Imagick $table, int $level, string $preview, ?Imagick $generated = null): array {
    $n=$level ** 2; $side=$level ** 3; $middle=intdiv($n-1,2); $samples=[];
    foreach (['black'=>[0,0,0],'red'=>[$n-1,0,0],'green'=>[0,$n-1,0],'blue'=>[0,0,$n-1],'middle'=>[$middle,$middle,$middle]] as $name=>[$r,$g,$b]) {
        $index=$r+$n*$g+$n*$n*$b; $x=$index%$side; $y=intdiv($index,$side);
        $samples[$name]=['lattice'=>[$r,$g,$b],'xy'=>[$x,$y],'native_normalized_color'=>$table->getImagePixelColor($x,$y)->getColor(1)];
    }
    return ['level'=>$level,'side'=>$side,'points_per_axis'=>$n,'preview_filename'=>$preview,'native_state'=>fxHaldState($table),'generated_native_hash'=>$generated === null ? null : fxHaldPixelHash($generated),'lattice_samples'=>$samples];
}
function saveFxHaldDiagnostic(string $id, array $data): void {
    $document=['schema'=>'aen-fx-hald-program-diagnostics-v1','example_id'=>$id,'metric_domain'=>'native Q16 opaque OVER-white TRUECOLOR RGB views; original alpha checked separately']+$data;
    if (file_put_contents(__DIR__ . '/metrics.json',json_encode($document,JSON_PRETTY_PRINT|JSON_PRESERVE_ZERO_FRACTION|JSON_THROW_ON_ERROR)."\n") === false) { throw new RuntimeException('Diagnostic write failed'); }
}

restoreFxHaldInput восстанавливает собственный вход и проверяет SHA. saveFxHaldPNG сохраняет clone с явными PNG bit depth/color type, не изменяя рабочий источник; отдельно сохранённые LUT16 не декодируются экранным PNG8-представлением. fxHaldTableInfo фиксирует lattice-координаты, реальные нормированные компоненты и Q16-хеш до/после файла. Сохранённые PNG8 previews нужны только для просмотра. fxHaldMetricViews проверяет размеры/пространство и готовит RGB-виды, но сама метрика вызывается в программе. helper не вызывает три изучаемых метода. Неизвестный пользовательский путь/файл в эти локальные примеры не подставляется.

Выбирать инструмент по задаче и соединять его с приложением

От задачи к действию: фиксированная цветовая формула, сохранённая цветовая сетка, пространственный эффект либо выбранная метрика
От задачи к действию: фиксированная цветовая формула, сохранённая цветовая сетка, пространственный эффект либо выбранная метрика
ЗадачаВыборНаблюдаемый результат
Проверить небольшую фиксированную цветовую формулуfxImage с явным RGBНовый объект и сохранённый PNG
Перенести известный pointwise look в файлIdentity HALD → FX → PNG16 → haldClutImageОтдельный recipe16.png и реальные карточки
Затемнить края/создать маску по координатамПрямой FX с i/j/w/hПространственный результат; не общий цветовой LUT
Сравнить аппроксимацииОдинаковые RGB-виды → getImageDistortionПолные native значения и отдельные PNG8 измерения
Понять, где расходятся вариантыNative difference с указанным усилениемУсиленная иллюстрация, отдельно от числа RMSE

Архивы запускаются через CLI: сначала сохраните файлы и посмотрите результат, затем подключайте этот сценарий к своему сайту. Для HTTP-изображения отдельный обработчик после подготовки задаёт настоящий Content-Type и отправляет только бинарный файл; диагностические echo из CLI перед PNG запрещают корректный ответ. Дальше соединяйте с чтением и записью файлов, HTTP-ответом, alpha и выбранным фоном, типом растра и битностью PNG, форматом и сжатием. Изменение профилей и массовая обработка кадров требуют собственных явно заданных правил. Никаких общих обещаний ускорения, точности произвольных LUT или субъективного качества из этих восьми входов не следует.

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

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

Imagick::fxImageВозвращает новый объект Imagick с вычисленным растром; исходник остаётся доступен.

PECL imagick 2, PECL imagick 3 · imagick

Imagick::fxImage(string $expression, int $channel = Imagick::CHANNEL_DEFAULT): Imagick

Самое простое действие до полной программы

Растр — сетка пикселей. RGB означает красную, зелёную и синюю компоненты, RGBA добавляет alpha: 0 прозрачно, максимум непрозрачен. $image — переменная с объектом Imagick; $ начинает имя, = присваивает, -> вызывает метод объекта, () содержит аргументы, ; заканчивает инструкцию. Строка в кавычках передаётся как данные. new Imagick($path) читает файл, clone создаёт независимую рабочую копию. Imagick:: обращается к именованной константе класса. Подробности: переменные, параметры и возврат, объекты и методы.

$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$result = $image->fxImage('u^0.8', $rgb);

Здесь u — нормированное значение текущего выбранного канала от 0 до 1. Например, 0.25 в степени 0.8 приблизительно равно 0.33: промежуточная компонента становится выше. Символ ^ означает степень именно в языке FX внутри строки, а в PHP вне строки ^ — побитовый XOR; PHP-степень записывается **. Мы не вычисляем PHP-выражение и не обещаем физическую коррекцию экспозиции в линейном свете. Полученный $result — новый объект; для самостоятельного файла его ещё нужно сохранить. Маска $rgb исключает alpha. Сама короткая запись использует уже прочитанный $image; ниже полный архив включает вход и сохранение.

Полный официальный контракт с разбором

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

Imagick::fxImage(string $expression, int $channel = Imagick::CHANNEL_DEFAULT): Imagick
ПараметрЧто передавать и зачем
$expression: stringОбязательная строка на языке FX ImageMagick; это не PHP-код.
$channel: int = Imagick::CHANNEL_DEFAULTНеобязательная маска подходящих каналов. Несколько каналов объединяются побитовым OR |. Здесь RED | GREEN | BLUE оставляет alpha вне обработки.

Возвращает новый объект Imagick с вычисленным растром; исходник остаётся доступен.

Доступность по официальному справочнику: PECL imagick 2, PECL imagick 3. Публичный метод обрабатывает текущее изображение объекта; список кадров не становится автоматически одной фотографией. Для многих кадров выбирайте и обрабатывайте их осознанно.

В исходном XML раздел returnvalues содержит шаблон «Returns true on success», хотя сигнатура возвращает Imagick. В Imagick 3.8.1 stub, PHP C-обёртка и реальный вызов подтверждают новый объект Imagick. Сохранённый ниже исходный XML показывает расхождение; не присваивайте результат как bool. FX понимает функции, арифметику, переменные, координаты i/j, размеры w/h, доступ к пикселям и другие средства, описанные в официальном FX operator. Наш первый рецепт зависит только от текущего цвета; отдельный пример с hypot и координатами показывает другую область применения.

Выражение задаётся разработчиком: выбирайте готовый рецепт из собственного списка. У FX есть и файловые возможности, в частности форма @file; произвольная строка посетителя не становится безопасной из-за того, что передана в метод изображения. Не создавайте таким способом открытый калькулятор выражений. Нет обещания скорости относительно другой обработки без измерения конкретной задачи.

Официальный раздел ошибок указывает ImagickException при ошибке. В реальном запуске пустой объект FX, пустая LUT и пустое изображение-ссылка вызвали ImagickException; синтаксически незавершённое выражение u+ также вызвало ImagickException. Неверный PHP-тип аргумента обрабатывается PHP-проверкой типов. Не заменяйте обработку ошибки проверкой файла по одному числу: чтение пути, доступ к записи и наличие coder — отдельные этапы.

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


<?php
function fxImage() {
    $imagick = new \Imagick();
    $imagick->newPseudoImage(200, 200, "xc:white");

    $fx = 'xx=i-w/2; yy=j-h/2; rr=hypot(xx,yy); (.5-rr/140)*1.2+.5';
    $fxImage = $imagick->fxImage($fx);

    header("Content-Type: image/png");
    $fxImage->setimageformat('png');
    echo $fxImage->getImageBlob();
}

?>

Официальный фрагмент объявляет функцию fxImage(), но сам её не вызывает. Он создаёт пустой белый холст 200×200, вычисляет радиальную формулу и при вызове отдаёт PNG в HTTP-ответ. Наша самостоятельная программа выше создаёт настоящий radial.png 400×320 по той же формуле, с явной RGB-маской и без смешивания бинарного ответа с CLI-текстом. Изменённый размер указан явно; исходный пример сохранён целиком.

Полный исходный XML: описание, параметры, возврат, ошибки и примеры
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.fximage" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::fxImage</refname>
  <refpurpose>Evaluate expression for each pixel in the image</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>Imagick</type><methodname>Imagick::fxImage</methodname>
   <methodparam><type>string</type><parameter>expression</parameter></methodparam>
   <methodparam choice="opt"><type>int</type><parameter>channel</parameter><initializer>Imagick::CHANNEL_DEFAULT</initializer></methodparam>
  </methodsynopsis>
  <para>
   Evaluate expression for each pixel in the image. Consult <link
   xlink:href="&url.imagemagick;script/fx.php">The Fx Special Effects Image
   Operator</link> for more information.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>expression</parameter></term>
     <listitem>
      <para>
      The expression.
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>channel</parameter></term>
     <listitem>
      <para>
       Provide any channel constant that is valid for your channel mode. To
       apply to more than one channel, combine channeltype constants using
       bitwise operators. Refer to this
       list of  <link linkend="imagick.constants.channel">channel constants</link>.
      </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>



 <refsect1 role="examples">
  &reftitle.examples;
  <para>
    <example>
      <title> <function>Imagick::fxImage</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
function fxImage() {
    $imagick = new \Imagick();
    $imagick->newPseudoImage(200, 200, "xc:white");

    $fx = 'xx=i-w/2; yy=j-h/2; rr=hypot(xx,yy); (.5-rr/140)*1.2+.5';
    $fxImage = $imagick->fxImage($fx);

    header("Content-Type: image/png");
    $fxImage->setimageformat('png');
    echo $fxImage->getImageBlob();
}

?>
]]>
      </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
-->

Получить новый объект одной формулой, сохранив исходную карточку

fxImage вернул новый объект Imagick. В формуле u — нормированная компонента выбранного канала; степень 0.8 поднимает промежуточные значения 0<u<1 в текущем представлении. Источник не изменён, alpha исключена явной RGB-маской; самостоятельный PNG можно открыть после запуска.

Получить новый объект одной формулой, сохранив исходную карточкуПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$before = fxHaldPixelHash($source);
$result = $source->fxImage('u^0.8', $rgb);
saveFxHaldPNG($result, 'fx-minimal.png', true);
$read = new Imagick(__DIR__ . '/fx-minimal.png');
saveFxHaldComparison([$sourceWhite, fxHaldPreview($read)], 'fx-minimal-comparison.png');
saveFxHaldDiagnostic('fx-hald-fximage-minimal', ['kind'=>'fx-minimal','expression'=>'u^0.8','source'=>fxHaldState($source),'native_result'=>fxHaldState($result),'saved_result'=>fxHaldState($read),'returns_new_object'=>$result!==$source]);
echo "fx-minimal-comparison.png\nfx-minimal.png\nmetrics.json\n";
printf("new-object=%d source-unchanged=%d alpha-preserved=%d\n", (int)($result!==$source), (int)($before===fxHaldPixelHash($source)), (int)(fxHaldPixelHash($source,'A',Imagick::PIXEL_CHAR)===fxHaldPixelHash($read,'A',Imagick::PIXEL_CHAR)));

fxImage вернул новый объект Imagick. В формуле u — нормированная компонента выбранного канала; степень 0.8 поднимает промежуточные значения 0<u<1 в текущем представлении. Источник не изменён, alpha исключена явной RGB-маской; самостоятельный PNG можно открыть после запуска. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
fx-minimal-comparison.png
fx-minimal.png
metrics.json
new-object=1 source-unchanged=1 alpha-preserved=1

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

Два доверенных рецепта решают разные задачи: первый зависит только от цвета, второй — ещё и от координат. Отдельный radial.png действительно сгенерирован на пустом холсте по пространственной формуле из официального примера. Пространственные эффекты нельзя переносить на произвольные картинки одной цветовой Hald-таблицей.

Выбрать доверенный рецепт и отдельно создать пространственную маскуПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$recipes = ['lift'=>'u^0.8','soft-edges'=>'u*(0.45+0.55*max(0,1-hypot(i-w/2,j-h/2)/200))'];
$pointwise = $source->fxImage($recipes['lift'], $rgb);
$spatial = $source->fxImage($recipes['soft-edges'], $rgb);
$canvas = new Imagick(); $canvas->newPseudoImage(400, 320, 'xc:white');
$radialExpression = 'xx=i-w/2; yy=j-h/2; rr=hypot(xx,yy); (.5-rr/140)*1.2+.5';
$radial = $canvas->fxImage($radialExpression, $rgb);
saveFxHaldPNG($pointwise, 'recipe-lift.png', true); saveFxHaldPNG($spatial, 'recipe-spatial.png', true); saveFxHaldPNG($radial, 'radial.png');
saveFxHaldComparison([$sourceWhite, fxHaldPreview(new Imagick(__DIR__.'/recipe-lift.png')), fxHaldPreview(new Imagick(__DIR__.'/recipe-spatial.png')), new Imagick(__DIR__.'/radial.png')], 'fx-spatial-comparison.png');
saveFxHaldDiagnostic('fx-hald-fximage-workflow', ['kind'=>'fx-workflow','recipes'=>$recipes,'radial_expression'=>$radialExpression,'source'=>fxHaldState($source),'pointwise'=>fxHaldState($pointwise),'spatial'=>fxHaldState($spatial),'radial'=>fxHaldState($radial),'pointwise_transferable_to_Hald'=>true,'coordinate_recipe_transferable_to_Hald'=>false]);
echo "fx-spatial-comparison.png\nrecipe-lift.png\nrecipe-spatial.png\nradial.png\nmetrics.json\n";
printf("recipes=2 alpha-preserved=%d radial-size=%dx%d\n", (int)(fxHaldPixelHash($source,'A',Imagick::PIXEL_CHAR)===fxHaldPixelHash($spatial,'A',Imagick::PIXEL_CHAR)), $radial->getImageWidth(), $radial->getImageHeight());

Два доверенных рецепта решают разные задачи: первый зависит только от цвета, второй — ещё и от координат. Отдельный radial.png действительно сгенерирован на пустом холсте по пространственной формуле из официального примера. Пространственные эффекты нельзя переносить на произвольные картинки одной цветовой Hald-таблицей. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
fx-spatial-comparison.png
recipe-lift.png
recipe-spatial.png
radial.png
metrics.json
recipes=2 alpha-preserved=1 radial-size=400x320
Imagick::haldClutImageМеняет текущее изображение и возвращает true при успехе. Для сохранения исходника сначала clone.

PECL imagick 3 · imagick

Imagick::haldClutImage(Imagick $clut, int $channel = Imagick::CHANNEL_DEFAULT): bool

Самое простое действие до полной программы

Растр — сетка пикселей. RGB означает красную, зелёную и синюю компоненты, RGBA добавляет alpha: 0 прозрачно, максимум непрозрачен. $image — переменная с объектом Imagick; $ начинает имя, = присваивает, -> вызывает метод объекта, () содержит аргументы, ; заканчивает инструкцию. Строка в кавычках передаётся как данные. new Imagick($path) читает файл, clone создаёт независимую рабочую копию. Imagick:: обращается к именованной константе класса. Подробности: переменные, параметры и возврат, объекты и методы.

$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$copy = clone $image;
$ok = $copy->haldClutImage($table, $rgb);

$table — объект уже прочитанной Hald-таблицы; $rgb явно объединяет красный, зелёный и синий каналы через |, не включает alpha. Таблица отвечает: какой выходной цвет нужен для данного входного цвета. Метод меняет $copy; $ok содержит bool-статус, а не картинку. Между известными узлами цветовой сетки библиотека вычисляет промежуточные значения. Вначале получите identity-таблицу: она сопоставляет цвет ему самому; затем цветовой рецепт можно применить к таблице и сохранить отдельно.

Полный официальный контракт с разбором

Заменить цвета текущего изображения по Hald-таблице

Imagick::haldClutImage(Imagick $clut, int $channel = Imagick::CHANNEL_DEFAULT): bool
ПараметрЧто передавать и зачем
$clut: ImagickОбязательный объект, содержащий Hald lookup image. Identity-таблицу умеет создавать HALD coder.
$channel: int = Imagick::CHANNEL_DEFAULTНеобязательная маска каналов; для сохранения прозрачности здесь явно выбраны RED | GREEN | BLUE.

Меняет текущее изображение и возвращает true при успехе. Для сохранения исходника сначала clone.

Доступность по официальному справочнику: PECL imagick 3. Публичный метод обрабатывает текущее изображение объекта; список кадров не становится автоматически одной фотографией. Для многих кадров выбирайте и обрабатывайте их осознанно.

HALD coder создаёт identity-таблицы, например newPseudoImage(216, 216, "hald:6"). Для уровня L квадрат имеет сторону L³, а цветовая сетка — L² точек на каждой RGB-оси. Формат LUT и полноцветный PNG фотографии — разные роли файлов. Не изменяйте размеры/цвета LUT как у миниатюры и не сжимайте её JPEG, если хотите сохранить таблицу. Повышение плотности сетки и битность хранения — два разных решения.

CHANNEL_DEFAULT — значение API по умолчанию, а не обещание сохранить alpha. В нашем входе default Hald сделал alpha непрозрачной; явная RGB-маска сохранила её. При несовпадении пространств LUT/изображения внутренний код может назначать цветовое пространство, а не выполнять ожидаемую ICC-конверсию. Поэтому здесь у входа и LUT одинаковое sRGB и это проверяется перед применением. Отдельная глава объясняет профили; Hald не заменяет управление цветом.

Официальный раздел ошибок указывает ImagickException при ошибке. В реальном запуске пустой объект FX, пустая LUT и пустое изображение-ссылка вызвали ImagickException; синтаксически незавершённое выражение u+ также вызвало ImagickException. Неверный PHP-тип аргумента обрабатывается PHP-проверкой типов. Не заменяйте обработку ошибки проверкой файла по одному числу: чтение пути, доступ к записи и наличие coder — отдельные этапы.

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


<?php
function haldClutImage($imagePath) {
    $imagick = new \Imagick(realpath($imagePath));
    $imagickPalette = new \Imagick(realpath("images/hald/hald_8.png"));
    $imagickPalette->sepiatoneImage(55);
    $imagick->haldClutImage($imagickPalette);
    header("Content-Type: image/jpg");
    echo $imagick->getImageBlob();
}

?>

Официальный фрагмент также только объявляет функцию. Ему нужны реальный $imagePath и внешний images/hald/hald_8.png, которых этот фрагмент не создаёт. Он применяет sepiatoneImage(55) к таблице и затем заменяет цвета фотографии. Заголовок Content-Type: image/jpg сам не выбирает формат getImageBlob: для JPEG следует явно выбрать формат и решить, какой фон заменяет alpha. Наши самостоятельные программы создают собственные identity/recipe LUT, сохраняют PNG и показывают результат; зависимость от отсутствующей внешней таблицы исключена.

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

<refentry xml:id="imagick.haldclutimage" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::haldClutImage</refname>
  <refpurpose>Replaces colors in the image</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::haldClutImage</methodname>
   <methodparam><type>Imagick</type><parameter>clut</parameter></methodparam>
   <methodparam choice="opt"><type>int</type><parameter>channel</parameter><initializer>Imagick::CHANNEL_DEFAULT</initializer></methodparam>
  </methodsynopsis>
  <para>
   Replaces colors in the image using a Hald lookup table. Hald images can be created using 
   HALD color coder.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>clut</parameter></term>
     <listitem>
      <para>
       Imagick object containing the Hald lookup image.
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>channel</parameter></term>
     <listitem>
      <para>
       &imagick.parameter.channel;
      </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>


 <refsect1 role="examples">
  &reftitle.examples;
  <para>
    <example>
      <title> <function>Imagick::haldClutImage</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
function haldClutImage($imagePath) {
    $imagick = new \Imagick(realpath($imagePath));
    $imagickPalette = new \Imagick(realpath("images/hald/hald_8.png"));
    $imagickPalette->sepiatoneImage(55);
    $imagick->haldClutImage($imagickPalette);
    header("Content-Type: image/jpg");
    echo $imagick->getImageBlob();
}

?>
]]>
      </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
-->

Применить identity Hald и понять устройство таблицы

Identity Hald отображает цвет в тот же цвет. В проверенной цепочке level3/PNG16 исходные Q16 компоненты и alpha сохранились точно. Таблица 27×27 — развёрнутый куб 9×9×9, не уменьшенная фотография. Метод меняет текущую рабочую копию и возвращает bool, а не новый объект.

Применить identity Hald и понять устройство таблицыПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$identity = new Imagick(); $identity->newPseudoImage(27, 27, 'hald:3');
saveFxHaldPNG($identity, 'identity16.png', false, 16);
$table = new Imagick(__DIR__ . '/identity16.png');
$result = clone $source;
$ok = $result->haldClutImage($table, $rgb);
saveFxHaldPNG($result, 'identity-result.png', true); saveFxHaldPNG($table, 'identity-preview8.png');
saveFxHaldComparison([$sourceWhite, fxHaldPreview(new Imagick(__DIR__.'/identity-result.png'))], 'hald-identity-comparison.png');
saveFxHaldDiagnostic('fx-hald-haldclutimage-minimal', ['kind'=>'hald-minimal','tables'=>['identity16.png'=>fxHaldTableInfo($table,3,'identity-preview8.png',$identity)],'source'=>fxHaldState($source),'native_result'=>fxHaldState($result),'saved_result'=>fxHaldState(new Imagick(__DIR__.'/identity-result.png')),'return_type'=>get_debug_type($ok),'return_value'=>$ok]);
echo "hald-identity-comparison.png\nidentity16.png\nidentity-result.png\nidentity-preview8.png\nmetrics.json\n";
printf("ok=%d table-size=%dx%d native-identity-exact=%d\n", (int)$ok, $table->getImageWidth(), $table->getImageHeight(), (int)(fxHaldPixelHash($source)===fxHaldPixelHash($result)));

Identity Hald отображает цвет в тот же цвет. В проверенной цепочке level3/PNG16 исходные Q16 компоненты и alpha сохранились точно. Таблица 27×27 — развёрнутый куб 9×9×9, не уменьшенная фотография. Метод меняет текущую рабочую копию и возвращает bool, а не новый объект. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
hald-identity-comparison.png
identity16.png
identity-result.png
identity-preview8.png
metrics.json
ok=1 table-size=27x27 native-identity-exact=1

Повторно использовать один сохранённый look независимо от положения пикселей

Один файл recipe16.png прочитан и применён к двум локальным входам. Второй вход — намеренное зеркальное преобразование собственного исходника, а не заявка на тестирование чужих фотографий. Поскольку рецепт зависит только от цвета, перестановка пикселей и применение look дают согласованный результат. Alpha проверена отдельно.

Повторно использовать один сохранённый look независимо от положения пикселейПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$identity = new Imagick(); $identity->newPseudoImage(216,216,'hald:6');
$recipe = $identity->fxImage($expression,$rgb);
saveFxHaldPNG($recipe,'recipe16.png',false,16);
$stored = new Imagick(__DIR__.'/recipe16.png');
saveFxHaldPNG($stored,'recipe-preview8.png');
$mirror = clone $source; $mirror->flopImage(); saveFxHaldPNG($mirror,'mirror-input.png',true);
$inputs=['original'=>$source,'mirror'=>$mirror]; $outputs=[];
foreach ($inputs as $name=>$input) {
    if ($input->getImageColorspace()!==$stored->getImageColorspace()) { throw new RuntimeException('LUT color space must match'); }
    $output=clone $input; $output->haldClutImage($stored,$rgb);
    saveFxHaldPNG($output,$name.'-graded.png',true); $outputs[$name]=$output;
}
$mirrorOfGraded=clone $outputs['original']; $mirrorOfGraded->flopImage();
saveFxHaldComparison([$sourceWhite,fxHaldPreview(new Imagick(__DIR__.'/original-graded.png')),fxHaldPreview($mirror),fxHaldPreview(new Imagick(__DIR__.'/mirror-graded.png'))],'hald-reuse-comparison.png');
saveFxHaldDiagnostic('fx-hald-haldclutimage-workflow',['kind'=>'hald-workflow','expression'=>$expression,'tables'=>['recipe16.png'=>fxHaldTableInfo($stored,6,'recipe-preview8.png',$recipe)],'source'=>fxHaldState($source),'mirror_input'=>fxHaldState($mirror),'original_result'=>fxHaldState($outputs['original']),'mirror_result'=>fxHaldState($outputs['mirror']),'mirror_commutes'=>fxHaldPixelHash($mirrorOfGraded)===fxHaldPixelHash($outputs['mirror'])]);
echo "hald-reuse-comparison.png\nrecipe16.png\nrecipe-preview8.png\nmirror-input.png\noriginal-graded.png\nmirror-graded.png\nmetrics.json\n";
printf("reused=2 mirror-consistent=%d alpha-preserved=%d\n",(int)(fxHaldPixelHash($mirrorOfGraded)===fxHaldPixelHash($outputs['mirror'])),(int)(fxHaldPixelHash($source,'A',Imagick::PIXEL_CHAR)===fxHaldPixelHash($outputs['original'],'A',Imagick::PIXEL_CHAR)));

Один файл recipe16.png прочитан и применён к двум локальным входам. Второй вход — намеренное зеркальное преобразование собственного исходника, а не заявка на тестирование чужих фотографий. Поскольку рецепт зависит только от цвета, перестановка пикселей и применение look дают согласованный результат. Alpha проверена отдельно. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
hald-reuse-comparison.png
recipe16.png
recipe-preview8.png
mirror-input.png
original-graded.png
mirror-graded.png
metrics.json
reused=2 mirror-consistent=1 alpha-preserved=1
Imagick::getImageDistortionВозвращает float — значение выбранной метрики либо её оценку. Смысл направления/диапазона зависит от метрики.

PECL imagick 2, PECL imagick 3 · imagick

Imagick::getImageDistortion(Imagick $reference, int $metric): float

Самое простое действие до полной программы

Растр — сетка пикселей. RGB означает красную, зелёную и синюю компоненты, RGBA добавляет alpha: 0 прозрачно, максимум непрозрачен. $image — переменная с объектом Imagick; $ начинает имя, = присваивает, -> вызывает метод объекта, () содержит аргументы, ; заканчивает инструкцию. Строка в кавычках передаётся как данные. new Imagick($path) читает файл, clone создаёт независимую рабочую копию. Imagick:: обращается к именованной константе класса. Подробности: переменные, параметры и возврат, объекты и методы.

$error = $reference->getImageDistortion($candidate, Imagick::METRIC_ROOTMEANSQUAREDERROR);
echo $error;

Оба объекта уже подготовлены по одной политике. Для RMSE 0 означает отсутствие различий в учитываемых компонентах этого сравнения; большее значение означает большую среднеквадратичную разницу. Это не вероятность совпадения и не процент качества. Для других METRIC_* направление и диапазон могут отличаться. float — число с дробной частью. В наших полных примерах сначала проверяются размеры/цветовое пространство и получаются непрозрачные RGB-виды на белом фоне: отдельно от исходной alpha и отдельно от 8-битных PNG для экрана.

Полный официальный контракт с разбором

Сравнить изображение с другим по указанной метрике

Imagick::getImageDistortion(Imagick $reference, int $metric): float
ПараметрЧто передавать и зачем
$reference: ImagickОбязательный объект изображения для сравнения. Тип MagickWand в XML — неточность описания PHP API.
$metric: intОбязательная именованная константа METRIC_*. В примерах METRIC_ROOTMEANSQUAREDERROR; у метода нет отдельного аргумента channel.

Возвращает float — значение выбранной метрики либо её оценку. Смысл направления/диапазона зависит от метрики.

Доступность по официальному справочнику: PECL imagick 2, PECL imagick 3. Публичный метод обрабатывает текущее изображение объекта; список кадров не становится автоматически одной фотографией. Для многих кадров выбирайте и обрабатывайте их осознанно.

В XML сигнатура ошибочно называет первый аргумент MagickWand, а описание параметра уже говорит Imagick. Реальный PHP-тип в Imagick 3.8.1 — Imagick; правильная PHP-запись показана первой. Исходное объявление для сверки:

Imagick::getImageDistortion(MagickWand $reference, int $metric): float

Метод возвращает выбранное численное сравнение, не изображение разницы. В проверенной библиотеке RMSE учитывает активные каналы/маски; при прозрачности участвуют правила alpha, поэтому без подготовки нельзя читать его как простую разницу RGB-байтов. Для прозрачных источников отдельно проверяем alpha, а для этого урока оба объекта накладываются OVER на белый фон, получают TRUECOLOR и проверку alpha=false. На этих непрозрачных одинаковых RGB-видах RMSE — корень среднего квадрата нормированных компонентных различий. Это native Q16 до сохранения PNG8.

Разные размеры не обязаны вызвать исключение: библиотека может сравнивать с виртуальными пикселями. Наш контроль 399×320 и 400×320 вернул 0, поэтому проверку геометрии выполняет вызывающий код. Нулевое число не доказывает совпадение файлов, метаданных, скрытого RGB при alpha=0 или субъективного качества. Для точного растра нужна отдельная проверка компонент. Выбор произвольного integer вместо METRIC_* также не гарантирует исключение: используйте подходящую именованную константу. Метод может записать служебное свойство distortion у текущего объекта, не меняя его пиксельный растр.

Официальный раздел ошибок указывает ImagickException при ошибке. В реальном запуске пустой объект FX, пустая LUT и пустое изображение-ссылка вызвали ImagickException; синтаксически незавершённое выражение u+ также вызвало ImagickException. Неверный PHP-тип аргумента обрабатывается PHP-проверкой типов. Не заменяйте обработку ошибки проверкой файла по одному числу: чтение пути, доступ к записи и наличие coder — отдельные этапы.

На этой официальной странице нет примера кода. Два самостоятельных применения ниже добавлены к полному базовому описанию: сравнение копии/правки и выбор точности сохранённого цветового рецепта.

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

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>float</type><methodname>Imagick::getImageDistortion</methodname>
   <methodparam><type>MagickWand</type><parameter>reference</parameter></methodparam>
   <methodparam><type>int</type><parameter>metric</parameter></methodparam>
  </methodsynopsis>
  <para>
   Compares an image to a reconstructed image and returns the specified distortion
   metric.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>reference</parameter></term>
     <listitem>
      <para>
      Imagick object to compare to.
      </para>
     </listitem>
    </varlistentry>
    <varlistentry>
     <term><parameter>metric</parameter></term>
     <listitem>
      <para>
      One of the <link linkend="imagick.constants.metric">metric type constants</link>.
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
  </para>
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   Returns the distortion metric used on the image (or the best guess
   thereof).
  </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
-->

Сравнить точную копию и видимую правку по одной заданной метрике

Точная копия даёт RMSE 0; изменённая картинка — положительное float-значение, записанное полностью в metrics.json. Перед сравнением получены одинаковые непрозрачные RGB-виды OVER-white с TRUECOLOR. Метод сохраняет служебное свойство distortion у сравниваемого объекта, но не меняет его пиксели.

Сравнить точную копию и видимую правку по одной заданной метрикеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$changed = $source->fxImage($expression,$rgb);
[$left,$right] = fxHaldMetricViews($source,$changed);
$exactCopy=clone $left; $before=fxHaldPixelHash($left);
$zero=$left->getImageDistortion($exactCopy,Imagick::METRIC_ROOTMEANSQUAREDERROR);
$error=$left->getImageDistortion($right,Imagick::METRIC_ROOTMEANSQUAREDERROR);
$property=$left->getImageProperty('distortion');
saveFxHaldPNG($left,'metric-reference.png'); saveFxHaldPNG($exactCopy,'metric-copy.png'); saveFxHaldPNG($right,'metric-changed.png');
saveFxHaldComparison([$sourceWhite,new Imagick(__DIR__.'/metric-copy.png'),new Imagick(__DIR__.'/metric-changed.png')],'metric-minimal-comparison.png');
saveFxHaldDiagnostic('fx-hald-getimagedistortion-minimal',['kind'=>'metric-minimal','metrics'=>['copy_Q16_RMSE'=>$zero,'changed_Q16_RMSE'=>$error],'metric_views'=>[fxHaldState($left),fxHaldState($right)],'return_type'=>get_debug_type($error),'distortion_property'=>$property,'metric_source_pixels_unchanged'=>$before===fxHaldPixelHash($left)]);
echo "metric-minimal-comparison.png\nmetric-reference.png\nmetric-copy.png\nmetric-changed.png\nmetrics.json\n";
printf("copy=%.6f changed-positive=%d alpha-free=%d pixels-unchanged=%d\n",$zero,(int)($error>0),(int)(!$left->getImageAlphaChannel()&&!$right->getImageAlphaChannel()),(int)($before===fxHaldPixelHash($left)));

Точная копия даёт RMSE 0; изменённая картинка — положительное float-значение, записанное полностью в metrics.json. Перед сравнением получены одинаковые непрозрачные RGB-виды OVER-white с TRUECOLOR. Метод сохраняет служебное свойство distortion у сравниваемого объекта, но не меняет его пиксели. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
metric-minimal-comparison.png
metric-reference.png
metric-copy.png
metric-changed.png
metrics.json
copy=0.000000 changed-positive=1 alpha-free=1 pixels-unchanged=1

Выбрать точность приближения и отдельно увидеть усиленную разницу

Для собственной формулы выбирается вариант с меньшей RMSE по фиксированной политике. Порог 0.001 — правило этого проекта, не универсальная граница качества. В metrics.json раздельно сохранены Q16 измерения до PNG8-доставки и измерения повторно прочитанных PNG8. Последняя панель — реальная native разница coarse/direct, умноженная на 32 с отсечением: это не исходная ошибка в натуральном цвете.

Выбрать точность приближения и отдельно увидеть усиленную разницуПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/fx-hald-fixture.php';
restoreFxHaldInput();
$source = new Imagick(__DIR__ . '/source.png');
$sourceWhite = fxHaldPreview($source);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$expression = 'u^0.8';
$direct=$source->fxImage($expression,$rgb); saveFxHaldPNG($direct,'metric-direct.png',true);
$errors=[]; $savedErrors=[]; $tables=[]; $results=[]; $best=null; $bestError=INF;
foreach (['coarse16'=>[3,16],'fine16'=>[6,16],'fine8'=>[6,8]] as $name=>[$level,$depth]) {
    $edge=$level ** 3; $identity=new Imagick(); $identity->newPseudoImage($edge,$edge,'hald:'.$level);
    $recipe=$identity->fxImage($expression,$rgb); saveFxHaldPNG($recipe,$name.'-lut.png',false,$depth);
    $table=new Imagick(__DIR__.'/'.$name.'-lut.png');
    $output=clone $source; $output->haldClutImage($table,$rgb);
    saveFxHaldPNG($output,$name.'-result.png',true); saveFxHaldPNG($table,$name.'-preview8.png');
    [$a,$b]=fxHaldMetricViews($direct,$output); $errors[$name]=$a->getImageDistortion($b,Imagick::METRIC_ROOTMEANSQUAREDERROR);
    [$savedA,$savedB]=fxHaldMetricViews(new Imagick(__DIR__.'/metric-direct.png'),new Imagick(__DIR__.'/'.$name.'-result.png'));
    $savedErrors[$name]=$savedA->getImageDistortion($savedB,Imagick::METRIC_ROOTMEANSQUAREDERROR);
    $results[$name]=$output; $tables[$name.'-lut.png']=fxHaldTableInfo($table,$level,$name.'-preview8.png',$recipe);
    if ($errors[$name]<$bestError) {$best=$name; $bestError=$errors[$name];}
}
$threshold=0.001;
[$reference,$coarse]=fxHaldMetricViews($direct,$results['coarse16']);
$difference=clone $reference; $difference->compositeImage($coarse,Imagick::COMPOSITE_DIFFERENCE,0,0);
$difference->evaluateImage(Imagick::EVALUATE_MULTIPLY,32,$rgb); saveFxHaldPNG($difference,'difference32.png');
saveFxHaldComparison([$sourceWhite,fxHaldPreview(new Imagick(__DIR__.'/metric-direct.png')),fxHaldPreview(new Imagick(__DIR__.'/coarse16-result.png')),new Imagick(__DIR__.'/difference32.png')],'metric-choice-comparison.png');
saveFxHaldDiagnostic('fx-hald-getimagedistortion-workflow',['kind'=>'metric-workflow','expression'=>$expression,'tables'=>$tables,'metrics'=>['native_Q16_RMSE'=>$errors,'saved_PNG8_RMSE'=>$savedErrors],'threshold'=>$threshold,'chosen'=>$best,'chosen_pass'=>$bestError<$threshold,'metric_views'=>[fxHaldState($reference),fxHaldState($coarse)],'difference'=>['filename'=>'difference32.png','factor'=>32,'operator'=>'native COMPOSITE_DIFFERENCE then EVALUATE_MULTIPLY; Q16 clamp before PNG8','domain'=>'native coarse16 and direct opaque TRUECOLOR white views']]);
echo "metric-choice-comparison.png\nmetric-direct.png\ncoarse16-lut.png\ncoarse16-result.png\ncoarse16-preview8.png\nfine16-lut.png\nfine16-result.png\nfine16-preview8.png\nfine8-lut.png\nfine8-result.png\nfine8-preview8.png\ndifference32.png\nmetrics.json\n";
printf("chosen=%s pass=%d coarse-pass=%d fine-pass=%d\n",$best,(int)($bestError<$threshold),(int)($errors['coarse16']<$threshold),(int)($errors['fine16']<$threshold));

Для собственной формулы выбирается вариант с меньшей RMSE по фиксированной политике. Порог 0.001 — правило этого проекта, не универсальная граница качества. В metrics.json раздельно сохранены Q16 измерения до PNG8-доставки и измерения повторно прочитанных PNG8. Последняя панель — реальная native разница coarse/direct, умноженная на 32 с отсечением: это не исходная ошибка в натуральном цвете. Сохраните example.php, source.png.b64 и fx-hald-fixture.php из архива в одной папке. Запустите php example.php с расширением Imagick, PNG и HALD coder. Требуется запись в эту папку; все входы и рецепты локальные. Helper опубликован целиком: он восстанавливает собственный PNG, сохраняет результаты и готовит виды для просмотра, но не вызывает fxImage, haldClutImage или getImageDistortion. Эти три действия видны в самой программе.

Ожидаемый вывод CLI и имена созданных файлов
metric-choice-comparison.png
metric-direct.png
coarse16-lut.png
coarse16-result.png
coarse16-preview8.png
fine16-lut.png
fine16-result.png
fine16-preview8.png
fine8-lut.png
fine8-result.png
fine8-preview8.png
difference32.png
metrics.json
chosen=fine16 pass=1 coarse-pass=0 fine-pass=1
Источники и обсуждения

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

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

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

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