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

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

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

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

Глава 382 · После объектов, клонирования, последовательностей изображений и исключений

Imagick: преобразование Фурье, два компонента и проверенный inverse

Исследовать периодические полосы в частотном представлении, сохранить правильную пару и измерить обратное восстановление в настоящей FFTW-сборке.

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

Повторяющиеся полосы на скане или изображении товара можно изучать как повторение яркости с определённым шагом. Частотное представление помогает исследовать такую структуру перед выбором фильтра. Начнём с собственного рисунка: к нему добавлены вертикальные полосы с периодом 16 пикселей и амплитудой до 18 уровней RGB8. Посмотрим на настоящее преобразование и сначала проверим обратный путь без изменения компонентов.

Сигнал с полосами, отдельный log-просмотр magnitude, просмотр phase и реальное обратное восстановление; полосы намеренно сохранены
Сигнал с полосами, отдельный log-просмотр magnitude, просмотр phase и реальное обратное восстановление; полосы намеренно сохранены

Представьте изображение как сочетание волн яркости с разным шагом и расположением. Magnitude показывает вклад частот, phase нужна для их согласования при восстановлении. Это модель численных данных по каналам, а не новая фотография объекта. Одной яркой картинки спектра недостаточно для обратного преобразования. В примере не заявлены ни координаты неизвестных пиков, ни универсальное удаление помех.

$ok = $image->forwardFourierTransformImage(true);
$image->setIteratorIndex(0);
$magnitude = $image->getImage();
$image->setIteratorIndex(1);
$phase = $image->getImage();

$image — переменная с объектом Imagick; -> вызывает метод. true — логическое значение, его не пишут в кавычках. $ok получает bool успеха. Сам объект теперь хранит два кадра: индекс 0 — первый, индекс 1 — второй. setIteratorIndex выбирает текущий кадр, getImage создаёт отдельный объект выбранного кадра. Это не чтение $ok[0]: массив и объект с последовательностью изображений — разные структуры.

Увидеть частотное представление полос и измеренное обратное восстановление

Показаны исходный сигнал с полосами, отдельный усиленный просмотр magnitude, просмотр phase и реальный inverse. Результат снова содержит те же полосы: здесь их изучают, а не удаляют. В проверенной Q16-сборке максимальная ошибка восстановленного RGB8 — 2 уровня из 255, RMSE≈0.459784.

Увидеть частотное представление полос и измеренное обратное восстановлениеПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/fourier-fixture.php';
fourierRestore();
$base=fourierBase(); $signal=fourierStriped($base);
$baseBefore=fourierHash($base);$before=fourierHash($signal);
$work=clone $signal;$forward=$work->forwardFourierTransformImage(true);
$index=$work->getIteratorIndex();$count=$work->getNumberImages();
$work->setIteratorIndex(0);$magnitude=$work->getImage();
$work->setIteratorIndex(1);$phase=$work->getImage();
$a=fourierHash($magnitude);$b=fourierHash($phase);
$padded=clone $magnitude;$inverse=$padded->inverseFourierTransformImage($phase,true);
$paddedSize=[$padded->getImageWidth(),$padded->getImageHeight()];
$fullError=fourierError(fourierPadded($signal),$padded);
$restored=clone $padded;$restored->cropImage(400,320,0,0);$restored->setImagePage(0,0,0,0);
$log=fourierLog($magnitude);
fourierView($base,'source-preview8.png');fourierView($signal,'striped-preview8.png');
fourierView($magnitude,'magnitude-raw8.png');fourierView($phase,'phase-raw8.png');
fourierView($log,'magnitude-log8.png');fourierView($restored,'restored-preview8.png');
$rects=fourierCompare([$signal,$log,$phase,$restored],'fourier-first.png');
$error=fourierError($signal,$restored);$pairUnchanged=$a===fourierHash($magnitude)&&$b===fourierHash($phase);
fourierDiagnostic('fourier-transform-first',['executed_apis'=>['forwardFourierTransformImage','inverseFourierTransformImage'],
    'representation'=>true,'forward_return'=>$forward,'inverse_return'=>$inverse,'pair_count'=>$count,'index_after_forward'=>$index,
    'pair_sizes'=>[[$magnitude->getImageWidth(),$magnitude->getImageHeight()],[$phase->getImageWidth(),$phase->getImageHeight()]],
    'inverse_padded_size'=>$paddedSize,'crop_size'=>[$restored->getImageWidth(),$restored->getImageHeight()],
    'virtual_pixel_method'=>$signal->getImageVirtualPixelMethod(),'first_double_range'=>fourierRange($magnitude),'second_double_range'=>fourierRange($phase),
    'full_padded_error'=>$fullError,'error_vs_striped'=>$error,'pair_unchanged'=>$pairUnchanged,'panel_geometry'=>$rects,
    'checks'=>['forward_true'=>$forward===true,'inverse_true'=>$inverse===true,'two_images'=>$count===2,'index_zero'=>$index===0,
        'even_square_pair'=>$magnitude->getImageWidth()===400&&$magnitude->getImageHeight()===400&&$phase->getImageWidth()===400&&$phase->getImageHeight()===400,
        'padded_geometry'=>$paddedSize===[400,400],'small_measured_RGB8_error'=>$error['RGB8_max_abs']<=2,
        'raw_pair_unchanged'=>$pairUnchanged,'source_unchanged'=>fourierHash($base)===$baseBefore&&fourierHash($signal)===$before]]);
echo "fourier-first.png\nsource-preview8.png\nstriped-preview8.png\nmagnitude-raw8.png\nphase-raw8.png\nmagnitude-log8.png\nrestored-preview8.png\nmetrics.json\n";
printf("forward=bool:%s pair=%d inverse=bool:%s padded=%dx%d crop=400x320 max=%d RMSE=%.6f\n",
    $forward?'true':'false',$count,$inverse?'true':'false',$paddedSize[0],$paddedSize[1],$error['RGB8_max_abs'],$error['RGB8_RMSE']);

Показаны исходный сигнал с полосами, отдельный усиленный просмотр magnitude, просмотр phase и реальный inverse. Результат снова содержит те же полосы: здесь их изучают, а не удаляют. В проверенной Q16-сборке максимальная ошибка восстановленного RGB8 — 2 уровня из 255, RMSE≈0.459784. Архив содержит example.php, source.png.b64 и fourier-fixture.php. Сохраните рядом и запустите php example.php с Imagick, реально работающим FFTW, поддержкой PNG и правом записи. Проверенная среда: Ubuntu 24.04 / WSL 2, PHP 8.3.6, Imagick 3.7.0, ImageMagick 6.9.12-98 Q16 без HDRI. В другой сборке сначала проверьте поддержку FFTW; наличие имени метода этого не доказывает. Полный helper приведён в конце главы: он восстанавливает собственный PNG, готовит непрозрачный сигнал с полосами, сохраняет просмотры и измерения. Только inverse-minimal использует явно объявленную подготовку настоящей пары через forward(true); численные компоненты не читаются из PNG8.

Ожидаемый вывод в указанной сборке
fourier-first.png
source-preview8.png
striped-preview8.png
magnitude-raw8.png
phase-raw8.png
magnitude-log8.png
restored-preview8.png
metrics.json
forward=bool:true pair=2 inverse=bool:true padded=400x400 crop=400x320 max=2 RMSE=0.459784

Как читать числа результата: max и RMSE в stdout и metrics.json измеряются через exportImagePixels(..., PIXEL_CHAR) непосредственно после inverse и crop, до сохранения PNG8. Это проверка численных пикселей в памяти. Запись PNG8 выполняет отдельное округление. При независимом сравнении именно сохранённых PNG8 с сохранённым сигналом получены: true — max=3, RMSE≈0.737636; false — max=116, RMSE≈41.092654. Поэтому числа из stdout нельзя выдавать за измерение готового PNG-файла. Оба этапа относятся к тому же исходному сигналу и проверенной сборке; изображение спектра для просмотра по-прежнему не используется как численный вход inverse.

Разобрать два компонента, расширение холста и отдельную ветку просмотра

Численная пара в памяти идёт в inverse с тем же true; отдельные PNG8 и log-копии предназначены только для просмотра
Численная пара в памяти идёт в inverse с тем же true; отдельные PNG8 и log-копии предназначены только для просмотра
СостояниеЧто на самом деле происходит
До forwardСигнал имеет 400 × 320. Мы явно выбираем чёрные виртуальные пиксели и работаем с непрозрачным RGB.
После forward(true)В объекте два кадра 400 × 400. Индекс 0 — magnitude, индекс 1 — phase. Это действительные численные компоненты в памяти.
ПросмотрДля PNG8 создаются копии. Для читаемой magnitude ещё одна копия получает log и autoLevel. Численные компоненты остаются прежними.
inverse(..., true)Берёт первый компонент как текущий объект и соответствующий второй как complement. На выходе 400 × 400, затем crop возвращает 400 × 320.
ПроверкаСравниваем восстановление с тем же сигналом с полосами, а не с рисунком до добавления полос. Иначе измеряли бы другую задачу.

