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

Дерево руководства
Глава 401 · После объектов, массивов и основ Imagick
Imagick: bias свёртки, отрицательные края и современные единицы
Построить карту перепадов без потери знака, различить QuantumRange и проценты, перенести исторические setters на проверенный путь IM7.
Получить карту двух направлений перепада
Для служебного превью таблички нужно показать вертикальные перепады: где начинается светлая полоска и где она заканчивается. Обычный фильтр разности может оставить только одну сторону. Сделаем карту, на которой отсутствие перепада выглядит серым, один знак разности — темнее, другой — светлее. Исходную табличку сохраним отдельно. Это полезно для визуальной проверки границ; решение о качестве всей фотографии такая карта сама не принимает.

Bias здесь — добавка к результату расчёта каждого пикселя. Представьте шкалу перепадов с нулём посередине: отрицательное и положительное можно показать по разные стороны серого. Аналогия описывает отображение чисел; пиксели исходника при одной установке bias никуда не перемещаются и ещё не меняют цвет. Сначала задаётся настройка, затем операция использует её.
У изображения есть цветовые каналы R, G и B: числовые составляющие красного, зелёного и синего. Ядро — небольшая таблица коэффициентов для соседних пикселей. Начнём с одной строки ненулевых чисел внутри таблицы 3×3: получим разность между левым и правым соседом. Остальные коэффициенты равны нулю. База для продолжения — массивы, объекты и вызов методов и ядро фильтра; текущий порядок действий разберём здесь.
$image = new Imagick(__DIR__ . "/plaque.png");
$kernel = ImagickKernel::fromMatrix([
[0, 0, 0],
[-0.5, 0, 0.5],
[0, 0, 0],
]);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$image->setImageArtifact("convolve:bias", "50%");
$image->convolveImage($kernel, $rgb);
$image->setImageFormat("png");
$image->writeImage(__DIR__ . "/edges.png");new Imagick читает plaque.png в объект $image. Знак = сохраняет значение в переменную; -> вызывает метод объекта. Квадратные скобки создают массив строк ядра, запятые разделяют числа. ImagickKernel::fromMatrix — вызов фабрики через ::, создающий объект ядра. Вертикальная черта | объединяет флаги трёх каналов; это побитовое объединение, а не условное «или». Строка "50%" содержит знак процента, поэтому означает долю диапазона. writeImage сохраняет изображение на диск.
Ожидаемый результат короткого фрагмента — edges.png с серым фоном и светлыми/тёмными вертикальными линиями, как справа на иллюстрации. Для него нужен plaque.png; полный архив ниже включает тот же собственный вход в base64 и восстанавливает PNG автоматически. В текущем IM7 convolveImage принимает ImagickKernel. Официальная PHP.net-страница пока показывает массив из исторической ветки; передача массива в проверенную сборку вызвала TypeError. Наш код использует подтверждённую текущую форму.
Показать обе стороны перепада яркости
На серой карте видны обе стороны перехода: в Q16 плоская область 32768, нарастающий край 24544, убывающий 40992. Без bias отрицательный отклик обрезан до 0. Оба исторических метода в проверенной IM7-сборке отсутствуют; выполнены setImageArtifact и convolveImage.
<?php
declare(strict_types=1);
require __DIR__ . '/bias-fixture.php';
biRestore();
$source = new Imagick(__DIR__ . '/plaque.png');
$sourceBefore = biState($source);
$sourceHash = hash_file('sha256', __DIR__ . '/plaque.png');
$matrix = [[0, 0, 0], [-0.5, 0, 0.5], [0, 0, 0]];
$kernel = ImagickKernel::fromMatrix($matrix);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$plain = clone $source;
$plain->convolveImage($kernel, $rgb);
$biased = clone $source;
$setReturn = $biased->setImageArtifact('convolve:bias', '50%');
$beforeConvolve = biState($biased);
$convolveReturn = $biased->convolveImage($kernel, $rgb);
$images = [
'source'=>biImage($source,'first-source8.png'),
'plain'=>biImage($plain,'first-plain8.png'),
'biased'=>biImage($biased,'first-biased8.png'),
];
$rects = biCompare([$source,$plain,$biased],'image-bias-first.png');
$errors = [
'wrong_array'=>biTry(fn()=>(clone $source)->convolveImage([0,0,0,-0.5,0,0.5,0,0,0],$rgb)),
'missing_input'=>biTry(fn()=>new Imagick(__DIR__.'/missing.png')),
];
biDiagnostic('image-bias-first',['ImagickKernel::fromMatrix','setImageArtifact','convolveImage'],[
'bias'=>'50%','bias_quantum'=>32768,'channel'=>$rgb,'set_return'=>$setReturn,'convolve_return'=>$convolveReturn,'before_convolve'=>$beforeConvolve,'errors'=>$errors,
'images'=>$images,'panel_keys'=>['source','plain','biased'],'panel_geometry'=>$rects,
'source_before'=>$sourceBefore,'source_after'=>biState($source),'source_sha256'=>$sourceHash,
'source_unchanged'=>biState($source)===$sourceBefore&&hash_file('sha256',__DIR__.'/plaque.png')===$sourceHash,
'checks'=>['setting_does_not_change_pixels'=>$beforeConvolve===$sourceBefore,'both_returns_true'=>$setReturn===true&&$convolveReturn===true,'flat_zero'=>$images['plain']['pixels']['10,10']===[0,0,0],'flat_midpoint'=>$images['biased']['pixels']['10,10']===[32768,32768,32768],'negative_edge_retained'=>$images['biased']['pixels']['39,80']===[24544,24544,24544],'positive_edge_retained'=>$images['biased']['pixels']['119,80']===[40992,40992,40992],'wrong_array_caught'=>($errors['wrong_array']['error']['class']??null)==='TypeError','missing_caught'=>($errors['missing_input']['error']['class']??null)==='ImagickException']
]);
echo "image-bias-first.png\nfirst-source8.png\nfirst-plain8.png\nfirst-biased8.png\nmetrics.json\nQ16 flat=32768 rising=24544 falling=40992 legacy=unavailable\n";
На серой карте видны обе стороны перехода: в Q16 плоская область 32768, нарастающий край 24544, убывающий 40992. Без bias отрицательный отклик обрезан до 0. Оба исторических метода в проверенной IM7-сборке отсутствуют; выполнены setImageArtifact и convolveImage. Архив: example.php, plaque.png.b64, bias-fixture.php. Распакуйте рядом; php example.php восстановит собственный PNG. Проверенная среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 non-HDRI, Windows. Здесь convolveImage принимает ImagickKernel. Исторические setter не вызывались.
Ожидаемый вывод настоящего запуска
image-bias-first.png
first-source8.png
first-plain8.png
first-biased8.png
metrics.json
Q16 flat=32768 rising=24544 falling=40992 legacy=unavailable
В полном примере clone создаёт отдельную копию для каждого варианта. Сравнение пикселей до convolveImage показывает: setImageArtifact ещё ничего не перекрасил. Затем обе операции вернули true. Файл metrics.json содержит реальные значения Q16, SHA256 исходника и выходов. В этой сборке оба старых имени setImageBias и setImageBiasQuantum отсутствуют; текущие рабочие программы от них не зависят.
Карта направленная: ядро сравнивает соседей слева и справа, поэтому подчёркивает вертикальные границы. Горизонтальные края здесь не являются целью. Для другого направления нужно другое ядро; сильный шум тоже создаёт перепады. Bias сохраняет знак выбранного отклика, но не распознаёт, где полезная деталь, а где шум.
Не перепутать долю, проценты и квантовые единицы

