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

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

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

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

Глава 361 · После масок, локальных фильтров и каналов

Imagick: выровнять и очистить скан ярлыка

Свои ядра, форма дефекта и небольшой наклон с видимыми потерями и полным результатом.

Из наклонённого ярлыка в самостоятельный ровный PNG

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

Ниже входной растр 400×320 и полезный результат: ровнее расположенные строки на полном полотне 423×349. Нижний правый трафарет и серые нижние плашки — отдельные контрольные области, добавленные после создания наклонённого скана. Надписи на растре остаются частью рисунка; обработка не переписывает их и не подтверждает смысл SKU.

Собственный наклонённый ярлык 400×320; отдельные трафарет и плашки — контролируемые данные
Собственный наклонённый ярлык 400×320; отдельные трафарет и плашки — контролируемые данные
Полный выровненный скан справа; новые белые поля сохраняются, результат не ужат до размеров входа
Полный выровненный скан справа; новые белые поля сохраняются, результат не ужат до размеров входа
Термин до схемыСмысл в примерах
RGB / RGBARGB — красная, зелёная и синяя компоненты цвета. RGBA добавляет alpha: 0 — прозрачный, 1 — непрозрачный. Наш основной скан RGB без alpha.
Q, T и градусыQ=QuantumRange, максимум 65535 в проверенной Q16-сборке; T=0.4Q=26214 — порог тёмных пикселей deskew. Найденный угол — отдельное число в градусах.
c, c′, clampc — значение компоненты соседа; c′ — результат. clamp ограничивает число диапазоном 0..Q. Сумма произведений коэффициентов и соседей — модель для непрозрачного RGB.
Ядро, опорная позицияЯдро 3×3 содержит три строки по три числа. Центральная позиция совмещается с обрабатываемым пикселем; convolve отражает ядро на 180°.
OPEN / CLOSEОперации минимума и максимума в окрестности. Поясняем явно чёрный рисунок на белом, а не универсальные слова «удалить шум».
x, y, W, H, pagex растёт вправо, y вниз от левого верхнего угла. W/H — реальный растр; page — отдельное виртуальное положение объекта. ROI — выбранный прямоугольник [x,y,width,height].

Ядро — маленькая таблица коэффициентов. У 3×3 опорная позиция по умолчанию в центре: она совмещается с текущим пикселем. Коэффициент умножает значение одного соседа; произведения складываются. Для непрозрачного RGB и суммы весов 1 среднее не меняет ровное поле. Например, девять значений 180 с весом 1/9 дают 180; один 0 и восемь 180 дают 160. Уменьшение точки одновременно размывает полезный край. fromMatrix само не нормирует введённые коэффициенты: явные 1/9 здесь задаёт автор. При активной alpha Core может делить взвешенную сумму на сумму alpha-весов — даже если маска выбрана RGB; не переносим простую формулу на произвольный RGBA.

Convolve отражает ядро на 180° относительно опорной позиции. Поэтому переданная средняя строка [-1,1,0] даёт центр−правый сосед. На контрольной точке вес 1 слева переносит её отклик влево. Симметричное среднее скрывает это различие. Sharpen с центром 2 и четырьмя соседями −0.25 имеет сумму 1; отрицательные коэффициенты могут усилить мусор и дать значения за диапазоном, которые ограничиваются до 0..Q. Направленный отклик — карта перепадов, не автоматически улучшенный скан.

Для белого фона и чёрной печати ERODE выбирает минимум в окрестности и расширяет чёрное, DILATE выбирает максимум и расширяет белое. OPEN = ERODE, затем DILATE: может закрыть маленькие белые отверстия в чёрном. CLOSE = DILATE, затем ERODE: может убрать маленькие чёрные островки на белом. Это та же пара правил, которую часто описывают для светлого foreground; смена полярности меняет бытовое объяснение. Библиотека не различает пылинку и нужную точку буквы. Размер ядра нужно сравнивать с толщиной нужного штриха, а не выбирать по слову «очистка».

Deskew сначала отмечает пиксели с R

Коэффициенты соседей, min/max формы и оценка небольшого наклона решают разные задачи
Коэффициенты соседей, min/max формы и оценка небольшого наклона решают разные задачи

Самая маленькая запись

В $image уже загружен скан. $ — начало переменной; = присваивает; -> вызывает метод; скобки передают аргументы, ; завершает команду. Imagick::getQuantumRange() — статический вызов класса; он возвращает массив. Ключ quantumRangeLong в [] достаёт внутренний максимум. * умножает его на 0.4. Возвращённое true обозначает статус, а результат остаётся в $image:

$Q = Imagick::getQuantumRange()['quantumRangeLong'];
$image->setImageBackgroundColor('white');
$image->deskewImage(0.4 * $Q);

Полная программа ниже подключает cleanup-fixture.php через require; __DIR__ — папка текущего файла, . соединяет её с именем helper. loadCleanupSource() восстанавливает включённые в ZIP байты, проверяет SHA и возвращает Imagick. Clone создаёт независимую копию; | объединяет маски RGB. if с ! проверяет отказ, throw останавливает программу с понятным сообщением. saveCleanupNative сохраняет целый результат; saveCleanupComparison строит иллюстрацию из целых панелей, ничего не масштабирует. echo выводит имя файла; "\n" — перевод строки. Переменные, массивы, объекты и исключения.

Сохранить выровненный скан ярлыка целиком

Из входного 400×320 получается самостоятельный first-deskew.png 423×349. На сравнении слева вход, справа полный выровненный скан; новые поля остаются в файле.

Сохранить выровненный скан ярлыка целикомПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$Q = Imagick::getQuantumRange()['quantumRangeLong'];
$clean = clone $source;
if (!$clean->deskewImage(0.4 * $Q)) { throw new RuntimeException('Deskew failed'); }
saveCleanupNative($clean, 'first-deskew.png');
saveCleanupComparison([$source, $clean], 'first-cleanup.png');
echo "first-cleanup.png\n";

Из входного 400×320 получается самостоятельный first-deskew.png 423×349. На сравнении слева вход, справа полный выровненный скан; новые поля остаются в файле. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
first-cleanup.png

Выбрать операцию по дефекту и заметить потери

ПотребностьИнструментЦена изменения
Ослабить мелкие точки/усилить различияconvolveImage с явным ядромУсреднение размывает края; отрицательные коэффициенты усиливают мусор и ограничиваются диапазоном.
Убрать островок/закрыть отверстиеmorphology с подходящей полярностьюТочка буквы и пылинка могут иметь одинаковый размер; форма и связность меняются.
Убрать небольшой наклон строкdeskewImage с Quantum-порогомРастр/page изменяются, новые пиксели интерполируются; перспектива и OCR не выполняются.

Самая маленькая запись

Заменяет значение пикселя взвешенной суммой его соседей; ядро задаёт коэффициенты и направление. Меняет текущий Imagick, возвращает bool true. $ начинает имя переменной; = присваивает результат; -> вызывает метод объекта; запятые разделяют аргументы; ; завершает команду. В $image уже загружен непустой PNG. RGB — красная, зелёная и синяя компоненты; | объединяет маски разрешённых каналов. Q=QuantumRange — внутренний максимум, 65535 в проверенной Q16-сборке.

Ядро — маленькая таблица коэффициентов. У 3×3 опорная позиция по умолчанию в центре: она совмещается с текущим пикселем. Коэффициент умножает значение одного соседа; произведения складываются. Для непрозрачного RGB и суммы весов 1 среднее не меняет ровное поле. Например, девять значений 180 с весом 1/9 дают 180; один 0 и восемь 180 дают 160. Уменьшение точки одновременно размывает полезный край. fromMatrix само не нормирует введённые коэффициенты: явные 1/9 здесь задаёт автор. При активной alpha Core может делить взвешенную сумму на сумму alpha-весов — даже если маска выбрана RGB; не переносим простую формулу на произвольный RGBA.

Convolve отражает ядро на 180° относительно опорной позиции. Поэтому переданная средняя строка [-1,1,0] даёт центр−правый сосед. На контрольной точке вес 1 слева переносит её отклик влево. Симметричное среднее скрывает это различие. Sharpen с центром 2 и четырьмя соседями −0.25 имеет сумму 1; отрицательные коэффициенты могут усилить мусор и дать значения за диапазоном, которые ограничиваются до 0..Q. Направленный отклик — карта перепадов, не автоматически улучшенный скан.

[] создаёт массив; три вложенных массива — строки таблицы 3×3. Выражение 1/9 — деление чисел. ImagickKernel::fromMatrix — статический метод класса, :: соединяет класс и имя метода; результат сохраняется в $kernel. Это отдельный объект с коэффициентами, а не картинка. Во фрагменте используем явную RGB-маску:

$kernel = ImagickKernel::fromMatrix([
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
]);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$image->convolveImage($kernel, $rgb);

Clone создаёт отдельную копию растра; вызов метода меняет эту копию. В полных программах ! означает отрицание, if проверяет отказ, throw new RuntimeException останавливает цепочку; затем writeImage сохраняет PNG. Основы: объект и метод, массивы, клонирование, исключения.

Самая маленькая запись

Сравнивает пиксель с окрестностью заданной формы; выбранный метод расширяет/сужает светлые области или сочетает стадии. Меняет текущий Imagick, возвращает bool true. $ начинает имя переменной; = присваивает результат; -> вызывает метод объекта; запятые разделяют аргументы; ; завершает команду. В $image уже загружен непустой PNG. RGB — красная, зелёная и синяя компоненты; | объединяет маски разрешённых каналов. Q=QuantumRange — внутренний максимум, 65535 в проверенной Q16-сборке.

Для белого фона и чёрной печати ERODE выбирает минимум в окрестности и расширяет чёрное, DILATE выбирает максимум и расширяет белое. OPEN = ERODE, затем DILATE: может закрыть маленькие белые отверстия в чёрном. CLOSE = DILATE, затем ERODE: может убрать маленькие чёрные островки на белом. Это та же пара правил, которую часто описывают для светлого foreground; смена полярности меняет бытовое объяснение. Библиотека не различает пылинку и нужную точку буквы. Размер ядра нужно сравнивать с толщиной нужного штриха, а не выбирать по слову «очистка».

ImagickKernel::fromMatrix создаёт объект ядра из массива строк. Imagick::MORPHOLOGY_CLOSE — целочисленная константа класса; :: здесь обращается к константе, не к переменной. Второй аргумент 1 — число проходов. Девять единиц задают квадратную форму соседства:

$kernel = ImagickKernel::fromMatrix([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
]);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$image->morphology(Imagick::MORPHOLOGY_CLOSE, 1, $kernel, $rgb);

Clone создаёт отдельную копию растра; вызов метода меняет эту копию. В полных программах ! означает отрицание, if проверяет отказ, throw new RuntimeException останавливает цепочку; затем writeImage сохраняет PNG. Основы: объект и метод, массивы, клонирование, исключения.

Выбрать между усреднением, удалением точек и выравниванием

Слева направо: вход, усреднение 3×3, CLOSE с квадратом 3×3, deskew. Усреднение смягчает и полезные края. В сохранённом результате CLOSE большинство тонких букв почти исчезло: такую глобальную очистку для нашего ярлыка отклоняем. Deskew решает другую задачу.

Выбрать между усреднением, удалением точек и выравниваниемПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$kernel = ImagickKernel::fromMatrix([
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
]);
$average = clone $source;
if (!$average->convolveImage($kernel, $rgb)) { throw new RuntimeException('Convolution failed'); }
$kernel = ImagickKernel::fromMatrix([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
]);
$closed = clone $source;
if (!$closed->morphology(Imagick::MORPHOLOGY_CLOSE, 1, $kernel, $rgb)) { throw new RuntimeException('Morphology failed'); }
$Q = Imagick::getQuantumRange()['quantumRangeLong'];
$clean = clone $source;
if (!$clean->deskewImage(0.4 * $Q)) { throw new RuntimeException('Deskew failed'); }
saveCleanupComparison([$source, $average, $closed, $clean], 'cleanup-controls.png');
echo "cleanup-controls.png\n";

Слева направо: вход, усреднение 3×3, CLOSE с квадратом 3×3, deskew. Усреднение смягчает и полезные края. В сохранённом результате CLOSE большинство тонких букв почти исчезло: такую глобальную очистку для нашего ярлыка отклоняем. Deskew решает другую задачу. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
cleanup-controls.png

Сравнение сохраняется как RGB PNG с 8 битами на компоненту, color-type 2. Это не индексированный PNG и не 1-битный скан. Даже если часть контроля имеет только чёрный и белый, формат хранения остаётся RGB8. Белые поля сравнения — контекст вокруг реальных панелей, а не отрезанные части результата.

Соседи и коэффициенты: среднее, штрихи и направление

Самая маленькая запись

Заменяет значение пикселя взвешенной суммой его соседей; ядро задаёт коэффициенты и направление. Меняет текущий Imagick, возвращает bool true. $ начинает имя переменной; = присваивает результат; -> вызывает метод объекта; запятые разделяют аргументы; ; завершает команду. В $image уже загружен непустой PNG. RGB — красная, зелёная и синяя компоненты; | объединяет маски разрешённых каналов. Q=QuantumRange — внутренний максимум, 65535 в проверенной Q16-сборке.

Ядро — маленькая таблица коэффициентов. У 3×3 опорная позиция по умолчанию в центре: она совмещается с текущим пикселем. Коэффициент умножает значение одного соседа; произведения складываются. Для непрозрачного RGB и суммы весов 1 среднее не меняет ровное поле. Например, девять значений 180 с весом 1/9 дают 180; один 0 и восемь 180 дают 160. Уменьшение точки одновременно размывает полезный край. fromMatrix само не нормирует введённые коэффициенты: явные 1/9 здесь задаёт автор. При активной alpha Core может делить взвешенную сумму на сумму alpha-весов — даже если маска выбрана RGB; не переносим простую формулу на произвольный RGBA.

Convolve отражает ядро на 180° относительно опорной позиции. Поэтому переданная средняя строка [-1,1,0] даёт центр−правый сосед. На контрольной точке вес 1 слева переносит её отклик влево. Симметричное среднее скрывает это различие. Sharpen с центром 2 и четырьмя соседями −0.25 имеет сумму 1; отрицательные коэффициенты могут усилить мусор и дать значения за диапазоном, которые ограничиваются до 0..Q. Направленный отклик — карта перепадов, не автоматически улучшенный скан.

[] создаёт массив; три вложенных массива — строки таблицы 3×3. Выражение 1/9 — деление чисел. ImagickKernel::fromMatrix — статический метод класса, :: соединяет класс и имя метода; результат сохраняется в $kernel. Это отдельный объект с коэффициентами, а не картинка. Во фрагменте используем явную RGB-маску:

$kernel = ImagickKernel::fromMatrix([
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
]);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$image->convolveImage($kernel, $rgb);

Clone создаёт отдельную копию растра; вызов метода меняет эту копию. В полных программах ! означает отрицание, if проверяет отказ, throw new RuntimeException останавливает цепочку; затем writeImage сохраняет PNG. Основы: объект и метод, массивы, клонирование, исключения.

Усреднение девяти соседей уменьшает отдельные точки и одновременно смягчает буквы
Усреднение девяти соседей уменьшает отдельные точки и одновременно смягчает буквы

Ослабить отдельный пиксельный мусор усреднением 3×3

Справа среднее девяти соседних значений. Точки становятся слабее, но края текста и штрихов тоже размываются; это видимый компромисс, а не восстановление недостающих букв.

Ослабить отдельный пиксельный мусор усреднением 3×3Пример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$kernel = ImagickKernel::fromMatrix([
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
]);
$average = clone $source;
if (!$average->convolveImage($kernel, $rgb)) { throw new RuntimeException('Convolution failed'); }
saveCleanupComparison([$source, $average], 'convolve-average.png');
echo "convolve-average.png\n";

Справа среднее девяти соседних значений. Точки становятся слабее, но края текста и штрихов тоже размываются; это видимый компромисс, а не восстановление недостающих букв. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
convolve-average.png
Защищённая область: исходник, умеренное усиление штрихов и отдельная диагностическая карта центр−правый сосед
Защищённая область: исходник, умеренное усиление штрихов и отдельная диагностическая карта центр−правый сосед

Подчеркнуть штрихи в выбранной области и увидеть направленный отклик

В середине умеренное усиление различий внутри ROI (24,90,352,150), справа диагностический отклик центр−правый сосед в той же области. Снаружи ROI и в SKU исходные значения пикселей сохранены.

Подчеркнуть штрихи в выбранной области и увидеть направленный откликПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$region = clone $source;
$region->cropImage(352, 150, 24, 90);
$region->setImagePage(0, 0, 0, 0);
$kernel = ImagickKernel::fromMatrix([
    [0, -0.25, 0],
    [-0.25, 2, -0.25],
    [0, -0.25, 0],
]);
$sharpRegion = clone $region;
if (!$sharpRegion->convolveImage($kernel, $rgb)) { throw new RuntimeException('Sharpen region failed'); }
$sharpened = clone $source;
$sharpened->compositeImage($sharpRegion, Imagick::COMPOSITE_OVER, 24, 90);
$directionKernel = ImagickKernel::fromMatrix([
    [0, 0, 0],
    [-1, 1, 0],
    [0, 0, 0],
]);
$directionRegion = clone $region;
if (!$directionRegion->convolveImage($directionKernel, $rgb)) { throw new RuntimeException('Direction map failed'); }
$direction = clone $source;
$direction->compositeImage($directionRegion, Imagick::COMPOSITE_OVER, 24, 90);
saveCleanupComparison([$source, $sharpened, $direction], 'convolve-protected.png');
echo "convolve-protected.png\n";