В точном Core прямоугольный или нечётный вход расширяется до чётного квадрата со стороной не меньше max(width,height). Поэтому 400 × 320 становится 400 × 400. Это не требование степени двойки. Виртуальные пиксели задают, что библиотека прочитает за прежней границей; в примерах это явно BLACK, поэтому добавленные 80 строк чёрные. Мы сохраняем исходный размер и после inverse явно вырезаем верхние 400 × 320, затем сбрасываем виртуальное смещение setImagePage(0,0,0,0).

В этом Core нормировка по умолчанию выполняется на forward. Не нужно автоматически делить результат inverse ещё раз на число пикселей. Phase для true хранится с преобразованием phase/(2π)+0.5, чтобы представить углы в диапазоне интенсивностей. В real/imaginary появляются знаковые значения, которым целочисленный non-HDRI Q16 может не дать сохраниться. Получить true от обоих методов и восстановить картинку без потери — разные утверждения.

Сохранённые magnitude-raw8.png и phase-raw8.png — просмотры, не исходные коэффициенты для повторной загрузки. Даже обычное сохранение их как PNG8 снижает точность; log/autoLevel дополнительно меняет значения. Мы не читаем эти PNG обратно в inverse. Хэши неизменности пары вычисляются по PIXEL_DOUBLE, чтобы проверка не потеряла мелкие отличия Q16 из-за предварительного перевода в 8 бит.

Запустить короткий forward и короткий inverse с правильным complement

Настоящие forward-просмотры двух компонентов и отдельный минимальный inverse из численной пары
Настоящие forward-просмотры двух компонентов и отдельный минимальный inverse из численной пары

Каждый архив ниже самостоятельный. source.png.b64 содержит собственный рисунок; fourierRestore проверяет его SHA и восстанавливает PNG. fourierBase кладёт его на белую основу и отключает альфа-сигнал через ALPHACHANNEL_DEACTIVATE, поддерживаемый этой IM6-сборкой. fourierStriped создаёт учебные полосы и устанавливает чёрные виртуальные пиксели. Эти функции подготовки не выполняют FFT. После такой явно объявленной подготовки минимальный forward показывает сам изучаемый вызов и выбор обоих кадров.

Получить bool и два настоящих кадра, а не массив из return

Метод вернул true, а объект $work теперь содержит два изображения 400 × 400. Индекс 0 даёт magnitude, индекс 1 — phase. Для видимого спектра отдельно подготовлена логарифмическая копия; численная пара в памяти сохранена.

Получить bool и два настоящих кадра, а не массив из returnПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/fourier-fixture.php';
fourierRestore();
$base=fourierBase(); $signal=fourierStriped($base);
$before=fourierHash($signal);$work=clone $signal;
$ok=$work->forwardFourierTransformImage(true);
$work->setIteratorIndex(0);$magnitude=$work->getImage();
$work->setIteratorIndex(1);$phase=$work->getImage();
fourierForwardProof('fourier-transform-forward-minimal',$work,$ok,$magnitude,$phase,$signal,$before);
echo "fourier-forward.png\nforward-magnitude-raw8.png\nforward-phase8.png\nforward-log8.png\nmetrics.json\n";
printf("forward=bool:%s images=%d first=%dx%d second=%dx%d\n",$ok?'true':'false',$work->getNumberImages(),
    $magnitude->getImageWidth(),$magnitude->getImageHeight(),$phase->getImageWidth(),$phase->getImageHeight());

Метод вернул true, а объект $work теперь содержит два изображения 400 × 400. Индекс 0 даёт magnitude, индекс 1 — phase. Для видимого спектра отдельно подготовлена логарифмическая копия; численная пара в памяти сохранена. Архив содержит example.php, source.png.b64 и fourier-fixture.php. Сохраните рядом и запустите php example.php с Imagick, реально работающим FFTW, поддержкой PNG и правом записи. Проверенная среда: Ubuntu 24.04 / WSL 2, PHP 8.3.6, Imagick 3.7.0, ImageMagick 6.9.12-98 Q16 без HDRI. В другой сборке сначала проверьте поддержку FFTW; наличие имени метода этого не доказывает. Полный helper приведён в конце главы: он восстанавливает собственный PNG, готовит непрозрачный сигнал с полосами, сохраняет просмотры и измерения. Только inverse-minimal использует явно объявленную подготовку настоящей пары через forward(true); численные компоненты не читаются из PNG8.

Ожидаемый вывод в указанной сборке
fourier-forward.png
forward-magnitude-raw8.png
forward-phase8.png
forward-log8.png
metrics.json
forward=bool:true images=2 first=400x400 second=400x400
$restored = clone $magnitude;
$ok = $restored->inverseFourierTransformImage($phase, true);
$restored->cropImage(400, 320, 0, 0);
$restored->setImagePage(0, 0, 0, 0);

clone сохраняет первый компонент отдельно. inverse изменяет $restored, а не $phase. Аргументы разделены запятой: первым передаётся соответствующий второй объект, вторым — true, поскольку пара подготовлена через forward(true). cropImage получает ширину, высоту и начало 0, 0. Это обратное преобразование неизменённой пары; оно возвращает прежний сигнал вместе с его полосами.

Чтобы сосредоточить короткий самостоятельный inverse на его собственных аргументах, fourierPreparedPair в fixture действительно выполняет forward(true), выбирает кадры 0/1 и возвращает два численных объекта плюс диагностические данные. Его полный исполняемый код приведён в последнем разделе. Это реальная подготовка, а не заранее нарисованные картинки или чтение PNG8-просмотров. Изучаемый inverse остаётся непосредственно в программе.

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

Учебная подготовка честно выполняет forward(true) в памяти и передаёт настоящие magnitude и phase. inverse(..., true) восстанавливает 400 × 400; явный crop возвращает исходные 400 × 320. Сохранённые PNG8 компонентов — только просмотры и не загружаются в inverse.

Передать правильный второй компонент и вернуть исходный размерПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/fourier-fixture.php';
fourierRestore();
$base=fourierBase(); $signal=fourierStriped($base);
[$magnitude,$phase,$prepared]=fourierPreparedPair($signal);
$restored=clone $magnitude;
$ok=$restored->inverseFourierTransformImage($phase,true);
$paddedSize=[$restored->getImageWidth(),$restored->getImageHeight()];
$fullError=fourierError(fourierPadded($signal),$restored);
$restored->cropImage(400,320,0,0);$restored->setImagePage(0,0,0,0);
fourierInverseProof('fourier-transform-inverse-minimal',$signal,$magnitude,$phase,$restored,$ok,$prepared,$paddedSize,$fullError);
$error=fourierError($signal,$restored);
echo "fourier-inverse.png\ninverse-magnitude-raw8.png\ninverse-phase8.png\nmetrics.json\n";
printf("forward=bool:%s inverse=bool:%s padded=%dx%d crop=400x320 max=%d RMSE=%.6f\n",
    $prepared['forward_return']?'true':'false',$ok?'true':'false',$paddedSize[0],$paddedSize[1],$error['RGB8_max_abs'],$error['RGB8_RMSE']);

Учебная подготовка честно выполняет forward(true) в памяти и передаёт настоящие magnitude и phase. inverse(..., true) восстанавливает 400 × 400; явный crop возвращает исходные 400 × 320. Сохранённые PNG8 компонентов — только просмотры и не загружаются в inverse. Архив содержит example.php, source.png.b64 и fourier-fixture.php. Сохраните рядом и запустите php example.php с Imagick, реально работающим FFTW, поддержкой PNG и правом записи. Проверенная среда: Ubuntu 24.04 / WSL 2, PHP 8.3.6, Imagick 3.7.0, ImageMagick 6.9.12-98 Q16 без HDRI. В другой сборке сначала проверьте поддержку FFTW; наличие имени метода этого не доказывает. Полный helper приведён в конце главы: он восстанавливает собственный PNG, готовит непрозрачный сигнал с полосами, сохраняет просмотры и измерения. Только inverse-minimal использует явно объявленную подготовку настоящей пары через forward(true); численные компоненты не читаются из PNG8.

Ожидаемый вывод в указанной сборке
fourier-inverse.png
inverse-magnitude-raw8.png
inverse-phase8.png
metrics.json
forward=bool:true inverse=bool:true padded=400x400 crop=400x320 max=2 RMSE=0.459784

Измерить качество, обработать ошибки и учитывать FFTW/HDRI

Одинаковый сигнал и два реальных восстановления: true почти сохраняет его, false заметно теряет данные в проверенной non-HDRI Q16-сборке
Одинаковый сигнал и два реальных восстановления: true почти сохраняет его, false заметно теряет данные в проверенной non-HDRI Q16-сборке

В обработчике изображений недостаточно проверить bool и назвать работу законченной. Сохраним вход, сделаем оба представления и измерим RGB8-ошибку. В каждом случае передаётся согласованный флаг: true→true и false→false. Поэтому разница результатов здесь не объясняется случайно перепутанным параметром; она показывает ограничение хранения этой конкретной non-HDRI Q16-сборки.

Сравнить измеренные true/false и отличить успех вызова от качества