В старом проекте встречаются два похожих имени setter, но число 0.5 у них имело разный смысл. QuantumRange — наибольшее значение обычного канала в данной сборке. Здесь Q16: от 0 до 65535. Это внутренняя шкала обработки; исходный PNG RGB8 имеет 0..255, при чтении значения масштабируются. Например, серый 128 превращается в 128 × 257 = 32896.
$range = Imagick::getQuantumRange()["quantumRangeLong"]; // 65535
$halfRange = 0.5 * $range; // 32767.5
$image->setImageArtifact("convolve:bias", "50%"); // Здесь 32768
// Строка "0.5" задала бы половину одного кванта.getQuantumRange возвращает массив; ключ quantumRangeLong выбирает целое число диапазона. Звёздочка * умножает долю на это число. Исторический setImageBias ожидал такую абсолютную величину, а setImageBiasQuantum масштабировал нормированную долю сам. В современном артефакте признак единицы находится прямо в строке: знак % существенен.
По исходникам ImageMagick 7.1.1-46 процентная строка рассчитывается относительно QuantumRange + 1: 50% от 65536 равно 32768. Старое нормирование 0.5 × 65535 даёт 32767.5. Это разные запросы. На нашем растре после округления в целочисленной Q16 получились одинаковые пиксели; нельзя переносить это равенство на любой вход и любую HDRI-сборку.
Различить долю QuantumRange, проценты и число квантов
Запросы 32767.5 и 50% означают немного разные смещения, но на этом входе после округления Q16 дают одинаковые пиксели. Строка 0.5 означает половину одного кванта: плоская область равна 1 вместо 32768 и выглядит чёрной.
<?php
declare(strict_types=1);
require __DIR__ . '/bias-fixture.php';
biRestore();
$source = new Imagick(__DIR__ . '/plaque.png');
$sourceBefore = biState($source);
$sourceHash = hash_file('sha256', __DIR__ . '/plaque.png');
$matrix = [[0, 0, 0], [-0.5, 0, 0.5], [0, 0, 0]];
$kernel = ImagickKernel::fromMatrix($matrix);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$range = Imagick::getQuantumRange()['quantumRangeLong'];
$fraction = 0.5;
$rawBias = $fraction * $range;
$raw = clone $source;
$raw->setImageArtifact('convolve:bias',sprintf('%.17g',$rawBias));
$raw->convolveImage($kernel,$rgb);
$percent = clone $source;
$percent->setImageArtifact('convolve:bias','50%');
$percent->convolveImage($kernel,$rgb);
$numeric = clone $source;
$numeric->setImageArtifact('convolve:bias','0.5');
$numeric->convolveImage($kernel,$rgb);
$images = [
'raw'=>biImage($raw,'units-raw8.png'),
'percent'=>biImage($percent,'units-percent8.png'),
'numeric'=>biImage($numeric,'units-numeric8.png'),
];
$rects = biCompare([$raw,$percent,$numeric],'image-bias-units.png');
biDiagnostic('image-bias-units',['ImagickKernel::fromMatrix','getQuantumRange','setImageArtifact','convolveImage'],[
'channel'=>$rgb,'fraction'=>$fraction,'range'=>$range,'raw_bias'=>$rawBias,'percent_bias_quantum'=>($range+1)*0.5,'numeric_bias_quantum'=>0.5,'max_native_difference_raw_percent'=>biMaxDifference($raw,$percent),
'images'=>$images,'panel_keys'=>['raw','percent','numeric'],'panel_geometry'=>$rects,
'source_before'=>$sourceBefore,'source_after'=>biState($source),'source_sha256'=>$sourceHash,
'source_unchanged'=>biState($source)===$sourceBefore&&hash_file('sha256',__DIR__.'/plaque.png')===$sourceHash,
'checks'=>['range_is_65535'=>$range===65535,'raw_is_half_range'=>$rawBias===32767.5,'percent_uses_range_plus_one'=>($range+1)*0.5===32768.0,'native_rounding_equal_for_this_input'=>biHash($raw)===biHash($percent),'numeric_is_not_percent'=>biHash($numeric)!==biHash($percent),'numeric_flat_one'=>$images['numeric']['pixels']['10,10']===[1,1,1]]
]);
echo "image-bias-units.png\nunits-raw8.png\nunits-percent8.png\nunits-numeric8.png\nmetrics.json\nraw=32767.5 percent=32768 numeric=0.5 native-raw-percent=equal flat-numeric=1\n";
Запросы 32767.5 и 50% означают немного разные смещения, но на этом входе после округления Q16 дают одинаковые пиксели. Строка 0.5 означает половину одного кванта: плоская область равна 1 вместо 32768 и выглядит чёрной. Архив: example.php, plaque.png.b64, bias-fixture.php. Распакуйте рядом; php example.php восстановит собственный PNG. Проверенная среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 non-HDRI, Windows. Здесь convolveImage принимает ImagickKernel. Исторические setter не вызывались.
Ожидаемый вывод настоящего запуска
image-bias-units.png
units-raw8.png
units-percent8.png
units-numeric8.png
metrics.json
raw=32767.5 percent=32768 numeric=0.5 native-raw-percent=equal flat-numeric=1
| Настройка в текущем примере | Запрошенная добавка | Плоская область Q16 |
|---|---|---|
| "32767.5" | 32767.5 кванта | 32768 после округления |
| "50%" | 32768 квантов | 32768 |
| "0.5" | 0.5 кванта | 1 после округления |
Картинки на странице — реально сохранённые RGB8 PNG. У них другая точность, чем у чисел в metrics.json: в этом кодере Q16=32768 записалось как RGB8=127. Равенство или различие двух PNG не заменяет сравнение внутренних Q16-значений. Программа хранит оба вида контрольных сумм и отдельно измеряет максимальную разность между абсолютной половиной диапазона и 50%; здесь она равна 0.
Выбирайте одну единицу на границе собственной функции. Ниже функция принимает проценты, а не неопределённое число bias. Это избавляет вызывающий код от догадки, означает ли 0.5 половину диапазона или половину кванта. Если нужна точная абсолютная величина, передайте строку без % и зафиксируйте QuantumRange вместе с результатом.
Добавить смещение до потери отрицательных значений