В середине умеренное усиление различий внутри ROI (24,90,352,150), справа диагностический отклик центр−правый сосед в той же области. Снаружи ROI и в SKU исходные значения пикселей сохранены. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
convolve-protected.png

cropImage принимает width,height,x,y; ROI (24,90,352,150) передаём как cropImage(352,150,24,90). После crop сбрасываем page через setImagePage(0,0,0,0), чтобы при обратной вставке использовать явные координаты 24,90. В OVER вставляется непрозрачный прямоугольник. Всё снаружи сохраняет исходные значения пикселей; это не утверждение идентичности всего PNG-файла. Направленный отклик справа не выдаётся за красивую или более читаемую фотографию.

Форма окрестности и полярность: точки против отверстий

Самая маленькая запись

Сравнивает пиксель с окрестностью заданной формы; выбранный метод расширяет/сужает светлые области или сочетает стадии. Меняет текущий Imagick, возвращает bool true. $ начинает имя переменной; = присваивает результат; -> вызывает метод объекта; запятые разделяют аргументы; ; завершает команду. В $image уже загружен непустой PNG. RGB — красная, зелёная и синяя компоненты; | объединяет маски разрешённых каналов. Q=QuantumRange — внутренний максимум, 65535 в проверенной Q16-сборке.

Для белого фона и чёрной печати ERODE выбирает минимум в окрестности и расширяет чёрное, DILATE выбирает максимум и расширяет белое. OPEN = ERODE, затем DILATE: может закрыть маленькие белые отверстия в чёрном. CLOSE = DILATE, затем ERODE: может убрать маленькие чёрные островки на белом. Это та же пара правил, которую часто описывают для светлого foreground; смена полярности меняет бытовое объяснение. Библиотека не различает пылинку и нужную точку буквы. Размер ядра нужно сравнивать с толщиной нужного штриха, а не выбирать по слову «очистка».

ImagickKernel::fromMatrix создаёт объект ядра из массива строк. Imagick::MORPHOLOGY_CLOSE — целочисленная константа класса; :: здесь обращается к константе, не к переменной. Второй аргумент 1 — число проходов. Девять единиц задают квадратную форму соседства:

$kernel = ImagickKernel::fromMatrix([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
]);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$image->morphology(Imagick::MORPHOLOGY_CLOSE, 1, $kernel, $rgb);

Clone создаёт отдельную копию растра; вызов метода меняет эту копию. В полных программах ! означает отрицание, if проверяет отказ, throw new RuntimeException останавливает цепочку; затем writeImage сохраняет PNG. Основы: объект и метод, массивы, клонирование, исключения.

CLOSE удаляет чёрные точки вместе с тонкими штрихами; OPEN ограничен отдельным трафаретом с белыми отверстиями
CLOSE удаляет чёрные точки вместе с тонкими штрихами; OPEN ограничен отдельным трафаретом с белыми отверстиями

Удалить мелкие чёрные точки на белом поле и увидеть потери

CLOSE убирает отдельные чёрные точки, но в нашем результате большинство тонких букв почти исчезло. Даже нижний контроль теряет часть тёмных пикселей: 1184 → 1111 при пороге R<80. Эту глобальную очистку отклоняем; уменьшение мусора не подтверждает читаемость.

Удалить мелкие чёрные точки на белом поле и увидеть потериПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$kernel = ImagickKernel::fromMatrix([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
]);
$closed = clone $source;
if (!$closed->morphology(Imagick::MORPHOLOGY_CLOSE, 1, $kernel, $rgb)) { throw new RuntimeException('Closing failed'); }
saveCleanupComparison([$source, $closed], 'morphology-close.png');
echo "morphology-close.png\n";

CLOSE убирает отдельные чёрные точки, но в нашем результате большинство тонких букв почти исчезло. Даже нижний контроль теряет часть тёмных пикселей: 1184 → 1111 при пороге R<80. Эту глобальную очистку отклоняем; уменьшение мусора не подтверждает читаемость. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
morphology-close.png

Закрыть белые отверстия в известном чёрном трафарете

OPEN применяется только к контрольному трафарету (280,250,96,48): небольшие белые отверстия становятся чёрными. Остальной скан и SKU не обрабатываются. Сам трафарет сохранён отдельно.

Закрыть белые отверстия в известном чёрном трафаретеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$stencil = clone $source;
$stencil->cropImage(96, 48, 280, 250);
$stencil->setImagePage(0, 0, 0, 0);
$kernel = ImagickKernel::fromMatrix([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
]);
if (!$stencil->morphology(Imagick::MORPHOLOGY_OPEN, 1, $kernel, $rgb)) { throw new RuntimeException('Opening failed'); }
$result = clone $source;
$result->compositeImage($stencil, Imagick::COMPOSITE_OVER, 280, 250);
saveCleanupNative($stencil, 'morphology-mask.png');
saveCleanupComparison([$source, $result], 'morphology-stencil.png');
echo "morphology-stencil.png\n";

OPEN применяется только к контрольному трафарету (280,250,96,48): небольшие белые отверстия становятся чёрными. Остальной скан и SKU не обрабатываются. Сам трафарет сохранён отдельно. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
morphology-stencil.png
Не перепутатьПроверка
Единица в форме и 1/9 в среднемДля min/max единица означает участие позиции; деление на девять не превращает morphology в усреднение.
CLOSE на всём документеПосмотреть не только исчезнувшие точки, но и мелкий нижний код/тонкие буквы. Потеря нужной детали — основание сузить область или отказаться от операции.
OPEN для чёрного трафаретаБелые однопиксельные отверстия заполняются; окружающий скан не меняется. Увеличение квадрата может соединить соседние чёрные области.
Фиксированный один проход-1 из официального примера не является безопасной настройкой по умолчанию для произвольного большого изображения.

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

Самая маленькая запись

Оценивает небольшой наклон по тёмным пикселям и поворачивает весь текущий растр; возвращает bool true. Угол читают отдельно после вызова. $ начинает имя переменной; = присваивает результат; -> вызывает метод объекта; запятые разделяют аргументы; ; завершает команду. В $image уже загружен непустой PNG. RGB — красная, зелёная и синяя компоненты; | объединяет маски разрешённых каналов. Q=QuantumRange — внутренний максимум, 65535 в проверенной Q16-сборке.

Q=QuantumRange — внутренний максимум канала, 65535 в проверенной Q16-сборке; выходной RGB PNG хранит 8 бит и значения 0..255 на компоненту. Imagick::getQuantumRange() — статический метод класса, который возвращает массив; ['quantumRangeLong'] читает значение по ключу. * умножает Q на долю 0.4, а не задаёт угол. Порог из CLI с % нельзя просто передать числом 40. Возвращённое true — статус; найденный угол в градусах хранится в deskew:angle.

$Q = Imagick::getQuantumRange()['quantumRangeLong'];
$image->setImageBackgroundColor('white');
$image->deskewImage(0.4 * $Q);

Clone создаёт отдельную копию растра; вызов метода меняет эту копию. В полных программах ! означает отрицание, if проверяет отказ, throw new RuntimeException останавливает цепочку; затем writeImage сохраняет PNG. Основы: объект и метод, массивы, клонирование, исключения.

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

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

Полный результат 423×349, вычисленный угол после округления 4.02°. Это результат анализа входа, а не угол, переданный первым аргументом. Фон новых полей белый.

Выровнять полный скан и прочитать найденный уголПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$Q = Imagick::getQuantumRange()['quantumRangeLong'];
$clean = clone $source;
if (!$clean->deskewImage(0.4 * $Q)) { throw new RuntimeException('Deskew failed'); }
saveCleanupComparison([$source, $clean], 'deskew-full.png');
echo "deskew-full.png\nangle=" . round((float)$clean->getImageArtifact('deskew:angle'), 2) . "\n";

Полный результат 423×349, вычисленный угол после округления 4.02°. Это результат анализа входа, а не угол, переданный первым аргументом. Фон новых полей белый. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
deskew-full.png
angle=4.02

Подготовить отдельную ровную PNG-вставку из области ярлыка

Слева полный скан, в середине выбранный ярлык 352×154, справа его самостоятельный полный результат 364×180. label-before.png и label-deskew.png пригодны для отдельной вставки; угол 3.91° отличается из-за другой анализируемой области.