Оба прямых и оба обратных вызова вернули true. Однако в этой non-HDRI Q16-сборке real/imaginary даёт заметную потерю: max=116 и RMSE≈40.739962 в исходной области. Проверяется качество реальных пикселей, сохраняется исходник и перехватываются два настоящих ошибочных входа.

Сравнить измеренные true/false и отличить успех вызова от качестваПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/fourier-fixture.php';
fourierRestore();
$base=fourierBase(); $signal=fourierStriped($base);
$baseBefore=fourierHash($base);$before=fourierHash($signal);$cases=[];$results=[];
foreach([true,false] as $representation){
    $name=$representation?'true':'false';$work=clone $signal;
    $forward=$work->forwardFourierTransformImage($representation);
    $index=$work->getIteratorIndex();$count=$work->getNumberImages();
    $work->setIteratorIndex(0);$first=$work->getImage();$work->setIteratorIndex(1);$second=$work->getImage();
    $a=fourierHash($first);$b=fourierHash($second);$result=clone $first;
    $inverse=$result->inverseFourierTransformImage($second,$representation);
    $paddedSize=[$result->getImageWidth(),$result->getImageHeight()];$fullError=fourierError(fourierPadded($signal),$result);
    $result->cropImage(400,320,0,0);$result->setImagePage(0,0,0,0);
    fourierView($result,'workflow-'.$name.'-restored8.png');$results[]=$result;
    $cases[$name]=['representation'=>$representation,'forward_return'=>$forward,'inverse_return'=>$inverse,'pair_count'=>$count,'index_after_forward'=>$index,
        'pair_sizes'=>[[$first->getImageWidth(),$first->getImageHeight()],[$second->getImageWidth(),$second->getImageHeight()]],
        'inverse_padded_size'=>$paddedSize,'crop_size'=>[$result->getImageWidth(),$result->getImageHeight()],
        'first_double_range'=>fourierRange($first),'second_double_range'=>fourierRange($second),'full_padded_error'=>$fullError,
        'error_vs_striped'=>fourierError($signal,$result),'pair_unchanged'=>$a===fourierHash($first)&&$b===fourierHash($second)];
}
$errors=[];
try{(new Imagick())->forwardFourierTransformImage(true);$errors['empty_forward']='not caught';}
catch(ImagickException $error){$errors['empty_forward']=get_class($error);}
try{$attempt=clone $signal;$attempt->inverseFourierTransformImage(new Imagick(),true);$errors['empty_complement']='not caught';}
catch(ImagickException $error){$errors['empty_complement']=get_class($error);}
fourierView($signal,'workflow-signal8.png');$rects=fourierCompare([$signal,...$results],'fourier-workflow.png');
$unchanged=fourierHash($base)===$baseBefore&&fourierHash($signal)===$before;
fourierDiagnostic('fourier-transform-workflow',['executed_apis'=>['forwardFourierTransformImage','inverseFourierTransformImage'],
    'PHP'=>PHP_VERSION,'Imagick'=>phpversion('imagick'),'ImageMagick'=>Imagick::getVersion(),'HDRI_enabled'=>Imagick::getHdriEnabled(),
    'delegates'=>Imagick::getConfigureOptions('DELEGATES'),'virtual_pixel_method'=>$signal->getImageVirtualPixelMethod(),
    'cases'=>$cases,'errors'=>$errors,'source_unchanged'=>$unchanged,'panel_geometry'=>$rects,
    'checks'=>['four_successful_returns'=>$cases['true']['forward_return']===true&&$cases['true']['inverse_return']===true&&$cases['false']['forward_return']===true&&$cases['false']['inverse_return']===true,
        'two_pairs'=>$cases['true']['pair_count']===2&&$cases['false']['pair_count']===2,
        'true_small_error'=>$cases['true']['error_vs_striped']['RGB8_max_abs']<=2,
        'false_loss_visible'=>$cases['false']['error_vs_striped']['RGB8_max_abs']>$cases['true']['error_vs_striped']['RGB8_max_abs'],
        'pairs_unchanged'=>$cases['true']['pair_unchanged']&&$cases['false']['pair_unchanged'],
        'two_errors_caught'=>array_values($errors)===['ImagickException','ImagickException'],'source_unchanged'=>$unchanged]]);
echo "fourier-workflow.png\nworkflow-signal8.png\nworkflow-true-restored8.png\nworkflow-false-restored8.png\nmetrics.json\n";
printf("true max=%d RMSE=%.6f; false max=%d RMSE=%.6f; errors=%d source-unchanged=%d\n",
    $cases['true']['error_vs_striped']['RGB8_max_abs'],$cases['true']['error_vs_striped']['RGB8_RMSE'],
    $cases['false']['error_vs_striped']['RGB8_max_abs'],$cases['false']['error_vs_striped']['RGB8_RMSE'],count($errors),(int)$unchanged);

Оба прямых и оба обратных вызова вернули true. Однако в этой non-HDRI Q16-сборке real/imaginary даёт заметную потерю: max=116 и RMSE≈40.739962 в исходной области. Проверяется качество реальных пикселей, сохраняется исходник и перехватываются два настоящих ошибочных входа. Архив содержит example.php, source.png.b64 и fourier-fixture.php. Сохраните рядом и запустите php example.php с Imagick, реально работающим FFTW, поддержкой PNG и правом записи. Проверенная среда: Ubuntu 24.04 / WSL 2, PHP 8.3.6, Imagick 3.7.0, ImageMagick 6.9.12-98 Q16 без HDRI. В другой сборке сначала проверьте поддержку FFTW; наличие имени метода этого не доказывает. Полный helper приведён в конце главы: он восстанавливает собственный PNG, готовит непрозрачный сигнал с полосами, сохраняет просмотры и измерения. Только inverse-minimal использует явно объявленную подготовку настоящей пары через forward(true); численные компоненты не читаются из PNG8.

Ожидаемый вывод в указанной сборке
fourier-workflow.png
workflow-signal8.png
workflow-true-restored8.png
workflow-false-restored8.png
metrics.json
true max=2 RMSE=0.459784; false max=116 RMSE=40.739962; errors=2 source-unchanged=1

Как читать числа результата: max и RMSE в stdout и metrics.json измеряются через exportImagePixels(..., PIXEL_CHAR) непосредственно после inverse и crop, до сохранения PNG8. Это проверка численных пикселей в памяти. Запись PNG8 выполняет отдельное округление. При независимом сравнении именно сохранённых PNG8 с сохранённым сигналом получены: true — max=3, RMSE≈0.737636; false — max=116, RMSE≈41.092654. Поэтому числа из stdout нельзя выдавать за измерение готового PNG-файла. Оба этапа относятся к тому же исходному сигналу и проверенной сборке; изображение спектра для просмотра по-прежнему не используется как численный вход inverse.

Для magnitude/phase в исходной области max=2 и RMSE≈0.459784 уровня RGB8; для real/imaginary max=116 и RMSE≈40.739962. На полном дополненном 400 × 400 кадре величины другие: true max=2/RMSE≈0.443112, false max=255/RMSE≈61.654485. Диагностика хранит оба измерения; crop не скрывает потерю в чёрном дополнении. Это измерения данного входа и runtime, а не универсальные допустимые пороги для любого изображения.

СитуацияЧто показывает рабочий сценарий
true вернулся, картинка невернаСмотрите реальный inverse и измеренную ошибку; успешный вызов не гарантирует сохранность signed-компонентов.
Пустой исходный объектforward выбрасывает ImagickException. Сначала нужен загруженный кадр.
Пустой complementinverse выбрасывает ImagickException: отсутствует второй компонент пары.
Метод есть, FFTW нетВ отдельно проверенной Windows-сборке именно так и произошло. Проверяйте реальную связанную библиотеку и её delegates, а не только method_exists.

try/catch перехватывает две намеренно вызванные ошибки пустых входов. В работающей WSL-сборке FFTW есть; программа не притворяется, что здесь должна возникнуть ошибка missing delegate. Отдельный сохранённый Windows-прогон доказывает этот случай в другой среде. При развёртывании сохраняйте понятную диагностику загрузки и преобразования; параметры установки зависят от вашей ОС и сборки.

Сохранить полный официальный базис и понять границы частотной обработки

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

Imagick::forwardFourierTransformimage(bool $magnitude): bool

Как читать числа результата: max и RMSE в stdout и metrics.json измеряются через exportImagePixels(..., PIXEL_CHAR) непосредственно после inverse и crop, до сохранения PNG8. Это проверка численных пикселей в памяти. Запись PNG8 выполняет отдельное округление. При независимом сравнении именно сохранённых PNG8 с сохранённым сигналом получены: true — max=3, RMSE≈0.737636; false — max=116, RMSE≈41.092654. Поэтому числа из stdout нельзя выдавать за измерение готового PNG-файла. Оба этапа относятся к тому же исходному сигналу и проверенной сборке; изображение спектра для просмотра по-прежнему не используется как численный вход inverse.

forwardFourierTransformImage выполняет дискретное преобразование Фурье: при magnitude=true представляет изображение парой magnitude/phase, при false — real/imaginary. Единственный обязательный параметр — логическое значение. Успешный return — true; сама пара появляется в изменённом объекте Imagick как два кадра. Поэтому $pair = $image->forwardFourierTransformImage(true) сохранит bool, а не массив компонентов.