Теперь проверим распространённую попытку миграции: сначала convolveImage, затем прибавить серый фон через evaluateImage. В non-HDRI сборке промежуточный канал хранится в ограниченном целочисленном диапазоне. Отрицательное значение при записи результата свёртки становится 0. Поздняя арифметика видит этот ноль и уже не знает, было до него −8224 или −1000.
Возьмём точку (39,80), непосредственно слева от светлой полосы. Её левый сосед равен 32896, правый — 49344. При свёртке ядро разворачивается относительно центра, поэтому для нашей записанной строки −0.5/0/0.5 отклик равен 0.5 × левый − 0.5 × правый = −8224. Это именно свёртка; применение таблицы без разворота называется корреляцией и изменило бы знак.
// Правильный порядок для текущей целочисленной сборки:
$early = clone $source;
$early->setImageArtifact("convolve:bias", "50%");
$early->convolveImage($kernel, $rgb);
// Контрольная ошибка: информация уже потеряна внутри convolveImage.
$late = clone $source;
$late->convolveImage($kernel, $rgb);
$late->evaluateImage(Imagick::EVALUATE_ADD, 32768, $rgb);| Шаг | До ограничения диапазона | Результат в Q16 |
|---|---|---|
| Bias внутри свёртки | −8224 + 32768 = 24544 | 24544, тёмная сторона края |
| Свёртка без bias | −8224 | 0 после обрезки |
| Поздний ADD после обрезки | 0 + 32768 | 32768, край слился с фоном |
Сравнить смещение внутри свёртки и позднюю прибавку
Отклик в точке (39,80) равен −8224. Правильное добавление 32768 до обрезки даёт 24544; после обрезки до 0 поздняя прибавка даёт 32768. Этот край сливается с фоном, и потерянный знак уже не восстановить.
<?php
declare(strict_types=1);
require __DIR__ . '/bias-fixture.php';
biRestore();
$source = new Imagick(__DIR__ . '/plaque.png');
$sourceBefore = biState($source);
$sourceHash = hash_file('sha256', __DIR__ . '/plaque.png');
$matrix = [[0, 0, 0], [-0.5, 0, 0.5], [0, 0, 0]];
$kernel = ImagickKernel::fromMatrix($matrix);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$plain = clone $source;
$plain->convolveImage($kernel,$rgb);
$late = clone $plain;
$late->evaluateImage(Imagick::EVALUATE_ADD,32768,$rgb);
$early = clone $source;
$early->setImageArtifact('convolve:bias','50%');
$early->convolveImage($kernel,$rgb);
$images = [
'plain'=>biImage($plain,'order-plain8.png'),
'late'=>biImage($late,'order-late8.png'),
'early'=>biImage($early,'order-early8.png'),
];
$rects = biCompare([$plain,$late,$early],'image-bias-order.png');
$input = $source->exportImagePixels(38,80,3,1,'R',Imagick::PIXEL_SHORT);
$signed = 0.5 * $input[0] - 0.5 * $input[2];
biDiagnostic('image-bias-order',['ImagickKernel::fromMatrix','convolveImage','evaluateImage','setImageArtifact'],[
'channel'=>$rgb,'sample'=>['x'=>39,'y'=>80,'input_left_center_right'=>$input,'signed_response'=>$signed,'bias'=>32768,'correct_before_clipping'=>$signed+32768,'clipped_response'=>max(0,$signed),'late_after_clipping'=>max(0,$signed)+32768],
'images'=>$images,'panel_keys'=>['plain','late','early'],'panel_geometry'=>$rects,
'source_before'=>$sourceBefore,'source_after'=>biState($source),'source_sha256'=>$sourceHash,
'source_unchanged'=>biState($source)===$sourceBefore&&hash_file('sha256',__DIR__.'/plaque.png')===$sourceHash,
'checks'=>['signed_negative'=>$signed===-8224.0,'before_clipping_retains_edge'=>$images['early']['pixels']['39,80']===[24544,24544,24544],'plain_clips_negative'=>$images['plain']['pixels']['39,80']===[0,0,0],'late_loses_edge'=>$images['late']['pixels']['39,80']===[32768,32768,32768],'positive_edge_same'=>$images['late']['pixels']['119,80']===$images['early']['pixels']['119,80'],'outputs_different'=>biHash($late)!==biHash($early)]
]);
echo "image-bias-order.png\norder-plain8.png\norder-late8.png\norder-early8.png\nmetrics.json\nsigned=-8224 plain=0 late=32768 early=24544 lost-edge=cannot-recover\n";
Отклик в точке (39,80) равен −8224. Правильное добавление 32768 до обрезки даёт 24544; после обрезки до 0 поздняя прибавка даёт 32768. Этот край сливается с фоном, и потерянный знак уже не восстановить. Архив: example.php, plaque.png.b64, bias-fixture.php. Распакуйте рядом; php example.php восстановит собственный PNG. Проверенная среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 non-HDRI, Windows. Здесь convolveImage принимает ImagickKernel. Исторические setter не вызывались.
Ожидаемый вывод настоящего запуска
image-bias-order.png
order-plain8.png
order-late8.png
order-early8.png
metrics.json
signed=-8224 plain=0 late=32768 early=24544 lost-edge=cannot-recover
На противоположной стороне полосы в точке (119,80) отклик положительный: 8224. Там оба пути дают 40992. Поэтому проверка одного удобного пикселя может скрыть ошибку; мы сравниваем два направления и хеш всего растра. В коде вычисление $signed независимо восстанавливает ожидаемую разность из трёх исходных соседей.
Это не означает, что любая добавка после любой свёртки всегда неправильна. В HDRI промежуточные значения могут сохраняться вне обычного диапазона; другая цепочка и её конечное кодирование требуют собственной проверки. Здесь доказано конкретное различие на Q16 non-HDRI. Также bias не гарантирует отсутствие clipping для произвольного сильного ядра: сумма коэффициентов и их масштаб определяют возможный отклик.
Наше ядро имеет сумму 0: одинаковые соседи дают ноль отклика. Добавление одной и той же яркости к исходному изображению не эквивалентно добавке к выходу такого фильтра — одинаковая константа вычтется. Настройка convolve:bias действует на аккумулятор результата до ClampToQuantum. Подробности других ядер и очистки изображения разобраны в главе о сканах.
Собрать функцию превью с понятными единицами и отказами