Подготовить отдельную ровную PNG-вставку из области ярлыкаПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$label = clone $source;
$label->cropImage(352, 154, 24, 86);
$label->setImagePage(0, 0, 0, 0);
$clean = clone $label;
$Q = Imagick::getQuantumRange()['quantumRangeLong'];
if (!$clean->deskewImage(0.4 * $Q)) { throw new RuntimeException('Label deskew failed'); }
saveCleanupNative($label, 'label-before.png');
saveCleanupNative($clean, 'label-deskew.png');
saveCleanupComparison([$source, $label, $clean], 'deskew-label.png');
echo "deskew-label.png\nangle=" . round((float)$clean->getImageArtifact('deskew:angle'), 2) . "\n";

Слева полный скан, в середине выбранный ярлык 352×154, справа его самостоятельный полный результат 364×180. label-before.png и label-deskew.png пригодны для отдельной вставки; угол 3.91° отличается из-за другой анализируемой области. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
deskew-label.png
angle=3.91

У полного объекта после вызова W/H=423×349, page=(423,349,−23,−1); у сброшенного crop после анализа W/H=364×180, page=(0,0,−12,−1). Это наблюдение нашей сборки, а не универсальные формулы для любого входа. В сравнении эти растры размещаются отдельно по явным прямоугольникам. Для будущей вставки PNG не полагайтесь на перенос page сторонним редактором; задавайте позицию явно. Угол хранится у текущего Imagick; напечатанное число округляется только для удобства.

Проверить читаемость, границы и отказ

СимптомПричина и следующий шаг
Массив не принят convolveImageНа IM7 нужен ImagickKernel. Не переписывайте сохранённую официальную сигнатуру молча; проверьте версию/Reflection и создайте объект из массива строк.
Исчезли штрихи или отверстияФильтр не знает смысл документа. Сравнить весь нужный текст, уменьшить ядро/число стадий, применить ROI или сохранить исходник без очистки.
Тёмный результат направленного фильтраОтрицательные отклики ограничиваются до 0, ровные области дают 0. Это карта перепадов, не автоматическая коррекция яркости.
Фон RGBA ведёт себя иначеAlpha может участвовать в весах RGB и меняться по умолчанию. Непрозрачный главный вход не доказывает общую сохранность прозрачности.
Deskew не выровнял фотографию/90°Малый наклон строк отличается от перспективы, ориентации EXIF и распознавания текста. Сначала выбрать подходящую геометрическую подготовку, не усиливать threshold как угол.
Угол ноль, но размер другойДаже нулевой поворот прошёл аффинную обработку и добавил поля на белом контроле. Проверять реальные размеры и качество, а не считать нулевой angle гарантией identity.
После фильтра нужен старый исходникКвантование, ограничение диапазона и геометрическая интерполяция теряют данные. Сохранить оригинал до обработки; обратный фильтр не обещает восстановление.

Наш вход — собственный синтетический скан с известным наклоном и явно добавленными контролями. Он не доказывает улучшение точности OCR, правильность считанных артикулов, качество архивного сканирования или стандартизацию серии фотографий. PHP-примеры создают локальные PNG; HTTP-выдачу, загрузку недоверенных файлов и лимиты рабочего сервера они не реализуют. Внешние размеры/ядра/итерации проверяются отдельно. Шрифт нужен только автору статического входа, а не PHP при запуске ZIP.

Полный переносимый helper
<?php
declare(strict_types=1);
function loadCleanupSource(): Imagick {
    $encoded = file_get_contents(__DIR__ . '/source.png.b64');
    if ($encoded === false) { throw new RuntimeException('Cannot read source.png.b64'); }
    $bytes = base64_decode($encoded, true);
    if ($bytes === false || hash('sha256', $bytes) !== '7266cfcb606ffeb861e2e269bfcf21971f6af13bbe396c7f9c0171d310bdcdba') {
        throw new RuntimeException('Unexpected source PNG bytes');
    }
    if (file_put_contents(__DIR__ . '/source.png', $bytes) === false) { throw new RuntimeException('Cannot restore source.png'); }
    $image = new Imagick(__DIR__ . '/source.png');
    $image->setImageBackgroundColor('white');
    $image->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_EDGE);
    return $image;
}
function saveCleanupNative(Imagick $image, string $filename): void {
    $saved = clone $image;
    $saved->setImageFormat('png');
    $saved->setImageDepth(8);
    $saved->setOption('png:color-type', '2');
    if (!$saved->writeImage(__DIR__ . '/' . $filename)) { throw new RuntimeException('Cannot save ' . $filename); }
}
function saveCleanupComparison(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) {
        $y = intdiv($height - $panel->getImageHeight(), 2);
        if (!$canvas->compositeImage($panel, Imagick::COMPOSITE_OVER, $x, $y)) { throw new RuntimeException('Comparison placement failed'); }
        $x += $panel->getImageWidth();
    }
    saveCleanupNative($canvas, $filename);
}

Задачи из прочитанных обсуждений:

Реальная проблемаКак ограничено наше решение
SO 41546181Порог deskew и фактический наклон: изменение порога меняет анализ штрихов, а не обещает заданный угол; большая ориентационная ошибка требует другого инструмента.
SO 75469241Проценты порога не градусы: CLI-варианты плюс/минус не являются PHP-сигнатурой; в уроке вычисляем Quantum-порог и отдельно читаем угол.
SO 46861201Удаление линий затрагивает пересекающиеся буквы: форма ядра и реальные потери важнее обещания улучшить OCR; наш направленный отклик не восстанавливает закрытые детали.
SO 77130366Одна очистка не подходит всем толщинам линий: показать уменьшение точек вместе с исчезновением тонких нужных штрихов.
SO 41576581Неполный пример с $this и разными объектами мешает новичку: наши программы явно создают ядро, меняют нужную копию и сохраняют именно обработанный результат.
SO 24184801Совпадение PECL-версии не гарантирует одинаковый native-контракт: сохраняем raw array/bias и отдельно проверяем текущую IM7-ветвь без переноса старого кода.

Самостоятельная область обработки и следующий шаг

Наклон → deskew; численные соседи → convolve; известная форма дефекта → morphology; исходник сохранить
Наклон → deskew; численные соседи → convolve; известная форма дефекта → morphology; исходник сохранить

Упражнение: убрать чёрные точки только вокруг нижнего трафарета, сохранив весь ярлык. Решение: в workflow трафарета оставить cropImage(96,48,280,250), заменить MORPHOLOGY_OPEN на MORPHOLOGY_CLOSE при том же квадрате и одном проходе. Сравнить точки, белые отверстия и границы трафарета; текст вне области должен остаться прежним. Изменённый вариант выполните отдельно — сохранённые свидетельства относятся к исходным восьми программам.

Следующие связи: медиана и локальные статистики, адаптивное размытие и усиление, уровни и контраст, собственная карточка для PNG-вставки. Исторические medianFilterImage/reduceNoiseImage и прочие оставшиеся nodes не исключаются из полной базы.

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

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

Imagick::convolveImageЗаменяет значение пикселя взвешенной суммой его соседей; ядро задаёт коэффициенты и направление. Меняет текущий Imagick, возвращает bool true.

PECL imagick 2, PECL imagick 3 · imagick

Imagick::convolveImage(array $kernel, int $channel = Imagick::CHANNEL_DEFAULT): bool

Самая маленькая запись

Заменяет значение пикселя взвешенной суммой его соседей; ядро задаёт коэффициенты и направление. Меняет текущий Imagick, возвращает bool true. $ начинает имя переменной; = присваивает результат; -> вызывает метод объекта; запятые разделяют аргументы; ; завершает команду. В $image уже загружен непустой PNG. RGB — красная, зелёная и синяя компоненты; | объединяет маски разрешённых каналов. Q=QuantumRange — внутренний максимум, 65535 в проверенной Q16-сборке.

Ядро — маленькая таблица коэффициентов. У 3×3 опорная позиция по умолчанию в центре: она совмещается с текущим пикселем. Коэффициент умножает значение одного соседа; произведения складываются. Для непрозрачного RGB и суммы весов 1 среднее не меняет ровное поле. Например, девять значений 180 с весом 1/9 дают 180; один 0 и восемь 180 дают 160. Уменьшение точки одновременно размывает полезный край. fromMatrix само не нормирует введённые коэффициенты: явные 1/9 здесь задаёт автор. При активной alpha Core может делить взвешенную сумму на сумму alpha-весов — даже если маска выбрана RGB; не переносим простую формулу на произвольный RGBA.