Оригинальный официальный пример сохранён полностью ниже. Он загружает файл, изменяет размер до 512 × 512, создаёт белый круг на чёрной маске, размывает её и выполняет autoLevel. Затем получает кадры через @setimageindex(0/1), умножает именно phase на маску, а после inverse выводит PNG. Ветка if(false) с выводом phase и обе HTTP-ветки также сохранены. Это демонстрация изменения частотного представления, а не объяснённый универсальный фильтр удаления полос. Подавление предупреждений оператором @ не делает устаревший setImageIndex предпочтительным: в наших примерах используется setIteratorIndex без @.

Официальная доступность: PECL imagick 3 >= 3.3.0. Точные wrapper/stub Imagick 3.7.0 и 3.8.1 включают forward с ImageMagick 6.5.5, inverse с 6.5.8. Обёртка требует непустой текущий объект, вызывает библиотеку и превращает неуспех в ImagickException. Для inverse пустой complement тоже даёт реальную ошибку. Указанные условия наличия метода не гарантируют, что ImageMagick собран с FFTW.

Рабочие изображения и ошибки этого урока получены в PHP 8.3.6 / Imagick 3.7.0 / ImageMagick 6.9.12-98 Q16, FFTW, без HDRI. В отдельной Windows-сборке PHP 8.5.11 / Imagick 3.8.1 / IM 7.1.1-46 оба метода существовали, но forward выбрасывал ошибку отсутствующего FFTW. Её нельзя выдавать за успешную FFT-проверку. Не переносите точные числа ошибки и особенности хранения знаковых компонентов на другую глубину/HDRI/версию без собственного измерения.

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

<refentry xml:id="imagick.forwardfouriertransformimage" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::forwardFourierTransformImage</refname>
  <refpurpose>Implements the discrete Fourier transform (DFT)</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::forwardFourierTransformimage</methodname>
   <methodparam><type>bool</type><parameter>magnitude</parameter></methodparam>
  </methodsynopsis>
  <para>
   Implements the discrete Fourier transform (DFT) of the image either as a magnitude / phase or real / imaginary image pair.
  </para>

 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <variablelist>
   <varlistentry>
    <term><parameter>magnitude</parameter></term>
    <listitem>
     <para>
      If true, return as magnitude / phase pair otherwise a real / imaginary image pair.
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </refsect1>

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




 <refsect1 role="examples">
  &reftitle.examples;
  <para>
    <example>
      <title> <function>Imagick::forwardFourierTransformImage</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
//Utility function for forwardTransformImage
function createMask() {
    $draw = new \ImagickDraw();

    $draw->setStrokeOpacity(0);
    $draw->setStrokeColor('rgb(255, 255, 255)');
    $draw->setFillColor('rgb(255, 255, 255)');

    //Draw a circle on the y-axis, with its centre
    //at x, y that touches the origin
    $draw->circle(250, 250, 220, 250);

    $imagick = new \Imagick();
    $imagick->newImage(512, 512, "black");
    $imagick->drawImage($draw);
    $imagick->gaussianBlurImage(20, 20);
    $imagick->autoLevelImage();

    return $imagick;
}