Превратим эксперимент в повторяемый шаг сервиса: получить одно изображение, проверить процент смещения, сделать копию и сохранить карту выбранных каналов. У вызывающего кода теперь один понятный параметр biasPercent. Служебные настройки не должны случайно попасть в следующую свёртку того же объекта, поэтому функция удаляет свой convolve:bias в finally.
$preview = edgePreview($source, 50.0, $rgb);
$redPreview = edgePreview($source, 50.0, Imagick::CHANNEL_RED);Это два вызова одной функции, определённой в полном примере ниже. Первый аргумент — исходный объект, второй — процент, третий — выбранные каналы. Функция возвращает новый Imagick, который присваивается переменной. Внутри if проверяет условия, throw сообщает об отказе, clone отделяет результат от исходника, а return отдаёт готовую копию. finally выполняется и при успешной операции, и при исключении; основы функций и обработки исключений доступны отдельно.
Сохранить проверяемое превью и выбрать каналы
Функция создаёт RGB-превью и отдельный вариант только для R. В R-варианте G/B и альфа неизменны; настройка bias удалена из результата. Четыре недопустимых входа отклонены до обработки. JSON связывает исходник и реальные сохранённые PNG.
<?php
declare(strict_types=1);
require __DIR__ . '/bias-fixture.php';
biRestore();
$source = new Imagick(__DIR__ . '/plaque.png');
$sourceBefore = biState($source);
$sourceHash = hash_file('sha256', __DIR__ . '/plaque.png');
$matrix = [[0, 0, 0], [-0.5, 0, 0.5], [0, 0, 0]];
$kernel = ImagickKernel::fromMatrix($matrix);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
function edgePreview(Imagick $input,float $biasPercent,int $channels):Imagick
{
if ($input->getNumberImages() !== 1) {
throw new InvalidArgumentException('Exactly one image required');
}
if (!is_finite($biasPercent) || $biasPercent < 0 || $biasPercent > 100) {
throw new InvalidArgumentException('Bias must be finite and between 0 and 100 percent');
}
$copy = clone $input;
if ($copy->getImageArtifact('convolve:scale') !== null) {
$copy->deleteImageArtifact('convolve:scale');
}
$copy->setImageArtifact('convolve:bias',sprintf('%.17g%%',$biasPercent));
try {
$filter = ImagickKernel::fromMatrix([[0,0,0],[-0.5,0,0.5],[0,0,0]]);
$copy->convolveImage($filter,$channels);
} finally {
$copy->deleteImageArtifact('convolve:bias');
}
return $copy;
}
$all = edgePreview($source,50.0,$rgb);
$red = edgePreview($source,50.0,Imagick::CHANNEL_RED);
$errors = [
'empty'=>biTry(fn()=>edgePreview(new Imagick(),50.0,$rgb)),
'negative'=>biTry(fn()=>edgePreview($source,-1.0,$rgb)),
'not_finite'=>biTry(fn()=>edgePreview($source,NAN,$rgb)),
'too_large'=>biTry(fn()=>edgePreview($source,101.0,$rgb)),
];
$images = [
'source'=>biImage($source,'workflow-source8.png'),
'rgb'=>biImage($all,'workflow-rgb8.png'),
'red'=>biImage($red,'workflow-red8.png'),
];
$rects = biCompare([$source,$all,$red],'image-bias-workflow.png');
$manifest = ['input'=>'plaque.png','input_sha256'=>$sourceHash,'bias_percent'=>50,'kernel'=>$matrix,'outputs'=>['rgb'=>$images['rgb']['encoded_png'],'red'=>$images['red']['encoded_png']],'scope'=>'Directional preview; not automatic quality classification; original remains unchanged'];
file_put_contents(__DIR__.'/edge-preview.json',json_encode($manifest,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)."\n");
biDiagnostic('image-bias-workflow',['ImagickKernel::fromMatrix','getNumberImages','setImageArtifact','convolveImage','deleteImageArtifact'],[
'channels'=>['rgb'=>$rgb,'red'=>Imagick::CHANNEL_RED],'bias_percent'=>50,'errors'=>$errors,'artifact_after'=>['rgb'=>$all->getImageArtifact('convolve:bias'),'red'=>$red->getImageArtifact('convolve:bias')],'manifest'=>$manifest,'manifest_sha256'=>hash_file('sha256',__DIR__.'/edge-preview.json'),
'images'=>$images,'panel_keys'=>['source','rgb','red'],'panel_geometry'=>$rects,
'source_before'=>$sourceBefore,'source_after'=>biState($source),'source_sha256'=>$sourceHash,
'source_unchanged'=>biState($source)===$sourceBefore&&hash_file('sha256',__DIR__.'/plaque.png')===$sourceHash,
'checks'=>['red_changed'=>biHash($red,'R')!==biHash($source,'R'),'green_preserved'=>biHash($red,'G')===biHash($source,'G'),'blue_preserved'=>biHash($red,'B')===biHash($source,'B'),'alpha_preserved'=>biHash($red,'A')===biHash($source,'A')&&biHash($all,'A')===biHash($source,'A'),'no_bias_leak'=>$all->getImageArtifact('convolve:bias')===null&&$red->getImageArtifact('convolve:bias')===null,'all_bad_inputs_caught'=>count(array_filter($errors,fn($e)=>($e['error']['class']??null)==='InvalidArgumentException'))===4,'manifest_bound'=>$manifest['outputs']['rgb']['sha256']===hash_file('sha256',__DIR__.'/workflow-rgb8.png')&&$manifest['outputs']['red']['sha256']===hash_file('sha256',__DIR__.'/workflow-red8.png')]
]);
echo "image-bias-workflow.png\nworkflow-source8.png\nworkflow-rgb8.png\nworkflow-red8.png\nedge-preview.json\nmetrics.json\npreview=RGB-or-R GBA-preserved=yes bias-cleared=yes rejected=4\n";
Функция создаёт RGB-превью и отдельный вариант только для R. В R-варианте G/B и альфа неизменны; настройка bias удалена из результата. Четыре недопустимых входа отклонены до обработки. JSON связывает исходник и реальные сохранённые PNG. Архив: example.php, plaque.png.b64, bias-fixture.php. Распакуйте рядом; php example.php восстановит собственный PNG. Проверенная среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 non-HDRI, Windows. Здесь convolveImage принимает ImagickKernel. Исторические setter не вызывались.
Ожидаемый вывод настоящего запуска
image-bias-workflow.png
workflow-source8.png
workflow-rgb8.png
workflow-red8.png
edge-preview.json
metrics.json
preview=RGB-or-R GBA-preserved=yes bias-cleared=yes rejected=4
В каждом цветном пятне компоненты RGB различаются. RGB-вариант строит разности каждого из трёх каналов; вариант R меняет только красную составляющую, сохраняя значения зелёной и синей. Поэтому результат R может выглядеть цветным — это проверка выбора каналов, а не обещание сохранить исходные цвета полноценного emboss. Наш исходник непрозрачный; равенство хеша альфы здесь подтверждает сохранение непрозрачности, но не тестирует сложные полупрозрачные края.
Четыре отказа проверены явно: пустой Imagick, −1%, NaN и 101%. Все дают InvalidArgumentException до свёртки. Ограничение 0..100 — политика нашей функции, а не объявление всех значений вне него запрещёнными библиотекой. Для входа допускается ровно один кадр; многокадровый файл требует отдельного решения. Функция также удаляет у копии convolve:scale, чтобы скрытая прежняя настройка не меняла наше фиксированное ядро.
После успешной обработки сохраняются workflow-rgb8.png, workflow-red8.png и edge-preview.json. JSON содержит матрицу, процент, хеш входного файла и хеши конкретных PNG; его можно хранить рядом с рабочим заданием. В metrics.json дополнительно есть значения каналов до PNG-кодирования. Исходник и его хеш не меняются. Имена файлов в учебном примере фиксированы; загрузку из формы и выбор пользовательского пути здесь не реализуем.
Цена обработки растёт с числом пикселей и размером ядра; в этом случае ядро фиксировано 3×3, а копии и экспорт пикселей для диагностики требуют дополнительной памяти. Для рабочего обработчика задайте предел геометрии и числа кадров до дорогих операций, сохраняйте нужные доказательства без многократного экспорта всего растра. Не обещайте ускорение без измерения своего входа.
Если нужна яркость обычной фотографии, начните с арифметики каналов, а не с bias свёртки. Если нужен художественный рельеф, выберите соответствующее ядро или операцию и отдельно проверьте цвета, альфу и диапазон. Наша небольшая функция решает конкретную задачу: воспроизводимая направленная карта перепадов с выбранными каналами.
Два исторических контракта и проверенная миграция
Imagick::setImageBias: исторический контракт
Imagick::setImageBias(float $bias): boolОфициальная доступность: PECL imagick 2, PECL imagick 3. Метод deprecated с Imagick 3.4.4. В нашей Imagick 3.8.1 с ImageMagick 7.1.1-46 method_exists вернул false: этот метод не вызывался. Wrapper включает обе старые функции только при сборке с ImageMagick 6. Пометка deprecated и фактическая доступность — разные вопросы; версия расширения сама по себе не сообщает версию подключённой библиотеки.
setImageBias принимает обязательный float $bias — смещение результата операций, которые выполняют свёртку изображения, например convolveImage. Значение задаётся в единицах квантового диапазона текущей сборки. Официальный результат — bool, true при успехе; при ошибке документация указывает ImagickException. Установка настройки сама по себе не является обработкой пикселей.
// Только чтение старого кода для совместимой IM6-сборки.
$range = Imagick::getQuantum();
$oldImage->setImageBias(0.5 * $range); // Q16: запрос 32767.5.
$oldImage->setImageBias(0.25 * $range); // Q16: запрос 16383.75.Официальный пример связывает четыре шага: прочитать изображение, умножить выбранную долю на Imagick::getQuantum(), задать bias до convolveImage и отдать PNG с заголовком Content-type: image/png. В нём используется направленное ядро с коэффициентами −0.70/0/0.70 в трёх строках и CHANNEL_ALL. Комментарий примера требует ImageMagick 6.9.0-1 для действия bias на convolveImage; это оговорка совместимости этого примера, а не доказательство наличия setter в IM7. Наши CLI-программы сохраняют PNG в файлы и не отправляют HTTP-заголовки.
Для Q8 и Q16 одно буквальное число означает разную долю диапазона, поэтому историческая формула читает QuantumRange. Не заменяйте старый вызов на evaluateImage(ADD) после свёртки: в целочисленной сборке отрицательные отклики к этому моменту уже могут быть потеряны. Современная замена для показанной задачи — артефакт convolve:bias до convolveImage; она не объявляется универсальным аналогом всех исторических эффектов.
| Задача | Как читать значение |
|---|---|
| Исторический setImageBias(32767.5) | Абсолютное смещение в квантовых единицах; значение рассчитано для Q16 |
| Исторический setImageBiasQuantum(0.5) | Нормированная доля QuantumRange согласно документации |
| Современный convolve:bias = "50%" | Процентная строка; в исследованном IM7 это 0.5 × (QuantumRange + 1) |
| Современный convolve:bias = "0.5" | Абсолютное число 0.5 кванта, не 50% |
Практические продолжения: свёртка и очистка скана, пиксели и ImagickKernel, свойства и артефакты изображения. Все четыре исполняемых примера этой главы используют современный путь; сравнение исторических единиц не выдаётся за выполненный deprecated-вызов.
Imagick::setImageBiasQuantum: исторический контракт
Imagick::setImageBiasQuantum(float $bias): voidОфициальная доступность: PECL imagick 3 >= 3.3.0. Метод deprecated с Imagick 3.4.4. В нашей Imagick 3.8.1 с ImageMagick 7.1.1-46 method_exists вернул false: этот метод не вызывался. Wrapper включает обе старые функции только при сборке с ImageMagick 6. Пометка deprecated и фактическая доступность — разные вопросы; версия расширения сама по себе не сообщает версию подключённой библиотеки.
setImageBiasQuantum принимает обязательный float $bias. Документация задаёт шкалу от 0 — без смещения — до 1, соответствующей QuantumRange. Например, 0.5 означает половину диапазона. Официальный возвращаемый тип — void: полезное возвращаемое значение использовать не следует. На странице нет собственного примера, дополнительных параметров и отдельного описания ошибок.
// Только чтение старого кода для совместимой IM6-сборки.
$oldImage->setImageBiasQuantum(0.5); // Половина QuantumRange.
$oldImage->setImageBiasQuantum(0.25); // Четверть QuantumRange.Это фрагменты исторического API, а не запущенные программы текущей сборки. В общей C-реализации Imagick 3.8.1 аргумент умножается на QuantumRange, затем вызывается MagickSetImageBias. Есть расхождение источников: PHP.net указывает float/void, stub 3.8.1 — string/void, а общая C-функция разбирает число double и содержит возврат true. Мы сохраняем официальный контракт и не обещаем конкретный результат исторического runtime, который здесь не запускался.
В исследованной C-ветке есть проверка непустого изображения и преобразование ошибки библиотеки в ImagickException; это чтение исходников, не проверка запуска IM6. Не рассчитывайте, что 0..1 автоматически валидируется как диапазон: приложение задаёт свою явную политику. В современной программе ниже мы принимаем проценты 0..100, проверяем конечность числа и используем артефакт с явным знаком %.
| Задача | Как читать значение |
|---|---|
| Исторический setImageBias(32767.5) | Абсолютное смещение в квантовых единицах; значение рассчитано для Q16 |
| Исторический setImageBiasQuantum(0.5) | Нормированная доля QuantumRange согласно документации |
| Современный convolve:bias = "50%" | Процентная строка; в исследованном IM7 это 0.5 × (QuantumRange + 1) |
| Современный convolve:bias = "0.5" | Абсолютное число 0.5 кванта, не 50% |
Практические продолжения: свёртка и очистка скана, пиксели и ImagickKernel, свойства и артефакты изображения. Все четыре исполняемых примера этой главы используют современный путь; сравнение исторических единиц не выдаётся за выполненный deprecated-вызов.
Запуск, ошибки и упражнения с ответами
Распакуйте три файла рядом и выполните php example.php. Нужны Imagick 3.8.1 и ImageMagick 7 в проверенной комбинации; здесь используется PHP 8.5.11 / ImageMagick 7.1.1-46 Q16 non-HDRI. Файлы результатов названы в stdout каждого примера. Все четыре программы работают с современным API. Наличие карточек двух deprecated-методов не означает, что они были исполнены: обе проверки method_exists дали false; среда IM6 не запускалась.
В первом примере неправильный тип ядра перехвачен как TypeError, отсутствующий файл — как ImagickException. Четвёртый пример заранее проверяет собственные входные условия. В коде нет подавления ошибок через @. Нулевой пиксель в правильной картинке не является ошибкой PHP: это значение, которое могло возникнуть из-за ограничения диапазона, и его смысл проверяется отдельно.
Упражнение 1: какую строку передать, если требуется половина диапазона в текущем артефакте? "50%", а не "0.5". Упражнение 2: почему 32767.5 и 50% дали одинаковые изображения? Запросы различаются, но этот исходник и округление Q16 дали одинаковые пиксели; это наблюдение, не тождество единиц. Упражнение 3: можно ли восстановить −8224 из уже сохранённого нуля? По одному нулю нельзя: отрицательных исходных значений было много. Упражнение 4: что будет с G и B при CHANNEL_RED? В проверенном примере они сохраняются. Упражнение 5: почему наша карта не содержит все горизонтальные края? Выбрано ядро разности соседей слева и справа; поворот задачи требует другого ядра.
Вспомогательный PHP из каждого архива
<?php
declare(strict_types=1);
function biRestore():void {
$hashes=json_decode('{"plaque.png":"91bcb66bfae4bfa8831198144f8d36cab0905f1fb6385c7eed6a7efcb6df4da1"}',true,512,JSON_THROW_ON_ERROR);
foreach($hashes as $name=>$hash){$raw=base64_decode((string)file_get_contents(__DIR__.'/'.$name.'.b64'),true);if($raw===false||hash('sha256',$raw)!==$hash||file_put_contents(__DIR__.'/'.$name,$raw)===false)throw new RuntimeException('Input missing or changed');}
if(!class_exists('ImagickKernel')||!method_exists(Imagick::class,'setImageArtifact'))throw new RuntimeException('ImagickKernel and image artifacts are required');
}
function biHash(Imagick $im,string $map='RGB'):string{return hash('sha256',json_encode($im->exportImagePixels(0,0,$im->getImageWidth(),$im->getImageHeight(),$map,Imagick::PIXEL_SHORT),JSON_THROW_ON_ERROR));}
function biState(Imagick $im):array{return ['geometry'=>[$im->getImageWidth(),$im->getImageHeight()],'RGB16_sha256'=>biHash($im),'RGBA16_sha256'=>biHash($im,'RGBA'),'R16_sha256'=>biHash($im,'R'),'G16_sha256'=>biHash($im,'G'),'B16_sha256'=>biHash($im,'B'),'A16_sha256'=>biHash($im,'A')];}
function biPixels(Imagick $im):array{$a=[];foreach([[10,10],[39,80],[40,80],[41,80],[119,80],[120,80],[121,80],[200,160]]as [$x,$y])$a[$x.','.$y]=$im->exportImagePixels($x,$y,1,1,'RGB',Imagick::PIXEL_SHORT);return $a;}
function biImage(Imagick $im,string $name):array{
$copy=clone $im;$copy->setImageFormat('png');$copy->setImageDepth(8);$copy->setOption('png:bit-depth','8');$copy->setOption('png:color-type','2');$copy->setOption('png:exclude-chunk','all');
if(!$copy->writeImage(__DIR__.'/'.$name))throw new RuntimeException('Cannot save PNG');$read=new Imagick(__DIR__.'/'.$name);
return biState($im)+['pixels'=>biPixels($im),'encoded_png'=>['filename'=>$name,'sha256'=>hash_file('sha256',__DIR__.'/'.$name),'RGB16_sha256'=>biHash($read),'RGBA16_sha256'=>biHash($read,'RGBA')],'display_basis'=>'Actual saved RGB8 PNG; diagnostic values are pre-encoding Q16 samples'];
}
function biCompare(array $images,string $name):array{$view=new Imagick();$view->newImage(400*count($images),320,'white','png');$rects=[];foreach($images as $i=>$im){$view->compositeImage($im,Imagick::COMPOSITE_COPY,400*$i,0);$rects[]=['x'=>400*$i,'y'=>0,'width'=>400,'height'=>320];}biImage($view,$name);return $rects;}
function biTry(callable $fn):array{try{$v=$fn();return ['type'=>get_debug_type($v),'value'=>$v];}catch(Throwable $e){return ['error'=>['class'=>get_class($e),'message'=>$e->getMessage()]];}}
function biMaxDifference(Imagick $a,Imagick $b):int{$x=$a->exportImagePixels(0,0,400,320,'RGB',Imagick::PIXEL_SHORT);$y=$b->exportImagePixels(0,0,400,320,'RGB',Imagick::PIXEL_SHORT);$m=0;foreach($x as $i=>$v)$m=max($m,abs($v-$y[$i]));return $m;}
function biDiagnostic(string $id,array $methods,array $data):void{
$available=['setImageBias'=>method_exists(Imagick::class,'setImageBias'),'setImageBiasQuantum'=>method_exists(Imagick::class,'setImageBiasQuantum')];
$doc=['schema'=>'aen-image-bias-program-diagnostics-v1','example_id'=>$id,'runtime'=>['php'=>PHP_VERSION,'imagick'=>phpversion('imagick'),'imagemagick'=>Imagick::getVersion(),'quantum'=>Imagick::getQuantumRange(),'hdri'=>Imagick::getHdriEnabled()],'legacy_available'=>$available,'legacy_executed'=>false,'target_method_executed'=>false,'legacy_runtime_executed'=>false,'current_runtime_executed'=>true,'invoked_methods'=>$methods,'executed_api'=>'setImageArtifact + convolveImage','kernel'=>[[0,0,0],[-0.5,0,0.5],[0,0,0]]]+$data;
if(file_put_contents(__DIR__.'/metrics.json',json_encode($doc,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)."\n")===false)throw new RuntimeException('Cannot save metrics');
}
biRestore проверяет и восстанавливает собственный PNG. biState и biPixels измеряют текущие Q16-значения; biImage сохраняет RGB8 и перечитывает настоящий файл для контрольных сумм. biCompare собирает панели без изменения масштаба, biDiagnostic записывает JSON. Основные настройки, вызовы свёртки и политика функции находятся в видимой программе, а не спрятаны во вспомогательном файле.
Реальные вопросы разработчиков
Эти обсуждения прочитаны как источники практических затруднений. Объяснения, исходник таблички и программы созданы для этой главы; технические контракты сверены с PHP.net и исходниками библиотеки, а поведение текущих примеров — с запуском.
setImageBias is not working on a LAMP box: Один PHP-код даёт разные результаты на двух версиях ImageMagick. Фиксируем версии PHP, расширения и библиотеки, проверяем доступность методов; текущий IM7-путь вызывается явно.
Does the position of the pixels in an image govern the edge detection?: Кажется, что фильтр видит только переходы яркости одного направления. На собственной табличке показываем положительный и отрицательный отклик и переносим ноль отклика к серому фону.
Convolution with a bias in Leptonica: Прибавление bias до или после фильтра не даёт ожидаемого результата. Сравниваем собственные программы: −8224 плюс 32768 до clipping даёт 24544, а обрезанный ноль после ADD становится 32768.
How can I find contour or edges in my picture with opencv in Python3?: Горизонтальные полосы мешают выделить вертикальные стороны прямоугольников. Выбираем направленное ядро и объясняем его ограничения: видны вертикальные перепады, горизонтальные края этим ядром не обнаруживаются.
PHP emboss with color: Нужен эффект рельефа с цветом, но результат получается серым. Разделяем ядро, bias и набор каналов: RGB-карта откликов отличается от изменения только R; не выдаём направленные края за универсальный цветной emboss.
How Can I Check for Blurry Images?: Нужна автоматическая оценка размытия изображения. Сохраняем проверяемую карту перепадов как диагностический материал и не называем её универсальным порогом качества для любых изображений.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
Imagick::setImageBiasНастроить смещение свёртки и сохранить отрицательные перепады
PECL imagick 2, PECL imagick 3 · imagick
Imagick::setImageBias(float $bias): boolImagick::setImageBias: исторический контракт
Imagick::setImageBias(float $bias): boolОфициальная доступность: PECL imagick 2, PECL imagick 3. Метод deprecated с Imagick 3.4.4. В нашей Imagick 3.8.1 с ImageMagick 7.1.1-46 method_exists вернул false: этот метод не вызывался. Wrapper включает обе старые функции только при сборке с ImageMagick 6. Пометка deprecated и фактическая доступность — разные вопросы; версия расширения сама по себе не сообщает версию подключённой библиотеки.
setImageBias принимает обязательный float $bias — смещение результата операций, которые выполняют свёртку изображения, например convolveImage. Значение задаётся в единицах квантового диапазона текущей сборки. Официальный результат — bool, true при успехе; при ошибке документация указывает ImagickException. Установка настройки сама по себе не является обработкой пикселей.
// Только чтение старого кода для совместимой IM6-сборки.
$range = Imagick::getQuantum();
$oldImage->setImageBias(0.5 * $range); // Q16: запрос 32767.5.
$oldImage->setImageBias(0.25 * $range); // Q16: запрос 16383.75.Официальный пример связывает четыре шага: прочитать изображение, умножить выбранную долю на Imagick::getQuantum(), задать bias до convolveImage и отдать PNG с заголовком Content-type: image/png. В нём используется направленное ядро с коэффициентами −0.70/0/0.70 в трёх строках и CHANNEL_ALL. Комментарий примера требует ImageMagick 6.9.0-1 для действия bias на convolveImage; это оговорка совместимости этого примера, а не доказательство наличия setter в IM7. Наши CLI-программы сохраняют PNG в файлы и не отправляют HTTP-заголовки.
Для Q8 и Q16 одно буквальное число означает разную долю диапазона, поэтому историческая формула читает QuantumRange. Не заменяйте старый вызов на evaluateImage(ADD) после свёртки: в целочисленной сборке отрицательные отклики к этому моменту уже могут быть потеряны. Современная замена для показанной задачи — артефакт convolve:bias до convolveImage; она не объявляется универсальным аналогом всех исторических эффектов.
| Задача | Как читать значение |
|---|---|
| Исторический setImageBias(32767.5) | Абсолютное смещение в квантовых единицах; значение рассчитано для Q16 |
| Исторический setImageBiasQuantum(0.5) | Нормированная доля QuantumRange согласно документации |
| Современный convolve:bias = "50%" | Процентная строка; в исследованном IM7 это 0.5 × (QuantumRange + 1) |
| Современный convolve:bias = "0.5" | Абсолютное число 0.5 кванта, не 50% |
Практические продолжения: свёртка и очистка скана, пиксели и ImagickKernel, свойства и артефакты изображения. Все четыре исполняемых примера этой главы используют современный путь; сравнение исторических единиц не выдаётся за выполненный deprecated-вызов.
Показать обе стороны перепада яркости
На серой карте видны обе стороны перехода: в Q16 плоская область 32768, нарастающий край 24544, убывающий 40992. Без bias отрицательный отклик обрезан до 0. Оба исторических метода в проверенной IM7-сборке отсутствуют; выполнены setImageArtifact и convolveImage.
<?php
declare(strict_types=1);
require __DIR__ . '/bias-fixture.php';
biRestore();
$source = new Imagick(__DIR__ . '/plaque.png');
$sourceBefore = biState($source);
$sourceHash = hash_file('sha256', __DIR__ . '/plaque.png');
$matrix = [[0, 0, 0], [-0.5, 0, 0.5], [0, 0, 0]];
$kernel = ImagickKernel::fromMatrix($matrix);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$plain = clone $source;
$plain->convolveImage($kernel, $rgb);
$biased = clone $source;
$setReturn = $biased->setImageArtifact('convolve:bias', '50%');
$beforeConvolve = biState($biased);
$convolveReturn = $biased->convolveImage($kernel, $rgb);
$images = [
'source'=>biImage($source,'first-source8.png'),
'plain'=>biImage($plain,'first-plain8.png'),
'biased'=>biImage($biased,'first-biased8.png'),
];
$rects = biCompare([$source,$plain,$biased],'image-bias-first.png');
$errors = [
'wrong_array'=>biTry(fn()=>(clone $source)->convolveImage([0,0,0,-0.5,0,0.5,0,0,0],$rgb)),
'missing_input'=>biTry(fn()=>new Imagick(__DIR__.'/missing.png')),
];
biDiagnostic('image-bias-first',['ImagickKernel::fromMatrix','setImageArtifact','convolveImage'],[
'bias'=>'50%','bias_quantum'=>32768,'channel'=>$rgb,'set_return'=>$setReturn,'convolve_return'=>$convolveReturn,'before_convolve'=>$beforeConvolve,'errors'=>$errors,
'images'=>$images,'panel_keys'=>['source','plain','biased'],'panel_geometry'=>$rects,
'source_before'=>$sourceBefore,'source_after'=>biState($source),'source_sha256'=>$sourceHash,
'source_unchanged'=>biState($source)===$sourceBefore&&hash_file('sha256',__DIR__.'/plaque.png')===$sourceHash,
'checks'=>['setting_does_not_change_pixels'=>$beforeConvolve===$sourceBefore,'both_returns_true'=>$setReturn===true&&$convolveReturn===true,'flat_zero'=>$images['plain']['pixels']['10,10']===[0,0,0],'flat_midpoint'=>$images['biased']['pixels']['10,10']===[32768,32768,32768],'negative_edge_retained'=>$images['biased']['pixels']['39,80']===[24544,24544,24544],'positive_edge_retained'=>$images['biased']['pixels']['119,80']===[40992,40992,40992],'wrong_array_caught'=>($errors['wrong_array']['error']['class']??null)==='TypeError','missing_caught'=>($errors['missing_input']['error']['class']??null)==='ImagickException']
]);
echo "image-bias-first.png\nfirst-source8.png\nfirst-plain8.png\nfirst-biased8.png\nmetrics.json\nQ16 flat=32768 rising=24544 falling=40992 legacy=unavailable\n";
На серой карте видны обе стороны перехода: в Q16 плоская область 32768, нарастающий край 24544, убывающий 40992. Без bias отрицательный отклик обрезан до 0. Оба исторических метода в проверенной IM7-сборке отсутствуют; выполнены setImageArtifact и convolveImage. Архив: example.php, plaque.png.b64, bias-fixture.php. Распакуйте рядом; php example.php восстановит собственный PNG. Проверенная среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 non-HDRI, Windows. Здесь convolveImage принимает ImagickKernel. Исторические setter не вызывались.
Ожидаемый вывод настоящего запуска
image-bias-first.png
first-source8.png
first-plain8.png
first-biased8.png
metrics.json
Q16 flat=32768 rising=24544 falling=40992 legacy=unavailable
Сравнить смещение внутри свёртки и позднюю прибавку
Отклик в точке (39,80) равен −8224. Правильное добавление 32768 до обрезки даёт 24544; после обрезки до 0 поздняя прибавка даёт 32768. Этот край сливается с фоном, и потерянный знак уже не восстановить.
<?php
declare(strict_types=1);
require __DIR__ . '/bias-fixture.php';
biRestore();
$source = new Imagick(__DIR__ . '/plaque.png');
$sourceBefore = biState($source);
$sourceHash = hash_file('sha256', __DIR__ . '/plaque.png');
$matrix = [[0, 0, 0], [-0.5, 0, 0.5], [0, 0, 0]];
$kernel = ImagickKernel::fromMatrix($matrix);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$plain = clone $source;
$plain->convolveImage($kernel,$rgb);
$late = clone $plain;
$late->evaluateImage(Imagick::EVALUATE_ADD,32768,$rgb);
$early = clone $source;
$early->setImageArtifact('convolve:bias','50%');
$early->convolveImage($kernel,$rgb);
$images = [
'plain'=>biImage($plain,'order-plain8.png'),
'late'=>biImage($late,'order-late8.png'),
'early'=>biImage($early,'order-early8.png'),
];
$rects = biCompare([$plain,$late,$early],'image-bias-order.png');
$input = $source->exportImagePixels(38,80,3,1,'R',Imagick::PIXEL_SHORT);
$signed = 0.5 * $input[0] - 0.5 * $input[2];
biDiagnostic('image-bias-order',['ImagickKernel::fromMatrix','convolveImage','evaluateImage','setImageArtifact'],[
'channel'=>$rgb,'sample'=>['x'=>39,'y'=>80,'input_left_center_right'=>$input,'signed_response'=>$signed,'bias'=>32768,'correct_before_clipping'=>$signed+32768,'clipped_response'=>max(0,$signed),'late_after_clipping'=>max(0,$signed)+32768],
'images'=>$images,'panel_keys'=>['plain','late','early'],'panel_geometry'=>$rects,
'source_before'=>$sourceBefore,'source_after'=>biState($source),'source_sha256'=>$sourceHash,
'source_unchanged'=>biState($source)===$sourceBefore&&hash_file('sha256',__DIR__.'/plaque.png')===$sourceHash,
'checks'=>['signed_negative'=>$signed===-8224.0,'before_clipping_retains_edge'=>$images['early']['pixels']['39,80']===[24544,24544,24544],'plain_clips_negative'=>$images['plain']['pixels']['39,80']===[0,0,0],'late_loses_edge'=>$images['late']['pixels']['39,80']===[32768,32768,32768],'positive_edge_same'=>$images['late']['pixels']['119,80']===$images['early']['pixels']['119,80'],'outputs_different'=>biHash($late)!==biHash($early)]
]);
echo "image-bias-order.png\norder-plain8.png\norder-late8.png\norder-early8.png\nmetrics.json\nsigned=-8224 plain=0 late=32768 early=24544 lost-edge=cannot-recover\n";
Отклик в точке (39,80) равен −8224. Правильное добавление 32768 до обрезки даёт 24544; после обрезки до 0 поздняя прибавка даёт 32768. Этот край сливается с фоном, и потерянный знак уже не восстановить. Архив: example.php, plaque.png.b64, bias-fixture.php. Распакуйте рядом; php example.php восстановит собственный PNG. Проверенная среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 non-HDRI, Windows. Здесь convolveImage принимает ImagickKernel. Исторические setter не вызывались.
Ожидаемый вывод настоящего запуска
image-bias-order.png
order-plain8.png
order-late8.png
order-early8.png
metrics.json
signed=-8224 plain=0 late=32768 early=24544 lost-edge=cannot-recover
Imagick::setImageBiasQuantumНастроить смещение свёртки и сохранить отрицательные перепады
PECL imagick 3 >= 3.3.0 · imagick
Imagick::setImageBiasQuantum(float $bias): voidImagick::setImageBiasQuantum: исторический контракт
Imagick::setImageBiasQuantum(float $bias): voidОфициальная доступность: PECL imagick 3 >= 3.3.0. Метод deprecated с Imagick 3.4.4. В нашей Imagick 3.8.1 с ImageMagick 7.1.1-46 method_exists вернул false: этот метод не вызывался. Wrapper включает обе старые функции только при сборке с ImageMagick 6. Пометка deprecated и фактическая доступность — разные вопросы; версия расширения сама по себе не сообщает версию подключённой библиотеки.
setImageBiasQuantum принимает обязательный float $bias. Документация задаёт шкалу от 0 — без смещения — до 1, соответствующей QuantumRange. Например, 0.5 означает половину диапазона. Официальный возвращаемый тип — void: полезное возвращаемое значение использовать не следует. На странице нет собственного примера, дополнительных параметров и отдельного описания ошибок.
// Только чтение старого кода для совместимой IM6-сборки.
$oldImage->setImageBiasQuantum(0.5); // Половина QuantumRange.
$oldImage->setImageBiasQuantum(0.25); // Четверть QuantumRange.Это фрагменты исторического API, а не запущенные программы текущей сборки. В общей C-реализации Imagick 3.8.1 аргумент умножается на QuantumRange, затем вызывается MagickSetImageBias. Есть расхождение источников: PHP.net указывает float/void, stub 3.8.1 — string/void, а общая C-функция разбирает число double и содержит возврат true. Мы сохраняем официальный контракт и не обещаем конкретный результат исторического runtime, который здесь не запускался.
В исследованной C-ветке есть проверка непустого изображения и преобразование ошибки библиотеки в ImagickException; это чтение исходников, не проверка запуска IM6. Не рассчитывайте, что 0..1 автоматически валидируется как диапазон: приложение задаёт свою явную политику. В современной программе ниже мы принимаем проценты 0..100, проверяем конечность числа и используем артефакт с явным знаком %.
| Задача | Как читать значение |
|---|---|
| Исторический setImageBias(32767.5) | Абсолютное смещение в квантовых единицах; значение рассчитано для Q16 |
| Исторический setImageBiasQuantum(0.5) | Нормированная доля QuantumRange согласно документации |
| Современный convolve:bias = "50%" | Процентная строка; в исследованном IM7 это 0.5 × (QuantumRange + 1) |
| Современный convolve:bias = "0.5" | Абсолютное число 0.5 кванта, не 50% |
Практические продолжения: свёртка и очистка скана, пиксели и ImagickKernel, свойства и артефакты изображения. Все четыре исполняемых примера этой главы используют современный путь; сравнение исторических единиц не выдаётся за выполненный deprecated-вызов.
Различить долю QuantumRange, проценты и число квантов
Запросы 32767.5 и 50% означают немного разные смещения, но на этом входе после округления Q16 дают одинаковые пиксели. Строка 0.5 означает половину одного кванта: плоская область равна 1 вместо 32768 и выглядит чёрной.
<?php
declare(strict_types=1);
require __DIR__ . '/bias-fixture.php';
biRestore();
$source = new Imagick(__DIR__ . '/plaque.png');
$sourceBefore = biState($source);
$sourceHash = hash_file('sha256', __DIR__ . '/plaque.png');
$matrix = [[0, 0, 0], [-0.5, 0, 0.5], [0, 0, 0]];
$kernel = ImagickKernel::fromMatrix($matrix);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$range = Imagick::getQuantumRange()['quantumRangeLong'];
$fraction = 0.5;
$rawBias = $fraction * $range;
$raw = clone $source;
$raw->setImageArtifact('convolve:bias',sprintf('%.17g',$rawBias));
$raw->convolveImage($kernel,$rgb);
$percent = clone $source;
$percent->setImageArtifact('convolve:bias','50%');
$percent->convolveImage($kernel,$rgb);
$numeric = clone $source;
$numeric->setImageArtifact('convolve:bias','0.5');
$numeric->convolveImage($kernel,$rgb);
$images = [
'raw'=>biImage($raw,'units-raw8.png'),
'percent'=>biImage($percent,'units-percent8.png'),
'numeric'=>biImage($numeric,'units-numeric8.png'),
];
$rects = biCompare([$raw,$percent,$numeric],'image-bias-units.png');
biDiagnostic('image-bias-units',['ImagickKernel::fromMatrix','getQuantumRange','setImageArtifact','convolveImage'],[
'channel'=>$rgb,'fraction'=>$fraction,'range'=>$range,'raw_bias'=>$rawBias,'percent_bias_quantum'=>($range+1)*0.5,'numeric_bias_quantum'=>0.5,'max_native_difference_raw_percent'=>biMaxDifference($raw,$percent),
'images'=>$images,'panel_keys'=>['raw','percent','numeric'],'panel_geometry'=>$rects,
'source_before'=>$sourceBefore,'source_after'=>biState($source),'source_sha256'=>$sourceHash,
'source_unchanged'=>biState($source)===$sourceBefore&&hash_file('sha256',__DIR__.'/plaque.png')===$sourceHash,
'checks'=>['range_is_65535'=>$range===65535,'raw_is_half_range'=>$rawBias===32767.5,'percent_uses_range_plus_one'=>($range+1)*0.5===32768.0,'native_rounding_equal_for_this_input'=>biHash($raw)===biHash($percent),'numeric_is_not_percent'=>biHash($numeric)!==biHash($percent),'numeric_flat_one'=>$images['numeric']['pixels']['10,10']===[1,1,1]]
]);
echo "image-bias-units.png\nunits-raw8.png\nunits-percent8.png\nunits-numeric8.png\nmetrics.json\nraw=32767.5 percent=32768 numeric=0.5 native-raw-percent=equal flat-numeric=1\n";
Запросы 32767.5 и 50% означают немного разные смещения, но на этом входе после округления Q16 дают одинаковые пиксели. Строка 0.5 означает половину одного кванта: плоская область равна 1 вместо 32768 и выглядит чёрной. Архив: example.php, plaque.png.b64, bias-fixture.php. Распакуйте рядом; php example.php восстановит собственный PNG. Проверенная среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 non-HDRI, Windows. Здесь convolveImage принимает ImagickKernel. Исторические setter не вызывались.
Ожидаемый вывод настоящего запуска
image-bias-units.png
units-raw8.png
units-percent8.png
units-numeric8.png
metrics.json
raw=32767.5 percent=32768 numeric=0.5 native-raw-percent=equal flat-numeric=1
Сохранить проверяемое превью и выбрать каналы
Функция создаёт RGB-превью и отдельный вариант только для R. В R-варианте G/B и альфа неизменны; настройка bias удалена из результата. Четыре недопустимых входа отклонены до обработки. JSON связывает исходник и реальные сохранённые PNG.
<?php
declare(strict_types=1);
require __DIR__ . '/bias-fixture.php';
biRestore();
$source = new Imagick(__DIR__ . '/plaque.png');
$sourceBefore = biState($source);
$sourceHash = hash_file('sha256', __DIR__ . '/plaque.png');
$matrix = [[0, 0, 0], [-0.5, 0, 0.5], [0, 0, 0]];
$kernel = ImagickKernel::fromMatrix($matrix);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
function edgePreview(Imagick $input,float $biasPercent,int $channels):Imagick
{
if ($input->getNumberImages() !== 1) {
throw new InvalidArgumentException('Exactly one image required');
}
if (!is_finite($biasPercent) || $biasPercent < 0 || $biasPercent > 100) {
throw new InvalidArgumentException('Bias must be finite and between 0 and 100 percent');
}
$copy = clone $input;
if ($copy->getImageArtifact('convolve:scale') !== null) {
$copy->deleteImageArtifact('convolve:scale');
}
$copy->setImageArtifact('convolve:bias',sprintf('%.17g%%',$biasPercent));
try {
$filter = ImagickKernel::fromMatrix([[0,0,0],[-0.5,0,0.5],[0,0,0]]);
$copy->convolveImage($filter,$channels);
} finally {
$copy->deleteImageArtifact('convolve:bias');
}
return $copy;
}
$all = edgePreview($source,50.0,$rgb);
$red = edgePreview($source,50.0,Imagick::CHANNEL_RED);
$errors = [
'empty'=>biTry(fn()=>edgePreview(new Imagick(),50.0,$rgb)),
'negative'=>biTry(fn()=>edgePreview($source,-1.0,$rgb)),
'not_finite'=>biTry(fn()=>edgePreview($source,NAN,$rgb)),
'too_large'=>biTry(fn()=>edgePreview($source,101.0,$rgb)),
];
$images = [
'source'=>biImage($source,'workflow-source8.png'),
'rgb'=>biImage($all,'workflow-rgb8.png'),
'red'=>biImage($red,'workflow-red8.png'),
];
$rects = biCompare([$source,$all,$red],'image-bias-workflow.png');
$manifest = ['input'=>'plaque.png','input_sha256'=>$sourceHash,'bias_percent'=>50,'kernel'=>$matrix,'outputs'=>['rgb'=>$images['rgb']['encoded_png'],'red'=>$images['red']['encoded_png']],'scope'=>'Directional preview; not automatic quality classification; original remains unchanged'];
file_put_contents(__DIR__.'/edge-preview.json',json_encode($manifest,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)."\n");
biDiagnostic('image-bias-workflow',['ImagickKernel::fromMatrix','getNumberImages','setImageArtifact','convolveImage','deleteImageArtifact'],[
'channels'=>['rgb'=>$rgb,'red'=>Imagick::CHANNEL_RED],'bias_percent'=>50,'errors'=>$errors,'artifact_after'=>['rgb'=>$all->getImageArtifact('convolve:bias'),'red'=>$red->getImageArtifact('convolve:bias')],'manifest'=>$manifest,'manifest_sha256'=>hash_file('sha256',__DIR__.'/edge-preview.json'),
'images'=>$images,'panel_keys'=>['source','rgb','red'],'panel_geometry'=>$rects,
'source_before'=>$sourceBefore,'source_after'=>biState($source),'source_sha256'=>$sourceHash,
'source_unchanged'=>biState($source)===$sourceBefore&&hash_file('sha256',__DIR__.'/plaque.png')===$sourceHash,
'checks'=>['red_changed'=>biHash($red,'R')!==biHash($source,'R'),'green_preserved'=>biHash($red,'G')===biHash($source,'G'),'blue_preserved'=>biHash($red,'B')===biHash($source,'B'),'alpha_preserved'=>biHash($red,'A')===biHash($source,'A')&&biHash($all,'A')===biHash($source,'A'),'no_bias_leak'=>$all->getImageArtifact('convolve:bias')===null&&$red->getImageArtifact('convolve:bias')===null,'all_bad_inputs_caught'=>count(array_filter($errors,fn($e)=>($e['error']['class']??null)==='InvalidArgumentException'))===4,'manifest_bound'=>$manifest['outputs']['rgb']['sha256']===hash_file('sha256',__DIR__.'/workflow-rgb8.png')&&$manifest['outputs']['red']['sha256']===hash_file('sha256',__DIR__.'/workflow-red8.png')]
]);
echo "image-bias-workflow.png\nworkflow-source8.png\nworkflow-rgb8.png\nworkflow-red8.png\nedge-preview.json\nmetrics.json\npreview=RGB-or-R GBA-preserved=yes bias-cleared=yes rejected=4\n";
Функция создаёт RGB-превью и отдельный вариант только для R. В R-варианте G/B и альфа неизменны; настройка bias удалена из результата. Четыре недопустимых входа отклонены до обработки. JSON связывает исходник и реальные сохранённые PNG. Архив: example.php, plaque.png.b64, bias-fixture.php. Распакуйте рядом; php example.php восстановит собственный PNG. Проверенная среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16 non-HDRI, Windows. Здесь convolveImage принимает ImagickKernel. Исторические setter не вызывались.
Ожидаемый вывод настоящего запуска
image-bias-workflow.png
workflow-source8.png
workflow-rgb8.png
workflow-red8.png
edge-preview.json
metrics.json
preview=RGB-or-R GBA-preserved=yes bias-cleared=yes rejected=4
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