Convolve отражает ядро на 180° относительно опорной позиции. Поэтому переданная средняя строка [-1,1,0] даёт центр−правый сосед. На контрольной точке вес 1 слева переносит её отклик влево. Симметричное среднее скрывает это различие. Sharpen с центром 2 и четырьмя соседями −0.25 имеет сумму 1; отрицательные коэффициенты могут усилить мусор и дать значения за диапазоном, которые ограничиваются до 0..Q. Направленный отклик — карта перепадов, не автоматически улучшенный скан.

[] создаёт массив; три вложенных массива — строки таблицы 3×3. Выражение 1/9 — деление чисел. ImagickKernel::fromMatrix — статический метод класса, :: соединяет класс и имя метода; результат сохраняется в $kernel. Это отдельный объект с коэффициентами, а не картинка. Во фрагменте используем явную RGB-маску:

$kernel = ImagickKernel::fromMatrix([
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
]);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$image->convolveImage($kernel, $rgb);

Clone создаёт отдельную копию растра; вызов метода меняет эту копию. В полных программах ! означает отрицание, if проверяет отказ, throw new RuntimeException останавливает цепочку; затем writeImage сохраняет PNG. Основы: объект и метод, массивы, клонирование, исключения.

Официальная сигнатура и текущий контракт

Imagick::convolveImage(array $kernel, int $channel = Imagick::CHANNEL_DEFAULT): bool
АргументЗначение и ограничения
$kernelВ сохранённом официальном XML — array. В проверенной Imagick 3.8.1 с ImageMagick 7 требуется ImagickKernel; создаём его из вложенного массива через ImagickKernel::fromMatrix. Это реальное различие ветвей IM6/IM7, не два допустимых типа одного вызова.
$channelНеобязательная маска CHANNEL_DEFAULT. Здесь явно RED | GREEN | BLUE, то есть три цветовые компоненты; | объединяет разрешённые каналы. Маска не делает изображение непрозрачным и не исключает влияние alpha на взвешивание.

Официальная доступность: PECL imagick 2, PECL imagick 3. Полная исходная запись ниже сохранена без изменения параметров, примеров и версионных указаний. Все три проверенных метода меняют текущий Imagick и возвращают true при успехе; ни один не возвращает новую картинку или найденный угол. Отказ нативной обработки преобразуется в ImagickException; неверный PHP-тип может дать TypeError.

Ветка IM7 exact 3.8.1 принимает ImagickKernel; raw array из страницы PHP относится к другой ветви. Наша передача массива дала TypeError. Старый официальный пример с setImageBias($bias*getQuantum()) сохранён как исходный материал, а не обещание совместимости со всеми версиями: текущий Core читает convolve:bias/convolve:scale как отдельные настройки. Основные программы их не задают. Для чётных размеров ядра fromMatrix требует явную опорную позицию [x,y]; без неё и при позиции вне ядра получен ImagickKernelException. Нечётное ядро по умолчанию центрировано. Ввод конечных чисел и ограниченный размер — отдельная проверка приложения, а не гарантия каждого внутреннего преобразования массива.

IM7 временно применяет маску каналов у convolve/morphology и восстанавливает прежнюю; deskew не имеет channel-аргумента. Активная alpha влияет на свёртку и может изменяться по умолчанию. RGB-маска сохранила alpha в контрольной пробе, но это не делает любой RGBA похожим на непрозрачный RGB. У основного входа alpha нет. Усреднение, отрицательные веса и геометрическая интерполяция теряют информацию; операция не обязана быть обратимой. Крупные ядра, копии и итерации требуют памяти/времени, скорость здесь не измерялась.

Полный официальный XML, включая все исходные примеры
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->
<refentry xmlns="http://docbook.org/ns/docbook" xml:id="imagick.convolveimage">
 <refnamediv>
  <refname>Imagick::convolveImage</refname>
  <refpurpose>Applies a custom convolution kernel to the image</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::convolveImage</methodname>
   <methodparam><type>array</type><parameter>kernel</parameter></methodparam>
   <methodparam choice="opt"><type>int</type><parameter>channel</parameter><initializer>Imagick::CHANNEL_DEFAULT</initializer></methodparam>
  </methodsynopsis>
  <para>
   Applies a custom convolution kernel to the image.
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>kernel</parameter></term>
     <listitem>
      <para>
      The convolution kernel
      </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::convolveImage</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