function forwardFourierTransformImage($imagePath) {
    $imagick = new \Imagick(realpath($imagePath));
    $imagick->resizeimage(512, 512, \Imagick::FILTER_LANCZOS, 1);

    $mask = createMask();
    $imagick->forwardFourierTransformImage(true);

    @$imagick->setimageindex(0);
    $magnitude = $imagick->getimage();

    @$imagick->setimageindex(1);
    $imagickPhase = $imagick->getimage();

    if (true) {
        $imagickPhase->compositeImage($mask, \Imagick::COMPOSITE_MULTIPLY, 0, 0);
    }

    if (false) {
        $output = clone $imagickPhase;
        $output->setimageformat('png');
        header("Content-Type: image/png");
        echo $output->getImageBlob();
    }

    $magnitude->inverseFourierTransformImage($imagickPhase, true);

    $magnitude->setimageformat('png');
    header("Content-Type: image/png");
    echo $magnitude->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
-->

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

Imagick::inverseFourierTransformImage(Imagick $complement, bool $magnitude): bool

Как читать числа результата: max и RMSE в stdout и metrics.json измеряются через exportImagePixels(..., PIXEL_CHAR) непосредственно после inverse и crop, до сохранения PNG8. Это проверка численных пикселей в памяти. Запись PNG8 выполняет отдельное округление. При независимом сравнении именно сохранённых PNG8 с сохранённым сигналом получены: true — max=3, RMSE≈0.737636; false — max=116, RMSE≈41.092654. Поэтому числа из stdout нельзя выдавать за измерение готового PNG-файла. Оба этапа относятся к тому же исходному сигналу и проверенной сборке; изображение спектра для просмотра по-прежнему не используется как численный вход inverse.

inverseFourierTransformImage выполняет обратное DFT. Текущий объект должен содержать первый компонент, обязательный complement — соответствующий второй. При magnitude=true это magnitude и phase; при false — real и imaginary. Флаг обязан соответствовать представлению исходной пары. Успешный return — true, а текущий объект изменяется на результат inverse. Возвращаемое значение не является новым восстановленным Imagick.

АргументЧто передать
$complement: ImagickВторой численный компонент именно той пары, к которой относится текущий первый компонент. В нашем true-примере это phase из того же forward(true).
$magnitude: booltrue для magnitude/phase, false для real/imaginary. Это выбор представления, а не регулировка силы эффекта и не необязательный флаг.

Официальный XML содержит предупреждение о неполноте документации функции; его описание, оба параметра и return сохранены без удаления. Исполняемого официального примера inverse на этой странице нет; законченные собственные программы дополняют базис отдельно.

Официальная доступность: PECL imagick 3 >= 3.3.0. Точные wrapper/stub Imagick 3.7.0 и 3.8.1 включают forward с ImageMagick 6.5.5, inverse с 6.5.8. Обёртка требует непустой текущий объект, вызывает библиотеку и превращает неуспех в ImagickException. Для inverse пустой complement тоже даёт реальную ошибку. Указанные условия наличия метода не гарантируют, что ImageMagick собран с FFTW.

Рабочие изображения и ошибки этого урока получены в PHP 8.3.6 / Imagick 3.7.0 / ImageMagick 6.9.12-98 Q16, FFTW, без HDRI. В отдельной Windows-сборке PHP 8.5.11 / Imagick 3.8.1 / IM 7.1.1-46 оба метода существовали, но forward выбрасывал ошибку отсутствующего FFTW. Её нельзя выдавать за успешную FFT-проверку. Не переносите точные числа ошибки и особенности хранения знаковых компонентов на другую глубину/HDRI/версию без собственного измерения.

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

<refentry xml:id="imagick.inversefouriertransformimage" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::inverseFourierTransformImage</refname>
  <refpurpose>Implements the inverse discrete Fourier transform (DFT)</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::inverseFourierTransformImage</methodname>
   <methodparam><type>Imagick</type><parameter>complement</parameter></methodparam>
   <methodparam><type>bool</type><parameter>magnitude</parameter></methodparam>
  </methodsynopsis>
  <para>
   Implements the inverse discrete Fourier transform (DFT) of the image either as a magnitude / phase or real / imaginary image pair.
  </para>

  &warn.undocumented.func;

 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <variablelist>
   <varlistentry>
    <term><parameter>complement</parameter></term>
    <listitem>
     <para>
      The second image to combine with this one to form either the magnitude / phase or real / imaginary image pair.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term><parameter>magnitude</parameter></term>
    <listitem>
     <para>
      If true, combine as magnitude / phase pair otherwise a real / imaginary image pair.
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   &imagick.return.success;
  </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
-->

Как запустить самостоятельные программы в Ubuntu 24.04

Для этой ОС прямой путь — официальные пакеты PHP CLI и Imagick. В терминале Ubuntu выполните следующую команду установки, затем распакуйте один архив примера и запустите php example.php в его каталоге. Это инструкция настройки среды, она не является PHP-примером обработки изображения.

sudo apt install php8.3-cli php8.3-imagick
php example.php

Пакет php8.3-imagick для Ubuntu 24.04 описан в официальном каталоге Ubuntu; ссылка приведена в источниках. Установка пакета и наличие метода ещё не подтверждают фактическую FFTW-поддержку любой сборки: минимальный forward должен реально вернуть true и два кадра. Все числа в уроке относятся к измеренной среде PHP 8.3.6 / Imagick 3.7.0 / IM 6.9.12-98, а не к обещанию самой новой версии из репозитория. На другой ОС используйте совместимую сборку с настоящим FFTW и проверьте результат этого же примера.

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

Полный исполняемый fixture: подготовка, численная пара inverse-примера и просмотры

<?php
declare(strict_types=1);
function fourierRestore():void {
    $raw=base64_decode((string)file_get_contents(__DIR__.'/source.png.b64'),true);
    if($raw===false||hash('sha256',$raw)!=='f51e5780445e0f5130a7f23112b2a00618035afd5545b8e3042c7ccff09b617c'||file_put_contents(__DIR__.'/source.png',$raw)===false){throw new RuntimeException('Exact own PNG missing or changed');}
}
function fourierHash(Imagick $image):string {
    return hash('sha256',json_encode($image->exportImagePixels(0,0,$image->getImageWidth(),$image->getImageHeight(),'RGB',Imagick::PIXEL_DOUBLE),JSON_THROW_ON_ERROR));
}
function fourierRange(Imagick $image):array {
    $values=$image->exportImagePixels(0,0,$image->getImageWidth(),$image->getImageHeight(),'RGB',Imagick::PIXEL_DOUBLE);
    return ['min'=>min($values),'max'=>max($values),'components'=>count($values),'double_sha256'=>hash('sha256',json_encode($values,JSON_THROW_ON_ERROR))];
}
function fourierBase():Imagick {
    $source=new Imagick(__DIR__.'/source.png');$base=new Imagick();$base->newImage(400,320,'white');
    $base->compositeImage($source,Imagick::COMPOSITE_OVER,0,0);$base->setImagePage(0,0,0,0);
    $base->setImageAlphaChannel(Imagick::ALPHACHANNEL_DEACTIVATE);
    return $base;
}
function fourierStriped(Imagick $base):Imagick {
    $signal=clone $base;$pixels=$signal->exportImagePixels(0,0,400,320,'RGB',Imagick::PIXEL_CHAR);
    for($y=0;$y<320;$y++)for($x=0;$x<400;$x++){
        $band=(int)round(18*cos(2*M_PI*$x/16));
        for($c=0;$c<3;$c++){$i=($y*400+$x)*3+$c;$pixels[$i]=max(0,min(255,$pixels[$i]+$band));}
    }
    $signal->importImagePixels(0,0,400,320,'RGB',Imagick::PIXEL_CHAR,$pixels);
    $signal->setImageVirtualPixelMethod(Imagick::VIRTUALPIXELMETHOD_BLACK);
    return $signal;
}
function fourierPadded(Imagick $signal):Imagick {
    $expected=new Imagick();$expected->newImage(400,400,'black');
    $expected->compositeImage($signal,Imagick::COMPOSITE_OVER,0,0);
    $expected->setImageAlphaChannel(Imagick::ALPHACHANNEL_DEACTIVATE);return $expected;
}
function fourierError(Imagick $expected,Imagick $actual):array {
    $w=$expected->getImageWidth();$h=$expected->getImageHeight();
    if($actual->getImageWidth()!==$w||$actual->getImageHeight()!==$h){throw new RuntimeException('Compare the same geometry');}
    $a=$expected->exportImagePixels(0,0,$w,$h,'RGB',Imagick::PIXEL_CHAR);
    $b=$actual->exportImagePixels(0,0,$w,$h,'RGB',Imagick::PIXEL_CHAR);
    $maximum=0;$sum=0.0;$different=0;
    foreach($a as $i=>$value){$delta=abs($value-$b[$i]);$maximum=max($maximum,$delta);$sum+=$delta*$delta;$different+=(int)($delta!==0);}
    return ['RGB8_max_abs'=>$maximum,'RGB8_RMSE'=>sqrt($sum/count($a)),'RGB8_changed_components'=>$different,'RGB8_components'=>count($a)];
}
function fourierView(Imagick $image,string $filename):void {
    $copy=clone $image;$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__.'/'.$filename)){throw new RuntimeException('PNG8 display write failed');}
}
function fourierLog(Imagick $magnitude):Imagick {
    $display=clone $magnitude;$display->evaluateImage(Imagick::EVALUATE_LOG,10000);$display->autoLevelImage();return $display;
}
function fourierCompare(array $images,string $filename):array {
    $height=max(array_map(static fn(Imagick $image):int=>$image->getImageHeight(),$images));
    $board=new Imagick();$board->newImage(400*count($images),$height,'white');$rects=[];
    foreach($images as $i=>$image){
        if($image->getImageWidth()!==400||!in_array($image->getImageHeight(),[320,400],true)){throw new RuntimeException('Expected a complete natural-width view');}
        $board->compositeImage($image,Imagick::COMPOSITE_OVER,400*$i,0);
        $rects[]=['x'=>400*$i,'y'=>0,'width'=>400,'height'=>$image->getImageHeight()];
    }
    fourierView($board,$filename);return $rects;
}
function fourierDiagnostic(string $id,array $data):void {
    $doc=['schema'=>'aen-fourier-transform-program-diagnostics-v1','example_id'=>$id]+$data;
    if(file_put_contents(__DIR__.'/metrics.json',json_encode($doc,JSON_PRETTY_PRINT|JSON_PRESERVE_ZERO_FRACTION|JSON_THROW_ON_ERROR)."\n")===false){throw new RuntimeException('Diagnostic write failed');}
}
// This declared inverse-example fixture REALLY computes the numerical pair.
// It never reads back a PNG8/log preview. Other programs show forward explicitly.
function fourierPreparedPair(Imagick $signal):array {
    $inputBefore=fourierHash($signal);$work=clone $signal;$ok=$work->forwardFourierTransformImage(true);
    $index=$work->getIteratorIndex();$count=$work->getNumberImages();
    $work->setIteratorIndex(0);$magnitude=$work->getImage();$work->setIteratorIndex(1);$phase=$work->getImage();
    return [$magnitude,$phase,['forward_return'=>$ok,'pair_count'=>$count,'index_after_forward'=>$index,
        'magnitude_double_sha256'=>fourierHash($magnitude),'phase_double_sha256'=>fourierHash($phase),
        'input_unchanged'=>$inputBefore===fourierHash($signal)]];
}
// Compact diagnostic/output helpers do NOT perform the studied transforms.
function fourierForwardProof(string $id,Imagick $work,bool $ok,Imagick $magnitude,Imagick $phase,Imagick $signal,string $before):void {
    $a=fourierHash($magnitude);$b=fourierHash($phase);$log=fourierLog($magnitude);
    fourierView($magnitude,'forward-magnitude-raw8.png');fourierView($phase,'forward-phase8.png');fourierView($log,'forward-log8.png');
    $rects=fourierCompare([$log,$phase],'fourier-forward.png');
    $first=[$magnitude->getImageWidth(),$magnitude->getImageHeight()];$second=[$phase->getImageWidth(),$phase->getImageHeight()];
    fourierDiagnostic($id,['executed_api'=>'forwardFourierTransformImage','representation'=>true,'forward_return'=>$ok,'pair_count'=>$work->getNumberImages(),
        'pair_sizes'=>[$first,$second],'virtual_pixel_method'=>$signal->getImageVirtualPixelMethod(),
        'first_double_range'=>fourierRange($magnitude),'second_double_range'=>fourierRange($phase),'panel_geometry'=>$rects,
        'checks'=>['bool_true'=>$ok===true,'two_images'=>$work->getNumberImages()===2,'pair_geometry'=>$first===[400,400]&&$second===[400,400],
            'source_unchanged'=>fourierHash($signal)===$before,'numerical_pair_unchanged'=>$a===fourierHash($magnitude)&&$b===fourierHash($phase)]]);
}
function fourierInverseProof(string $id,Imagick $signal,Imagick $magnitude,Imagick $phase,Imagick $restored,bool $ok,array $prepared,array $paddedSize,array $fullError):void {
    fourierView($magnitude,'inverse-magnitude-raw8.png');fourierView($phase,'inverse-phase8.png');fourierView($restored,'fourier-inverse.png');
    $error=fourierError($signal,$restored);$samePair=$prepared['magnitude_double_sha256']===fourierHash($magnitude)&&$prepared['phase_double_sha256']===fourierHash($phase);
    fourierDiagnostic($id,['executed_api'=>'inverseFourierTransformImage','representation'=>true,'inverse_return'=>$ok,'prepared_pair'=>$prepared,
        'inverse_padded_size'=>$paddedSize,'crop_size'=>[$restored->getImageWidth(),$restored->getImageHeight()],
        'full_padded_error'=>$fullError,'error_vs_striped'=>$error,'pair_unchanged'=>$samePair,
        'checks'=>['inverse_true'=>$ok===true,'real_forward_precondition'=>$prepared['forward_return']===true&&$prepared['pair_count']===2,
            'padded_geometry'=>$paddedSize===[400,400],'cropped_geometry'=>$restored->getImageWidth()===400&&$restored->getImageHeight()===320,
            'small_measured_RGB8_error'=>$error['RGB8_max_abs']<=2,'pair_unchanged'=>$samePair,'source_unchanged'=>$prepared['input_unchanged']]]);
}

fourierView и fourierLog создают копии. fourierForwardProof/fourierInverseProof только сохраняют результаты и диагностику; сами изучаемые преобразования они не вызывают. Исключение подготовки честно обозначено отдельно: fourierPreparedPair вычисляет настоящий forward(true) только для короткого inverse-примера. В веб-ответе PNG передают отдельно с Content-Type: image/png, текстовую диагностику с байтами изображения не смешивают. Далее: геометрия и виртуальные пиксели, цветовая модель, клонирование и исключения.

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

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

Imagick::forwardFourierTransformImageДва частотных компонента, правильный inverse и измеренная погрешность Q16