function convolveImage($imagePath, $bias, $kernelMatrix) {
    $imagick = new \Imagick(realpath($imagePath));    
    //$edgeFindingKernel = [-1, -1, -1, -1, 8, -1, -1, -1, -1,];
    $imagick->setImageBias($bias * \Imagick::getQuantum());
    $imagick->convolveImage($kernelMatrix);
    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
-->

Ослабить отдельный пиксельный мусор усреднением 3×3

Справа среднее девяти соседних значений. Точки становятся слабее, но края текста и штрихов тоже размываются; это видимый компромисс, а не восстановление недостающих букв.

Ослабить отдельный пиксельный мусор усреднением 3×3Пример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$kernel = ImagickKernel::fromMatrix([
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
    [1/9, 1/9, 1/9],
]);
$average = clone $source;
if (!$average->convolveImage($kernel, $rgb)) { throw new RuntimeException('Convolution failed'); }
saveCleanupComparison([$source, $average], 'convolve-average.png');
echo "convolve-average.png\n";

Справа среднее девяти соседних значений. Точки становятся слабее, но края текста и штрихов тоже размываются; это видимый компромисс, а не восстановление недостающих букв. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
convolve-average.png

Подчеркнуть штрихи в выбранной области и увидеть направленный отклик

В середине умеренное усиление различий внутри ROI (24,90,352,150), справа диагностический отклик центр−правый сосед в той же области. Снаружи ROI и в SKU исходные значения пикселей сохранены.

Подчеркнуть штрихи в выбранной области и увидеть направленный откликПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$region = clone $source;
$region->cropImage(352, 150, 24, 90);
$region->setImagePage(0, 0, 0, 0);
$kernel = ImagickKernel::fromMatrix([
    [0, -0.25, 0],
    [-0.25, 2, -0.25],
    [0, -0.25, 0],
]);
$sharpRegion = clone $region;
if (!$sharpRegion->convolveImage($kernel, $rgb)) { throw new RuntimeException('Sharpen region failed'); }
$sharpened = clone $source;
$sharpened->compositeImage($sharpRegion, Imagick::COMPOSITE_OVER, 24, 90);
$directionKernel = ImagickKernel::fromMatrix([
    [0, 0, 0],
    [-1, 1, 0],
    [0, 0, 0],
]);
$directionRegion = clone $region;
if (!$directionRegion->convolveImage($directionKernel, $rgb)) { throw new RuntimeException('Direction map failed'); }
$direction = clone $source;
$direction->compositeImage($directionRegion, Imagick::COMPOSITE_OVER, 24, 90);
saveCleanupComparison([$source, $sharpened, $direction], 'convolve-protected.png');
echo "convolve-protected.png\n";

В середине умеренное усиление различий внутри ROI (24,90,352,150), справа диагностический отклик центр−правый сосед в той же области. Снаружи ROI и в SKU исходные значения пикселей сохранены. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
convolve-protected.png
Imagick::morphologyСравнивает пиксель с окрестностью заданной формы; выбранный метод расширяет/сужает светлые области или сочетает стадии. Меняет текущий Imagick, возвращает bool true.

PECL imagick 3 >= 3.3.0 · imagick

Imagick::morphology(int $morphologyMethod, int $iterations, ImagickKernel $ImagickKernel, int $channel = Imagick::CHANNEL_DEFAULT): bool

Самая маленькая запись

Сравнивает пиксель с окрестностью заданной формы; выбранный метод расширяет/сужает светлые области или сочетает стадии. Меняет текущий Imagick, возвращает bool true. $ начинает имя переменной; = присваивает результат; -> вызывает метод объекта; запятые разделяют аргументы; ; завершает команду. В $image уже загружен непустой PNG. RGB — красная, зелёная и синяя компоненты; | объединяет маски разрешённых каналов. Q=QuantumRange — внутренний максимум, 65535 в проверенной Q16-сборке.

Для белого фона и чёрной печати ERODE выбирает минимум в окрестности и расширяет чёрное, DILATE выбирает максимум и расширяет белое. OPEN = ERODE, затем DILATE: может закрыть маленькие белые отверстия в чёрном. CLOSE = DILATE, затем ERODE: может убрать маленькие чёрные островки на белом. Это та же пара правил, которую часто описывают для светлого foreground; смена полярности меняет бытовое объяснение. Библиотека не различает пылинку и нужную точку буквы. Размер ядра нужно сравнивать с толщиной нужного штриха, а не выбирать по слову «очистка».

ImagickKernel::fromMatrix создаёт объект ядра из массива строк. Imagick::MORPHOLOGY_CLOSE — целочисленная константа класса; :: здесь обращается к константе, не к переменной. Второй аргумент 1 — число проходов. Девять единиц задают квадратную форму соседства:

$kernel = ImagickKernel::fromMatrix([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
]);
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$image->morphology(Imagick::MORPHOLOGY_CLOSE, 1, $kernel, $rgb);

Clone создаёт отдельную копию растра; вызов метода меняет эту копию. В полных программах ! означает отрицание, if проверяет отказ, throw new RuntimeException останавливает цепочку; затем writeImage сохраняет PNG. Основы: объект и метод, массивы, клонирование, исключения.

Официальная сигнатура и текущий контракт

Imagick::morphology(int $morphologyMethod, int $iterations, ImagickKernel $ImagickKernel, int $channel = Imagick::CHANNEL_DEFAULT): bool
АргументЗначение и ограничения
$morphologyMethodОбязательная int-константа MORPHOLOGY_*. Это правило обработки, а не строка PHP или пользовательская функция.
$iterationsОбязательное целое число проходов. В основных программах 1. Для составных методов число относится к примитивным стадиям; это не обещание простого повторения готового OPEN/CLOSE. Raw -1 означает до прекращения изменений; Core ограничивает такой цикл размером изображения, но он всё равно может быть дорогим. Ноль в нашей пробе дал ImagickException, не копию входа.
$ImagickKernelОбязательный непустой ImagickKernel с формой окрестности. В квадрате 3×3 девять единиц обозначают участвующие позиции; это не среднее девяти значений. false в fromMatrix может обозначать пропуск, а не числовой ноль.
$channelНеобязательная маска CHANNEL_DEFAULT. Явная RGB-маска отделяет изменение цветов от alpha на нашей пробе; для прозрачного входа общий эффект требуется проверять отдельно.

Официальная доступность: PECL imagick 3 >= 3.3.0. Полная исходная запись ниже сохранена без изменения параметров, примеров и версионных указаний. Все три проверенных метода меняют текущий Imagick и возвращают true при успехе; ни один не возвращает новую картинку или найденный угол. Отказ нативной обработки преобразуется в ImagickException; неверный PHP-тип может дать TypeError.

Метод требует ImageMagick 6.8.0 или новее; ImagickKernel и PHP morphology доступны с PECL 3.3.0. Reflection проверенной 3.8.1 называет первые аргументы morphology/iterations/kernel, тогда как raw использует morphologyMethod/ImagickKernel; наши вызовы позиционные. Все 26 официальных примеров и общий helper сохранены в полном XML. Они включают $this->getCharacter()/getCharacterOutline(), то есть контекст класса, а не готовые самостоятельные программы. Наши два ZIP содержат собственный вход и полный helper, не требуют угадывать $this.

СемействоЧто учитывать
CONVOLVE / CORRELATEВзвешенная сумма; различается отражение ядра. Это не min/max. См. отдельную модель свёртки.
ERODE / DILATEМинимум/максимум значений участвующих соседей; меняют размер светлых областей.
ERODE_INTENSITY / DILATE_INTENSITYВыбирают по интенсивности, то есть численной яркости из цветовых компонентов; отличаются от отдельного min/max каждого канала.
OPEN / CLOSEДве стадии для сглаживания формы и удаления небольших островков либо отверстий; полярность принципиальна.
OPEN_INTENSITY / CLOSE_INTENSITYСоставные варианты с выбором по интенсивности; не обещают сохранение каждого исходного цвета.
SMOOTHПоследовательность нескольких примитивных стадий; действие шире одного OPEN.
EDGE / EDGE_IN / EDGE_OUTВыделяют различия у границы формы, внутри либо снаружи; это растр, не векторный контур.
TOP_HAT / BOTTOM_HATРазности с результатами открытия/закрытия выделяют мелкие светлые или тёмные структуры; не являются распознаванием текста.
HIT_AND_MISSСопоставление конфигурации соседей; важно различать требуемый фон, foreground и пропущенные позиции.
THINNING / THICKENУтончение/утолщение по шаблонам, часто с несколькими ядрами; могут менять связность.
DISTANCEКарта расстояний с выбранной метрикой ядра: Chebyshev/Manhattan/Octagonal/Euclidean в официальных примерах. Число результата не толщина линии в любой шкале.
ITERATIVE / построение convex hullПовторяемая обработка/несколько ядер; официальные примеры сохраняем, но основные программы ограничены одним проходом. Итерации до стабилизации не имеют постоянной цены.

В raw helper есть исторические имена операторов/подготовка силуэта. Их нельзя механически переносить на текущую IM7-сборку или чужую alpha. Основные OPEN/CLOSE рассчитаны на известную непрозрачную графику; результат не является OCR, поиском букв или универсальной реставрацией документа.

IM7 временно применяет маску каналов у convolve/morphology и восстанавливает прежнюю; deskew не имеет channel-аргумента. Активная alpha влияет на свёртку и может изменяться по умолчанию. RGB-маска сохранила alpha в контрольной пробе, но это не делает любой RGBA похожим на непрозрачный RGB. У основного входа alpha нет. Усреднение, отрицательные веса и геометрическая интерполяция теряют информацию; операция не обязана быть обратимой. Крупные ядра, копии и итерации требуют памяти/времени, скорость здесь не измерялась.

Полный официальный XML, включая все исходные примеры
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->

<refentry xml:id="imagick.morphology" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::morphology</refname>
  <refpurpose>Applies a user supplied kernel to the image according to the given morphology method.</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::morphology</methodname>
   <methodparam><type>int</type><parameter>morphologyMethod</parameter></methodparam>
   <methodparam><type>int</type><parameter>iterations</parameter></methodparam>
   <methodparam><type>ImagickKernel</type><parameter>ImagickKernel</parameter></methodparam>
   <methodparam choice="opt"><type>int</type><parameter>channel</parameter><initializer>Imagick::CHANNEL_DEFAULT</initializer></methodparam>
  </methodsynopsis>
  <para>
   Applies a user supplied kernel to the image according to the given morphology method.

  </para>

 
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <variablelist>
   <varlistentry>
    <term><parameter>morphologyMethod</parameter></term>
    <listitem>
     <para>
      Which morphology method to use one of the \Imagick::MORPHOLOGY_* constants.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term><parameter>iterations</parameter></term>
    <listitem>
     <para>
      The number of iteration to apply the morphology function. A value of -1 means loop until no change found. How this is applied may depend on the morphology method. Typically this is a value of 1.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term><parameter>ImagickKernel</parameter></term>
    <listitem>
     <para>
      
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term><parameter>channel</parameter></term>
    <listitem>
     <para>
      
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </refsect1>

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




 <refsect1 role="examples">
  &reftitle.examples;
  <para>
    <example>
      <title>Convolve <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $imagick = $this->getCharacter();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_GAUSSIAN, "5,1");
        $imagick->morphology(\Imagick::MORPHOLOGY_CONVOLVE, 2, $kernel);
        header("Content-Type: image/png");
        echo $imagick->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Correlate <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php

        // Top-left pixel must be black
        // Bottom right pixel must be white
        // We don't care about the rest.
        

        $imagick = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromMatrix(self::$correlateMatrix, [2, 2]);
        $imagick->morphology(\Imagick::MORPHOLOGY_CORRELATE, 1, $kernel);
        header("Content-Type: image/png");
        echo $imagick->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Erode <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGON, "3");
        $canvas->morphology(\Imagick::MORPHOLOGY_ERODE, 2, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Erode Intensity <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacter();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGON, "1");
        $canvas->morphology(\Imagick::MORPHOLOGY_ERODE_INTENSITY, 2, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Dilate <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGON, "3");
        $canvas->morphology(\Imagick::MORPHOLOGY_DILATE, 4, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Dilate intensity <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacter();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGON, "1");
        $canvas->morphology(\Imagick::MORPHOLOGY_DILATE_INTENSITY, 4, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Distance with Chebyshev kernel <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_CHEBYSHEV, "3");
        $canvas->morphology(\Imagick::MORPHOLOGY_DISTANCE, 3, $kernel);
        $canvas->autoLevelImage();
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Distance with Manhattan kernel <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_MANHATTAN, "5");
        $canvas->morphology(\Imagick::MORPHOLOGY_DISTANCE, 3, $kernel);
        $canvas->autoLevelImage();
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Distance with ocatagonal kernel <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGONAL, "5");
        $canvas->morphology(\Imagick::MORPHOLOGY_DISTANCE, 3, $kernel);
        $canvas->autoLevelImage();
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Distance with Euclidean kernel <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_EUCLIDEAN, "4");
        $canvas->morphology(\Imagick::MORPHOLOGY_DISTANCE, 3, $kernel);
        $canvas->autoLevelImage();
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Edge <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGON, "3");
        $canvas->morphology(\Imagick::MORPHOLOGY_EDGE, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Open <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        // As a result you will see that 'Open' smoothed the outline, by rounding off any sharp points, and remove any parts that is smaller than the shape used. It will also disconnect or 'open' any thin bridges.
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DISK, "6");
        $canvas->morphology(\Imagick::MORPHOLOGY_OPEN, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Open intensity <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        // As a result you will see that 'Open' smoothed the outline, by rounding off any sharp points, and remove any parts that is smaller than the shape used. It will also disconnect or 'open' any thin bridges.

        $canvas = $this->getCharacter();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DISK, "6");
        $canvas->morphology(\Imagick::MORPHOLOGY_OPEN_INTENSITY, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Close <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        //The basic use of the 'Close' method is to reduce or remove any 'holes' or 'gaps' about the size of the kernel 'Structure Element'. That is 'close' parts of the background that are about that size.
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DISK, "6");
        $canvas->morphology(\Imagick::MORPHOLOGY_CLOSE, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Close Intensity <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        //The basic use of the 'Close' method is to reduce or remove any 'holes' or 'gaps' about the size of the kernel 'Structure Element'. That is 'close' parts of the background that are about that size.
        $canvas = $this->getCharacter();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DISK, "6");
        $canvas->morphology(\Imagick::MORPHOLOGY_CLOSE_INTENSITY, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Smooth <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGON, "3");
        $canvas->morphology(\Imagick::MORPHOLOGY_SMOOTH, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Edge in <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGON, "3");
        $canvas->morphology(\Imagick::MORPHOLOGY_EDGE_IN, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Edge out <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_OCTAGON, "3");
        $canvas->morphology(\Imagick::MORPHOLOGY_EDGE_OUT, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>The 'TopHat' method, or more specifically 'White Top Hat', returns the pixels that were removed by a Opening of the shape, that is the pixels that were removed to round off the points, and the connecting bridged between shapes. <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DISK, "5");
        $canvas->morphology(\Imagick::MORPHOLOGY_TOP_HAT, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>The 'BottomHat' method, also known as 'Black TopHat' is the pixels that a Closing of the shape adds to the image. That is the pixels that were used to fill in the 'holes', 'gaps', and 'bridges'. <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php

        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DISK, "5");
        $canvas->morphology(\Imagick::MORPHOLOGY_BOTTOM_HAT, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Hit and Miss <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        //This finds all the pixels with 3 pixels of the right edge
        $matrix = [[1, false, false, 0]];
        $kernel = \ImagickKernel::fromMatrix(
            $matrix,
            [0, 0]
        );
        $canvas->morphology(\Imagick::MORPHOLOGY_HIT_AND_MISS, 1, $kernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Thinning <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $leftEdgeKernel = \ImagickKernel::fromMatrix([[0, 1]], [1, 0]);
        $rightEdgeKernel = \ImagickKernel::fromMatrix([[1, 0]], [0, 0]);
        $leftEdgeKernel->addKernel($rightEdgeKernel);
        
        $canvas->morphology(\Imagick::MORPHOLOGY_THINNING, 3, $leftEdgeKernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Thicken <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $leftEdgeKernel = \ImagickKernel::fromMatrix([[0, 1]], [1, 0]);
        $rightEdgeKernel = \ImagickKernel::fromMatrix([[1, 0]], [0, 0]);
        $leftEdgeKernel->addKernel($rightEdgeKernel);

        $canvas->morphology(\Imagick::MORPHOLOGY_THICKEN, 3, $leftEdgeKernel);
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Thick to generate a convex hull <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $diamondKernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DIAMOND, "1");
        $convexKernel =  \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_CONVEX_HULL, "");

        // The thicken morphology doesn't handle small gaps. We close them
        // with the close morphology.
        $canvas->morphology(\Imagick::MORPHOLOGY_CLOSE, 1, $diamondKernel);
        $canvas->morphology(\Imagick::MORPHOLOGY_THICKEN, -1, $convexKernel);
        $canvas->morphology(\Imagick::MORPHOLOGY_CLOSE, 1, $diamondKernel);

        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Iterative morphology <function>Imagick::morphology</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
        $canvas = $this->getCharacterOutline();
        $kernel = \ImagickKernel::fromBuiltIn(\Imagick::KERNEL_DISK, "2");        
        $canvas->morphology(\Imagick::MORPHOLOGY_ITERATIVE, 3, $kernel);
        $canvas->autoLevelImage();
        header("Content-Type: image/png");
        echo $canvas->getImageBlob();

?>
]]>
      </programlisting>
    </example>

    <example>
      <title>Helper function to get an image silhouette <function>Imagick::morphology</function></title>
      <programlisting role="php">
<![CDATA[
<?php
function getCharacterOutline() {
    $imagick = new \Imagick(realpath("./images/character.png"));
    $character = new \Imagick();
    $character->newPseudoImage(
        $imagick->getImageWidth(),
        $imagick->getImageHeight(),
        "canvas:white"
    );
    $canvas = new \Imagick();
    $canvas->newPseudoImage(
        $imagick->getImageWidth(),
        $imagick->getImageHeight(),
        "canvas:black"
    );

    $character->compositeimage(
        $imagick,
        \Imagick::COMPOSITE_COPYOPACITY,
        0, 0
    );
    $canvas->compositeimage(
        $character,
        \Imagick::COMPOSITE_ATOP,
        0, 0
    );
    $canvas->setFormat('png');

    return $canvas;
}
?>
]]>
      </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
-->

Удалить мелкие чёрные точки на белом поле и увидеть потери

CLOSE убирает отдельные чёрные точки, но в нашем результате большинство тонких букв почти исчезло. Даже нижний контроль теряет часть тёмных пикселей: 1184 → 1111 при пороге R<80. Эту глобальную очистку отклоняем; уменьшение мусора не подтверждает читаемость.

Удалить мелкие чёрные точки на белом поле и увидеть потериПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$kernel = ImagickKernel::fromMatrix([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
]);
$closed = clone $source;
if (!$closed->morphology(Imagick::MORPHOLOGY_CLOSE, 1, $kernel, $rgb)) { throw new RuntimeException('Closing failed'); }
saveCleanupComparison([$source, $closed], 'morphology-close.png');
echo "morphology-close.png\n";

CLOSE убирает отдельные чёрные точки, но в нашем результате большинство тонких букв почти исчезло. Даже нижний контроль теряет часть тёмных пикселей: 1184 → 1111 при пороге R<80. Эту глобальную очистку отклоняем; уменьшение мусора не подтверждает читаемость. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
morphology-close.png

Закрыть белые отверстия в известном чёрном трафарете

OPEN применяется только к контрольному трафарету (280,250,96,48): небольшие белые отверстия становятся чёрными. Остальной скан и SKU не обрабатываются. Сам трафарет сохранён отдельно.

Закрыть белые отверстия в известном чёрном трафаретеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$stencil = clone $source;
$stencil->cropImage(96, 48, 280, 250);
$stencil->setImagePage(0, 0, 0, 0);
$kernel = ImagickKernel::fromMatrix([
    [1, 1, 1],
    [1, 1, 1],
    [1, 1, 1],
]);
if (!$stencil->morphology(Imagick::MORPHOLOGY_OPEN, 1, $kernel, $rgb)) { throw new RuntimeException('Opening failed'); }
$result = clone $source;
$result->compositeImage($stencil, Imagick::COMPOSITE_OVER, 280, 250);
saveCleanupNative($stencil, 'morphology-mask.png');
saveCleanupComparison([$source, $result], 'morphology-stencil.png');
echo "morphology-stencil.png\n";

OPEN применяется только к контрольному трафарету (280,250,96,48): небольшие белые отверстия становятся чёрными. Остальной скан и SKU не обрабатываются. Сам трафарет сохранён отдельно. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
morphology-stencil.png
Imagick::deskewImageОценивает небольшой наклон по тёмным пикселям и поворачивает весь текущий растр; возвращает bool true. Угол читают отдельно после вызова.

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

Imagick::deskewImage(float $threshold): bool

Самая маленькая запись

Оценивает небольшой наклон по тёмным пикселям и поворачивает весь текущий растр; возвращает bool true. Угол читают отдельно после вызова. $ начинает имя переменной; = присваивает результат; -> вызывает метод объекта; запятые разделяют аргументы; ; завершает команду. В $image уже загружен непустой PNG. RGB — красная, зелёная и синяя компоненты; | объединяет маски разрешённых каналов. Q=QuantumRange — внутренний максимум, 65535 в проверенной Q16-сборке.