PECL imagick 3 >= 3.3.0 · imagick

Imagick::forwardFourierTransformimage(bool $magnitude): bool

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

Imagick::forwardFourierTransformimage(bool $magnitude): bool

Как читать числа результата: max и RMSE в stdout и metrics.json измеряются через exportImagePixels(..., PIXEL_CHAR) непосредственно после inverse и crop, до сохранения PNG8. Это проверка численных пикселей в памяти. Запись PNG8 выполняет отдельное округление. При независимом сравнении именно сохранённых PNG8 с сохранённым сигналом получены: true — max=3, RMSE≈0.737636; false — max=116, RMSE≈41.092654. Поэтому числа из stdout нельзя выдавать за измерение готового PNG-файла. Оба этапа относятся к тому же исходному сигналу и проверенной сборке; изображение спектра для просмотра по-прежнему не используется как численный вход inverse.

forwardFourierTransformImage выполняет дискретное преобразование Фурье: при magnitude=true представляет изображение парой magnitude/phase, при false — real/imaginary. Единственный обязательный параметр — логическое значение. Успешный return — true; сама пара появляется в изменённом объекте Imagick как два кадра. Поэтому $pair = $image->forwardFourierTransformImage(true) сохранит bool, а не массив компонентов.

Оригинальный официальный пример сохранён полностью ниже. Он загружает файл, изменяет размер до 512 × 512, создаёт белый круг на чёрной маске, размывает её и выполняет autoLevel. Затем получает кадры через @setimageindex(0/1), умножает именно phase на маску, а после inverse выводит PNG. Ветка if(false) с выводом phase и обе HTTP-ветки также сохранены. Это демонстрация изменения частотного представления, а не объяснённый универсальный фильтр удаления полос. Подавление предупреждений оператором @ не делает устаревший setImageIndex предпочтительным: в наших примерах используется setIteratorIndex без @.

Официальная доступность: PECL imagick 3 >= 3.3.0. Точные wrapper/stub Imagick 3.7.0 и 3.8.1 включают forward с ImageMagick 6.5.5, inverse с 6.5.8. Обёртка требует непустой текущий объект, вызывает библиотеку и превращает неуспех в ImagickException. Для inverse пустой complement тоже даёт реальную ошибку. Указанные условия наличия метода не гарантируют, что ImageMagick собран с FFTW.

Рабочие изображения и ошибки этого урока получены в PHP 8.3.6 / Imagick 3.7.0 / ImageMagick 6.9.12-98 Q16, FFTW, без HDRI. В отдельной Windows-сборке PHP 8.5.11 / Imagick 3.8.1 / IM 7.1.1-46 оба метода существовали, но forward выбрасывал ошибку отсутствующего FFTW. Её нельзя выдавать за успешную FFT-проверку. Не переносите точные числа ошибки и особенности хранения знаковых компонентов на другую глубину/HDRI/версию без собственного измерения.

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