Q=QuantumRange — внутренний максимум канала, 65535 в проверенной Q16-сборке; выходной RGB PNG хранит 8 бит и значения 0..255 на компоненту. Imagick::getQuantumRange() — статический метод класса, который возвращает массив; ['quantumRangeLong'] читает значение по ключу. * умножает Q на долю 0.4, а не задаёт угол. Порог из CLI с % нельзя просто передать числом 40. Возвращённое true — статус; найденный угол в градусах хранится в deskew:angle.

$Q = Imagick::getQuantumRange()['quantumRangeLong'];
$image->setImageBackgroundColor('white');
$image->deskewImage(0.4 * $Q);

Clone создаёт отдельную копию растра; вызов метода меняет эту копию. В полных программах ! означает отрицание, if проверяет отказ, throw new RuntimeException останавливает цепочку; затем writeImage сохраняет PNG. Основы: объект и метод, массивы, клонирование, исключения.

Официальная сигнатура и текущий контракт

Imagick::deskewImage(float $threshold): bool
АргументЗначение и ограничения
$thresholdОбязательный float в Quantum-единицах: T=0.4×Q, здесь 26214. В Radon-анализ попадает пиксель, у которого хотя бы одна из R/G/B строго меньше T. Это порог отделения фона от штрихов, не число градусов и не процент наклона.

Официальная доступность: PECL imagick 2 >= 2.3.0, PECL imagick 3 >= 3.3.0. Полная исходная запись ниже сохранена без изменения параметров, примеров и версионных указаний. Все три проверенных метода меняют текущий Imagick и возвращают true при успехе; ни один не возвращает новую картинку или найденный угол. Отказ нативной обработки преобразуется в ImagickException; неверный PHP-тип может дать TypeError.

Raw указывает ImageMagick 6.4.5 или новее. Алгоритм рассчитан на небольшой наклон строк, приблизительно в пределах ±7°, а не на поворот страницы на 90°, перспективу трапеции или EXIF-ориентацию. Core анализирует проекции тёмных пикселей и выполняет аффинное преобразование, принудительно используя виртуальный фон. Размер растра и page могут измениться. Белый background задаём явно. deskew:auto-crop — отдельный параметр; здесь его не включаем, сохраняем полный результат. Даже плоский белый контроль с нулевым углом стал 19×13 вместо 17×11: zero angle не обещает побайтную копию.

getImageArtifact('deskew:angle') читает текст вычисленного угла. (float) переводит его в число; round(...,2) округляет до двух знаков только для вывода. Точка . соединяет строки, "\n" добавляет перевод строки. getImagePage() описывает виртуальное положение текущего объекта; это отдельно от ширины/высоты реального PNG и отдельно от координат размещения панели сравнения. После crop перед новым анализом явно сбрасываем page. Пустой Imagick дал ImagickException; наша политика внешних данных дополнительно ограничивает размеры и проверяет конечный порог в 0..Q, но фиксированные программы не реализуют веб-загрузчик.

IM7 временно применяет маску каналов у convolve/morphology и восстанавливает прежнюю; deskew не имеет channel-аргумента. Активная alpha влияет на свёртку и может изменяться по умолчанию. RGB-маска сохранила alpha в контрольной пробе, но это не делает любой RGBA похожим на непрозрачный RGB. У основного входа alpha нет. Усреднение, отрицательные веса и геометрическая интерполяция теряют информацию; операция не обязана быть обратимой. Крупные ядра, копии и итерации требуют памяти/времени, скорость здесь не измерялась.

Полный официальный XML, включая все исходные примеры
<?xml version="1.0" encoding="utf-8"?>
<!-- $Revision$ -->

<refentry xml:id="imagick.deskewimage" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::deskewImage</refname>
  <refpurpose>Removes skew from the image</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::deskewImage</methodname>
   <methodparam><type>float</type><parameter>threshold</parameter></methodparam>
  </methodsynopsis>
  <para>
   This method can be used to remove skew from for example scanned images where the paper
   was not properly placed on the scanning surface. &imagick.method.available.0x645;
  </para>
 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <para>
   <variablelist>
    <varlistentry>
     <term><parameter>threshold</parameter></term>
     <listitem>
      <para>
       Deskew threshold
      </para>
     </listitem>
    </varlistentry>
   </variablelist>
  </para>
 </refsect1>

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

  </para>
 </refsect1>


 <refsect1 role="examples">
  &reftitle.examples;
  <para>
    <example>
      <title> <function>Imagick::deskewImage</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
function deskewImage($threshold) {
    $imagick = new \Imagick(realpath("images/NYTimes-Page1-11-11-1918.jpg"));
    $deskewImagick = clone $imagick;
    
    //This is the only thing required for deskewing.
    $deskewImagick->deskewImage($threshold);

    //The rest of this example is to make the result obvious - because
    //otherwise the result is not obvious.
    $trim = 9;

    $deskewImagick->cropImage($deskewImagick->getImageWidth() - $trim, $deskewImagick->getImageHeight(), $trim, 0);
    $imagick->cropImage($imagick->getImageWidth() - $trim, $imagick->getImageHeight(), $trim, 0);
    $deskewImagick->resizeimage($deskewImagick->getImageWidth() / 2, $deskewImagick->getImageHeight() / 2, \Imagick::FILTER_LANCZOS, 1);
    $imagick->resizeimage($imagick->getImageWidth() / 2, $imagick->getImageHeight() / 2, \Imagick::FILTER_LANCZOS, 1);
    $newCanvas = new \Imagick();
    $newCanvas->newimage($imagick->getImageWidth() + $deskewImagick->getImageWidth() + 20, $imagick->getImageHeight(), 'red', 'jpg');
    $newCanvas->compositeimage($imagick, \Imagick::COMPOSITE_COPY, 5, 0);
    $newCanvas->compositeimage($deskewImagick, \Imagick::COMPOSITE_COPY, $imagick->getImageWidth() + 10, 0);

    header("Content-Type: image/jpg");
    echo $newCanvas->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
-->

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

Полный результат 423×349, вычисленный угол после округления 4.02°. Это результат анализа входа, а не угол, переданный первым аргументом. Фон новых полей белый.

Выровнять полный скан и прочитать найденный уголПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$Q = Imagick::getQuantumRange()['quantumRangeLong'];
$clean = clone $source;
if (!$clean->deskewImage(0.4 * $Q)) { throw new RuntimeException('Deskew failed'); }
saveCleanupComparison([$source, $clean], 'deskew-full.png');
echo "deskew-full.png\nangle=" . round((float)$clean->getImageArtifact('deskew:angle'), 2) . "\n";

Полный результат 423×349, вычисленный угол после округления 4.02°. Это результат анализа входа, а не угол, переданный первым аргументом. Фон новых полей белый. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
deskew-full.png
angle=4.02

Подготовить отдельную ровную PNG-вставку из области ярлыка

Слева полный скан, в середине выбранный ярлык 352×154, справа его самостоятельный полный результат 364×180. label-before.png и label-deskew.png пригодны для отдельной вставки; угол 3.91° отличается из-за другой анализируемой области.

Подготовить отдельную ровную PNG-вставку из области ярлыкаПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__ . '/cleanup-fixture.php';
$source = loadCleanupSource();
$rgb = Imagick::CHANNEL_RED | Imagick::CHANNEL_GREEN | Imagick::CHANNEL_BLUE;
$label = clone $source;
$label->cropImage(352, 154, 24, 86);
$label->setImagePage(0, 0, 0, 0);
$clean = clone $label;
$Q = Imagick::getQuantumRange()['quantumRangeLong'];
if (!$clean->deskewImage(0.4 * $Q)) { throw new RuntimeException('Label deskew failed'); }
saveCleanupNative($label, 'label-before.png');
saveCleanupNative($clean, 'label-deskew.png');
saveCleanupComparison([$source, $label, $clean], 'deskew-label.png');
echo "deskew-label.png\nangle=" . round((float)$clean->getImageArtifact('deskew:angle'), 2) . "\n";

Слева полный скан, в середине выбранный ярлык 352×154, справа его самостоятельный полный результат 364×180. label-before.png и label-deskew.png пригодны для отдельной вставки; угол 3.91° отличается из-за другой анализируемой области. В отдельной папке нужны example.php, cleanup-fixture.php и source.png.b64; сохраните и остальные файлы ZIP. Запуск: php example.php с Imagick 3.8.1/IM7. Все входы включены, шрифт и сеть PHP не нужны. PNG сравнения содержит целые панели без масштабирования; отдельные PNG результата перечислены в программе.

Ожидаемый вывод
deskew-label.png
angle=3.91
Источники и обсуждения

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

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

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

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