<refentry xml:id="imagick.forwardfouriertransformimage" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::forwardFourierTransformImage</refname>
  <refpurpose>Implements the discrete Fourier transform (DFT)</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::forwardFourierTransformimage</methodname>
   <methodparam><type>bool</type><parameter>magnitude</parameter></methodparam>
  </methodsynopsis>
  <para>
   Implements the discrete Fourier transform (DFT) of the image either as a magnitude / phase or real / imaginary image pair.
  </para>

 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <variablelist>
   <varlistentry>
    <term><parameter>magnitude</parameter></term>
    <listitem>
     <para>
      If true, return as magnitude / phase pair otherwise a real / imaginary image pair.
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </refsect1>

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




 <refsect1 role="examples">
  &reftitle.examples;
  <para>
    <example>
      <title> <function>Imagick::forwardFourierTransformImage</function></title>
      <programlisting role="php">
      <![CDATA[
<?php
//Utility function for forwardTransformImage
function createMask() {
    $draw = new \ImagickDraw();

    $draw->setStrokeOpacity(0);
    $draw->setStrokeColor('rgb(255, 255, 255)');
    $draw->setFillColor('rgb(255, 255, 255)');

    //Draw a circle on the y-axis, with its centre
    //at x, y that touches the origin
    $draw->circle(250, 250, 220, 250);

    $imagick = new \Imagick();
    $imagick->newImage(512, 512, "black");
    $imagick->drawImage($draw);
    $imagick->gaussianBlurImage(20, 20);
    $imagick->autoLevelImage();

    return $imagick;
}


function forwardFourierTransformImage($imagePath) {
    $imagick = new \Imagick(realpath($imagePath));
    $imagick->resizeimage(512, 512, \Imagick::FILTER_LANCZOS, 1);

    $mask = createMask();
    $imagick->forwardFourierTransformImage(true);

    @$imagick->setimageindex(0);
    $magnitude = $imagick->getimage();

    @$imagick->setimageindex(1);
    $imagickPhase = $imagick->getimage();

    if (true) {
        $imagickPhase->compositeImage($mask, \Imagick::COMPOSITE_MULTIPLY, 0, 0);
    }

    if (false) {
        $output = clone $imagickPhase;
        $output->setimageformat('png');
        header("Content-Type: image/png");
        echo $output->getImageBlob();
    }

    $magnitude->inverseFourierTransformImage($imagickPhase, true);

    $magnitude->setimageformat('png');
    header("Content-Type: image/png");
    echo $magnitude->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
-->

Получить bool и два настоящих кадра, а не массив из return

Метод вернул true, а объект $work теперь содержит два изображения 400 × 400. Индекс 0 даёт magnitude, индекс 1 — phase. Для видимого спектра отдельно подготовлена логарифмическая копия; численная пара в памяти сохранена.

Получить bool и два настоящих кадра, а не массив из returnПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/fourier-fixture.php';
fourierRestore();
$base=fourierBase(); $signal=fourierStriped($base);
$before=fourierHash($signal);$work=clone $signal;
$ok=$work->forwardFourierTransformImage(true);
$work->setIteratorIndex(0);$magnitude=$work->getImage();
$work->setIteratorIndex(1);$phase=$work->getImage();
fourierForwardProof('fourier-transform-forward-minimal',$work,$ok,$magnitude,$phase,$signal,$before);
echo "fourier-forward.png\nforward-magnitude-raw8.png\nforward-phase8.png\nforward-log8.png\nmetrics.json\n";
printf("forward=bool:%s images=%d first=%dx%d second=%dx%d\n",$ok?'true':'false',$work->getNumberImages(),
    $magnitude->getImageWidth(),$magnitude->getImageHeight(),$phase->getImageWidth(),$phase->getImageHeight());

Метод вернул true, а объект $work теперь содержит два изображения 400 × 400. Индекс 0 даёт magnitude, индекс 1 — phase. Для видимого спектра отдельно подготовлена логарифмическая копия; численная пара в памяти сохранена. Архив содержит example.php, source.png.b64 и fourier-fixture.php. Сохраните рядом и запустите php example.php с Imagick, реально работающим FFTW, поддержкой PNG и правом записи. Проверенная среда: Ubuntu 24.04 / WSL 2, PHP 8.3.6, Imagick 3.7.0, ImageMagick 6.9.12-98 Q16 без HDRI. В другой сборке сначала проверьте поддержку FFTW; наличие имени метода этого не доказывает. Полный helper приведён в конце главы: он восстанавливает собственный PNG, готовит непрозрачный сигнал с полосами, сохраняет просмотры и измерения. Только inverse-minimal использует явно объявленную подготовку настоящей пары через forward(true); численные компоненты не читаются из PNG8.

Ожидаемый вывод в указанной сборке
fourier-forward.png
forward-magnitude-raw8.png
forward-phase8.png
forward-log8.png
metrics.json
forward=bool:true images=2 first=400x400 second=400x400

Сравнить измеренные true/false и отличить успех вызова от качества

Оба прямых и оба обратных вызова вернули true. Однако в этой non-HDRI Q16-сборке real/imaginary даёт заметную потерю: max=116 и RMSE≈40.739962 в исходной области. Проверяется качество реальных пикселей, сохраняется исходник и перехватываются два настоящих ошибочных входа.

Сравнить измеренные true/false и отличить успех вызова от качестваПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/fourier-fixture.php';
fourierRestore();
$base=fourierBase(); $signal=fourierStriped($base);
$baseBefore=fourierHash($base);$before=fourierHash($signal);$cases=[];$results=[];
foreach([true,false] as $representation){
    $name=$representation?'true':'false';$work=clone $signal;
    $forward=$work->forwardFourierTransformImage($representation);
    $index=$work->getIteratorIndex();$count=$work->getNumberImages();
    $work->setIteratorIndex(0);$first=$work->getImage();$work->setIteratorIndex(1);$second=$work->getImage();
    $a=fourierHash($first);$b=fourierHash($second);$result=clone $first;
    $inverse=$result->inverseFourierTransformImage($second,$representation);
    $paddedSize=[$result->getImageWidth(),$result->getImageHeight()];$fullError=fourierError(fourierPadded($signal),$result);
    $result->cropImage(400,320,0,0);$result->setImagePage(0,0,0,0);
    fourierView($result,'workflow-'.$name.'-restored8.png');$results[]=$result;
    $cases[$name]=['representation'=>$representation,'forward_return'=>$forward,'inverse_return'=>$inverse,'pair_count'=>$count,'index_after_forward'=>$index,
        'pair_sizes'=>[[$first->getImageWidth(),$first->getImageHeight()],[$second->getImageWidth(),$second->getImageHeight()]],
        'inverse_padded_size'=>$paddedSize,'crop_size'=>[$result->getImageWidth(),$result->getImageHeight()],
        'first_double_range'=>fourierRange($first),'second_double_range'=>fourierRange($second),'full_padded_error'=>$fullError,
        'error_vs_striped'=>fourierError($signal,$result),'pair_unchanged'=>$a===fourierHash($first)&&$b===fourierHash($second)];
}
$errors=[];
try{(new Imagick())->forwardFourierTransformImage(true);$errors['empty_forward']='not caught';}
catch(ImagickException $error){$errors['empty_forward']=get_class($error);}
try{$attempt=clone $signal;$attempt->inverseFourierTransformImage(new Imagick(),true);$errors['empty_complement']='not caught';}
catch(ImagickException $error){$errors['empty_complement']=get_class($error);}
fourierView($signal,'workflow-signal8.png');$rects=fourierCompare([$signal,...$results],'fourier-workflow.png');
$unchanged=fourierHash($base)===$baseBefore&&fourierHash($signal)===$before;
fourierDiagnostic('fourier-transform-workflow',['executed_apis'=>['forwardFourierTransformImage','inverseFourierTransformImage'],
    'PHP'=>PHP_VERSION,'Imagick'=>phpversion('imagick'),'ImageMagick'=>Imagick::getVersion(),'HDRI_enabled'=>Imagick::getHdriEnabled(),
    'delegates'=>Imagick::getConfigureOptions('DELEGATES'),'virtual_pixel_method'=>$signal->getImageVirtualPixelMethod(),
    'cases'=>$cases,'errors'=>$errors,'source_unchanged'=>$unchanged,'panel_geometry'=>$rects,
    'checks'=>['four_successful_returns'=>$cases['true']['forward_return']===true&&$cases['true']['inverse_return']===true&&$cases['false']['forward_return']===true&&$cases['false']['inverse_return']===true,
        'two_pairs'=>$cases['true']['pair_count']===2&&$cases['false']['pair_count']===2,
        'true_small_error'=>$cases['true']['error_vs_striped']['RGB8_max_abs']<=2,
        'false_loss_visible'=>$cases['false']['error_vs_striped']['RGB8_max_abs']>$cases['true']['error_vs_striped']['RGB8_max_abs'],
        'pairs_unchanged'=>$cases['true']['pair_unchanged']&&$cases['false']['pair_unchanged'],
        'two_errors_caught'=>array_values($errors)===['ImagickException','ImagickException'],'source_unchanged'=>$unchanged]]);
echo "fourier-workflow.png\nworkflow-signal8.png\nworkflow-true-restored8.png\nworkflow-false-restored8.png\nmetrics.json\n";
printf("true max=%d RMSE=%.6f; false max=%d RMSE=%.6f; errors=%d source-unchanged=%d\n",
    $cases['true']['error_vs_striped']['RGB8_max_abs'],$cases['true']['error_vs_striped']['RGB8_RMSE'],
    $cases['false']['error_vs_striped']['RGB8_max_abs'],$cases['false']['error_vs_striped']['RGB8_RMSE'],count($errors),(int)$unchanged);

Оба прямых и оба обратных вызова вернули true. Однако в этой non-HDRI Q16-сборке real/imaginary даёт заметную потерю: max=116 и RMSE≈40.739962 в исходной области. Проверяется качество реальных пикселей, сохраняется исходник и перехватываются два настоящих ошибочных входа. Архив содержит example.php, source.png.b64 и fourier-fixture.php. Сохраните рядом и запустите php example.php с Imagick, реально работающим FFTW, поддержкой PNG и правом записи. Проверенная среда: Ubuntu 24.04 / WSL 2, PHP 8.3.6, Imagick 3.7.0, ImageMagick 6.9.12-98 Q16 без HDRI. В другой сборке сначала проверьте поддержку FFTW; наличие имени метода этого не доказывает. Полный helper приведён в конце главы: он восстанавливает собственный PNG, готовит непрозрачный сигнал с полосами, сохраняет просмотры и измерения. Только inverse-minimal использует явно объявленную подготовку настоящей пары через forward(true); численные компоненты не читаются из PNG8.

Ожидаемый вывод в указанной сборке
fourier-workflow.png
workflow-signal8.png
workflow-true-restored8.png
workflow-false-restored8.png
metrics.json
true max=2 RMSE=0.459784; false max=116 RMSE=40.739962; errors=2 source-unchanged=1
Imagick::inverseFourierTransformImageДва частотных компонента, правильный inverse и измеренная погрешность Q16

PECL imagick 3 >= 3.3.0 · imagick

Imagick::inverseFourierTransformImage(Imagick $complement, bool $magnitude): bool

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

Imagick::inverseFourierTransformImage(Imagick $complement, bool $magnitude): bool

Как читать числа результата: max и RMSE в stdout и metrics.json измеряются через exportImagePixels(..., PIXEL_CHAR) непосредственно после inverse и crop, до сохранения PNG8. Это проверка численных пикселей в памяти. Запись PNG8 выполняет отдельное округление. При независимом сравнении именно сохранённых PNG8 с сохранённым сигналом получены: true — max=3, RMSE≈0.737636; false — max=116, RMSE≈41.092654. Поэтому числа из stdout нельзя выдавать за измерение готового PNG-файла. Оба этапа относятся к тому же исходному сигналу и проверенной сборке; изображение спектра для просмотра по-прежнему не используется как численный вход inverse.

inverseFourierTransformImage выполняет обратное DFT. Текущий объект должен содержать первый компонент, обязательный complement — соответствующий второй. При magnitude=true это magnitude и phase; при false — real и imaginary. Флаг обязан соответствовать представлению исходной пары. Успешный return — true, а текущий объект изменяется на результат inverse. Возвращаемое значение не является новым восстановленным Imagick.

АргументЧто передать
$complement: ImagickВторой численный компонент именно той пары, к которой относится текущий первый компонент. В нашем true-примере это phase из того же forward(true).
$magnitude: booltrue для magnitude/phase, false для real/imaginary. Это выбор представления, а не регулировка силы эффекта и не необязательный флаг.

Официальный XML содержит предупреждение о неполноте документации функции; его описание, оба параметра и return сохранены без удаления. Исполняемого официального примера inverse на этой странице нет; законченные собственные программы дополняют базис отдельно.

Официальная доступность: PECL imagick 3 >= 3.3.0. Точные wrapper/stub Imagick 3.7.0 и 3.8.1 включают forward с ImageMagick 6.5.5, inverse с 6.5.8. Обёртка требует непустой текущий объект, вызывает библиотеку и превращает неуспех в ImagickException. Для inverse пустой complement тоже даёт реальную ошибку. Указанные условия наличия метода не гарантируют, что ImageMagick собран с FFTW.

Рабочие изображения и ошибки этого урока получены в PHP 8.3.6 / Imagick 3.7.0 / ImageMagick 6.9.12-98 Q16, FFTW, без HDRI. В отдельной Windows-сборке PHP 8.5.11 / Imagick 3.8.1 / IM 7.1.1-46 оба метода существовали, но forward выбрасывал ошибку отсутствующего FFTW. Её нельзя выдавать за успешную FFT-проверку. Не переносите точные числа ошибки и особенности хранения знаковых компонентов на другую глубину/HDRI/версию без собственного измерения.

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

<refentry xml:id="imagick.inversefouriertransformimage" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
 <refnamediv>
  <refname>Imagick::inverseFourierTransformImage</refname>
  <refpurpose>Implements the inverse discrete Fourier transform (DFT)</refpurpose>
 </refnamediv>

 <refsect1 role="description">
  &reftitle.description;
  <methodsynopsis>
   <modifier>public</modifier> <type>bool</type><methodname>Imagick::inverseFourierTransformImage</methodname>
   <methodparam><type>Imagick</type><parameter>complement</parameter></methodparam>
   <methodparam><type>bool</type><parameter>magnitude</parameter></methodparam>
  </methodsynopsis>
  <para>
   Implements the inverse discrete Fourier transform (DFT) of the image either as a magnitude / phase or real / imaginary image pair.
  </para>

  &warn.undocumented.func;

 </refsect1>

 <refsect1 role="parameters">
  &reftitle.parameters;
  <variablelist>
   <varlistentry>
    <term><parameter>complement</parameter></term>
    <listitem>
     <para>
      The second image to combine with this one to form either the magnitude / phase or real / imaginary image pair.
     </para>
    </listitem>
   </varlistentry>
   <varlistentry>
    <term><parameter>magnitude</parameter></term>
    <listitem>
     <para>
      If true, combine as magnitude / phase pair otherwise a real / imaginary image pair.
     </para>
    </listitem>
   </varlistentry>
  </variablelist>
 </refsect1>

 <refsect1 role="returnvalues">
  &reftitle.returnvalues;
  <para>
   &imagick.return.success;
  </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
-->

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

Учебная подготовка честно выполняет forward(true) в памяти и передаёт настоящие magnitude и phase. inverse(..., true) восстанавливает 400 × 400; явный crop возвращает исходные 400 × 320. Сохранённые PNG8 компонентов — только просмотры и не загружаются в inverse.

Передать правильный второй компонент и вернуть исходный размерПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/fourier-fixture.php';
fourierRestore();
$base=fourierBase(); $signal=fourierStriped($base);
[$magnitude,$phase,$prepared]=fourierPreparedPair($signal);
$restored=clone $magnitude;
$ok=$restored->inverseFourierTransformImage($phase,true);
$paddedSize=[$restored->getImageWidth(),$restored->getImageHeight()];
$fullError=fourierError(fourierPadded($signal),$restored);
$restored->cropImage(400,320,0,0);$restored->setImagePage(0,0,0,0);
fourierInverseProof('fourier-transform-inverse-minimal',$signal,$magnitude,$phase,$restored,$ok,$prepared,$paddedSize,$fullError);
$error=fourierError($signal,$restored);
echo "fourier-inverse.png\ninverse-magnitude-raw8.png\ninverse-phase8.png\nmetrics.json\n";
printf("forward=bool:%s inverse=bool:%s padded=%dx%d crop=400x320 max=%d RMSE=%.6f\n",
    $prepared['forward_return']?'true':'false',$ok?'true':'false',$paddedSize[0],$paddedSize[1],$error['RGB8_max_abs'],$error['RGB8_RMSE']);

Учебная подготовка честно выполняет forward(true) в памяти и передаёт настоящие magnitude и phase. inverse(..., true) восстанавливает 400 × 400; явный crop возвращает исходные 400 × 320. Сохранённые PNG8 компонентов — только просмотры и не загружаются в inverse. Архив содержит example.php, source.png.b64 и fourier-fixture.php. Сохраните рядом и запустите php example.php с Imagick, реально работающим FFTW, поддержкой PNG и правом записи. Проверенная среда: Ubuntu 24.04 / WSL 2, PHP 8.3.6, Imagick 3.7.0, ImageMagick 6.9.12-98 Q16 без HDRI. В другой сборке сначала проверьте поддержку FFTW; наличие имени метода этого не доказывает. Полный helper приведён в конце главы: он восстанавливает собственный PNG, готовит непрозрачный сигнал с полосами, сохраняет просмотры и измерения. Только inverse-minimal использует явно объявленную подготовку настоящей пары через forward(true); численные компоненты не читаются из PNG8.

Ожидаемый вывод в указанной сборке
fourier-inverse.png
inverse-magnitude-raw8.png
inverse-phase8.png
metrics.json
forward=bool:true inverse=bool:true padded=400x400 crop=400x320 max=2 RMSE=0.459784

Сравнить измеренные true/false и отличить успех вызова от качества

Оба прямых и оба обратных вызова вернули true. Однако в этой non-HDRI Q16-сборке real/imaginary даёт заметную потерю: max=116 и RMSE≈40.739962 в исходной области. Проверяется качество реальных пикселей, сохраняется исходник и перехватываются два настоящих ошибочных входа.

Сравнить измеренные true/false и отличить успех вызова от качестваПример для указанного окружения
<?php
declare(strict_types=1);
require __DIR__.'/fourier-fixture.php';
fourierRestore();
$base=fourierBase(); $signal=fourierStriped($base);
$baseBefore=fourierHash($base);$before=fourierHash($signal);$cases=[];$results=[];
foreach([true,false] as $representation){
    $name=$representation?'true':'false';$work=clone $signal;
    $forward=$work->forwardFourierTransformImage($representation);
    $index=$work->getIteratorIndex();$count=$work->getNumberImages();
    $work->setIteratorIndex(0);$first=$work->getImage();$work->setIteratorIndex(1);$second=$work->getImage();
    $a=fourierHash($first);$b=fourierHash($second);$result=clone $first;
    $inverse=$result->inverseFourierTransformImage($second,$representation);
    $paddedSize=[$result->getImageWidth(),$result->getImageHeight()];$fullError=fourierError(fourierPadded($signal),$result);
    $result->cropImage(400,320,0,0);$result->setImagePage(0,0,0,0);
    fourierView($result,'workflow-'.$name.'-restored8.png');$results[]=$result;
    $cases[$name]=['representation'=>$representation,'forward_return'=>$forward,'inverse_return'=>$inverse,'pair_count'=>$count,'index_after_forward'=>$index,
        'pair_sizes'=>[[$first->getImageWidth(),$first->getImageHeight()],[$second->getImageWidth(),$second->getImageHeight()]],
        'inverse_padded_size'=>$paddedSize,'crop_size'=>[$result->getImageWidth(),$result->getImageHeight()],
        'first_double_range'=>fourierRange($first),'second_double_range'=>fourierRange($second),'full_padded_error'=>$fullError,
        'error_vs_striped'=>fourierError($signal,$result),'pair_unchanged'=>$a===fourierHash($first)&&$b===fourierHash($second)];
}
$errors=[];
try{(new Imagick())->forwardFourierTransformImage(true);$errors['empty_forward']='not caught';}
catch(ImagickException $error){$errors['empty_forward']=get_class($error);}
try{$attempt=clone $signal;$attempt->inverseFourierTransformImage(new Imagick(),true);$errors['empty_complement']='not caught';}
catch(ImagickException $error){$errors['empty_complement']=get_class($error);}
fourierView($signal,'workflow-signal8.png');$rects=fourierCompare([$signal,...$results],'fourier-workflow.png');
$unchanged=fourierHash($base)===$baseBefore&&fourierHash($signal)===$before;
fourierDiagnostic('fourier-transform-workflow',['executed_apis'=>['forwardFourierTransformImage','inverseFourierTransformImage'],
    'PHP'=>PHP_VERSION,'Imagick'=>phpversion('imagick'),'ImageMagick'=>Imagick::getVersion(),'HDRI_enabled'=>Imagick::getHdriEnabled(),
    'delegates'=>Imagick::getConfigureOptions('DELEGATES'),'virtual_pixel_method'=>$signal->getImageVirtualPixelMethod(),
    'cases'=>$cases,'errors'=>$errors,'source_unchanged'=>$unchanged,'panel_geometry'=>$rects,
    'checks'=>['four_successful_returns'=>$cases['true']['forward_return']===true&&$cases['true']['inverse_return']===true&&$cases['false']['forward_return']===true&&$cases['false']['inverse_return']===true,
        'two_pairs'=>$cases['true']['pair_count']===2&&$cases['false']['pair_count']===2,
        'true_small_error'=>$cases['true']['error_vs_striped']['RGB8_max_abs']<=2,
        'false_loss_visible'=>$cases['false']['error_vs_striped']['RGB8_max_abs']>$cases['true']['error_vs_striped']['RGB8_max_abs'],
        'pairs_unchanged'=>$cases['true']['pair_unchanged']&&$cases['false']['pair_unchanged'],
        'two_errors_caught'=>array_values($errors)===['ImagickException','ImagickException'],'source_unchanged'=>$unchanged]]);
echo "fourier-workflow.png\nworkflow-signal8.png\nworkflow-true-restored8.png\nworkflow-false-restored8.png\nmetrics.json\n";
printf("true max=%d RMSE=%.6f; false max=%d RMSE=%.6f; errors=%d source-unchanged=%d\n",
    $cases['true']['error_vs_striped']['RGB8_max_abs'],$cases['true']['error_vs_striped']['RGB8_RMSE'],
    $cases['false']['error_vs_striped']['RGB8_max_abs'],$cases['false']['error_vs_striped']['RGB8_RMSE'],count($errors),(int)$unchanged);

Оба прямых и оба обратных вызова вернули true. Однако в этой non-HDRI Q16-сборке real/imaginary даёт заметную потерю: max=116 и RMSE≈40.739962 в исходной области. Проверяется качество реальных пикселей, сохраняется исходник и перехватываются два настоящих ошибочных входа. Архив содержит example.php, source.png.b64 и fourier-fixture.php. Сохраните рядом и запустите php example.php с Imagick, реально работающим FFTW, поддержкой PNG и правом записи. Проверенная среда: Ubuntu 24.04 / WSL 2, PHP 8.3.6, Imagick 3.7.0, ImageMagick 6.9.12-98 Q16 без HDRI. В другой сборке сначала проверьте поддержку FFTW; наличие имени метода этого не доказывает. Полный helper приведён в конце главы: он восстанавливает собственный PNG, готовит непрозрачный сигнал с полосами, сохраняет просмотры и измерения. Только inverse-minimal использует явно объявленную подготовку настоящей пары через forward(true); численные компоненты не читаются из PNG8.

Ожидаемый вывод в указанной сборке
fourier-workflow.png
workflow-signal8.png
workflow-true-restored8.png
workflow-false-restored8.png
metrics.json
true max=2 RMSE=0.459784; false max=116 RMSE=40.739962; errors=2 source-unchanged=1
Источники и обсуждения

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

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

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

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