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

Дерево руководства
Глава 392 · После объектов и RGB; подготовка двух изображений
Imagick: собрать стереопару в красно-голубой анаглиф
Готовый PNG из двух видов, понятный путь RGB, фактический новый объект вместо bool, ошибки совмещения и настоящее сохранение/чтение.
Получить готовый анаглиф из двух подготовленных видов
Для учебной карточки стереопары нужен один красно-голубой PNG, который можно положить на сайт и скачать. У нас два готовых вида одной условной сцены: задний прямоугольник и передний круг на неподвижной сетке. Ниже виден настоящий результат PHP. Без очков заметны красные и голубые контуры; с подходящей стереопарой и красно-голубыми очками они разделяются между глазами. Код сам по себе не превращает одиночную фотографию в объёмную сцену.

Оба изображения приложены к ZIP в base64; полный example.php автоматически восстанавливает left.png и right.png перед обработкой. Короткие четыре строки ниже показывают основной вызов для уже подготовленных файлов. Это самостоятельно нарисованная модель двух плоских слоёв, а не фотографии откалиброванной камеры. Между видами прямоугольник смещён на 4 px, круг на 12 px, сетка — на 0 px. Так видно, зачем нужны два различающихся вида. Рисунок упрощает реальные перспективу и перекрытия; одинаковую фотографию нельзя просто объявить вторым независимым ракурсом.
$left = new Imagick(__DIR__ . "/left.png");
$right = new Imagick(__DIR__ . "/right.png");
$result = $left->stereoImage($right);
$result->writeImage(__DIR__ . "/anaglyph.png");New Imagick открывает файл и создаёт объект. __DIR__ обозначает папку PHP-файла, точка склеивает её с именем. Стрелка -> вызывает метод объекта: слева берётся первый вид, в скобках передаётся второй. Знак = сохраняет новый результат в $result. Последняя строка записывает именно его. Это четыре основных действия; дальнейший полный запуск добавляет лишь восстановление входов, отдельные картинки и отчёт, чтобы результат было с чем сравнить. Основы объектов и аргументов и возвращаемых значений помогают разобрать запись.
Собрать красно-голубой превью-файл из двух готовых видов
Получаются левый вид, правый вид и красно-голубой анаглиф 400×320. Метод создал новый Imagick; оба входа не изменились. Проверены все 384000 значений RGB: R взят слева, G и B справа.
<?php
declare(strict_types=1);
require __DIR__.'/stereo-fixture.php';
stRestore();
$left=new Imagick(__DIR__.'/left.png');
$right=new Imagick(__DIR__.'/right.png');
$beforeLeft=stState($left);$beforeRight=stState($right);
$result=$left->stereoImage($right);
$images=['left'=>stImage($left,'first-left8.png'),'right'=>stImage($right,'first-right8.png'),'result'=>stImage($result,'first-result8.png')];
$rects=stCompare([$left,$right,$result],'stereo-first.png');
$mismatches=stChannelMismatches($left,$right,$result);
stDiagnostic('stereo-first',['executed_api'=>'stereoImage','returned_type'=>get_debug_type($result),'distinct_object'=>$result!==$left&&$result!==$right,
'before_left'=>$beforeLeft,'before_right'=>$beforeRight,'after_left'=>stState($left),'after_right'=>stState($right),'channel_mismatches'=>$mismatches,
'images'=>$images,'panel_keys'=>['left','right','result'],'panel_geometry'=>$rects,'source_unchanged'=>stState($left)===$beforeLeft&&stState($right)===$beforeRight,
'checks'=>['actual_Imagick'=>$result instanceof Imagick,'new_object'=>$result!==$left&&$result!==$right,'R_left_GB_right'=>$mismatches===0,
'left_unchanged'=>stState($left)===$beforeLeft,'right_unchanged'=>stState($right)===$beforeRight,'size_preserved'=>$result->getImageGeometry()===$left->getImageGeometry()]]);
echo "stereo-first.png\nfirst-left8.png\nfirst-right8.png\nfirst-result8.png\nmetrics.json\nreturn=Imagick new-object=1 channels-exact=1 inputs-unchanged=1\n";
Получаются левый вид, правый вид и красно-голубой анаглиф 400×320. Метод создал новый Imagick; оба входа не изменились. Проверены все 384000 значений RGB: R взят слева, G и B справа. Каждый ZIP самодостаточен: example.php, два собственных PNG в base64 и stereo-fixture.php. Распакуйте рядом и выполните php example.php. stRestore проверяет хэши и восстанавливает left.png/right.png. stImage/stCompare сохраняют настоящие RGB8-копии и панели без масштабирования; stState читает размеры и RGB16; stDiagnostic пишет metrics.json. Изучаемые stereoImage-вызовы находятся в основном коде. Полный helper раскрыт в последнем разделе. Среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16, Windows.
Ожидаемый вывод программы в проверенной среде
stereo-first.png
first-left8.png
first-right8.png
first-result8.png
metrics.json
return=Imagick new-object=1 channels-exact=1 inputs-unchanged=1
Проследить три канала и понять роль двух разных видов

RGB — три числа, задающие красную, зелёную и синюю составляющие пикселя. Представьте три провода на выходе: красный подключён к левому виду, зелёный и синий — к правому. В точке с левым RGB(17,89,201) и правым RGB(231,57,102) получаем RGB(17,57,102). Это реальный контрольный вызов во втором примере. Числа 89,201 и 231 в этот выходной пиксель не попали. Аналогия описывает выбор каналов, а не физическую схему памяти PHP.
Мы используем серые исходники, где R=G=B: это позволяет показать содержимое обоих видов без подавления цветного объекта только потому, что у него слабый красный канал. Метод не переводит любое цветное фото в серое автоматически. Он не измеряет расстояние до круга и не вычисляет карту глубины. Фиксированная сетка остаётся совмещённой, а разные горизонтальные смещения фигур дают видимые цветные края. Перестановка $left и $right меняет назначение глаз; вертикальная ошибка остаётся ошибкой.
Сохранить новый объект и проверить RGB на простом примере

Не все методы Imagick меняют получателя. Здесь $left->stereoImage($right) возвращает отдельный Imagick. Если проигнорировать результат и вызвать $left->writeImage(...), сохранится левый вид без анаглифа. Поэтому полезно сначала освоить одну строку присваивания, а потом помещать её в обработчик. Официальное bool/true и Reflection в этой версии неточны; ниже рядом показаны объявленный тип и фактический объект, а также контрольный пиксель.
Присвоить результат вызова и проверить один цветной пиксель
Исходный левый вид остаётся серым, а новый объект содержит анаглиф. Контрольный RGB слева (17,89,201), справа (231,57,102) даёт (17,57,102). В проверенном binding Reflection сообщает bool, но действительный результат — Imagick.
<?php
declare(strict_types=1);
require __DIR__.'/stereo-fixture.php';
stRestore();
$left=new Imagick(__DIR__.'/left.png');
$right=new Imagick(__DIR__.'/right.png');
$beforeLeft=stState($left);$beforeRight=stState($right);
$result=$left->stereoImage($right);
$images=['before'=>stImage($left,'minimal-before8.png'),'result'=>stImage($result,'minimal-result8.png')];
$rects=stCompare([$left,$result],'stereo-minimal.png');
$leftPixel=new Imagick();$leftPixel->newImage(1,1,'rgb(17,89,201)','png');
$rightPixel=new Imagick();$rightPixel->newImage(1,1,'rgb(231,57,102)','png');
$pixelResult=$leftPixel->stereoImage($rightPixel);
$pixel=$pixelResult->getImagePixelColor(0,0)->getColor();
$declared=(string)(new ReflectionMethod(Imagick::class,'stereoImage'))->getReturnType();
stDiagnostic('stereo-minimal',['executed_api'=>'stereoImage','returned_type'=>get_debug_type($result),'declared_return'=>$declared,
'calibration'=>['left'=>[17,89,201],'right'=>[231,57,102],'result'=>[$pixel['r'],$pixel['g'],$pixel['b']]],
'before_left'=>$beforeLeft,'before_right'=>$beforeRight,'after_left'=>stState($left),'after_right'=>stState($right),
'images'=>$images,'panel_keys'=>['before','result'],'panel_geometry'=>$rects,'source_unchanged'=>stState($left)===$beforeLeft&&stState($right)===$beforeRight,
'checks'=>['actual_Imagick'=>$result instanceof Imagick,'declared_bool_is_not_actual'=>$declared==='bool','pixel_17_57_102'=>[$pixel['r'],$pixel['g'],$pixel['b']] === [17,57,102],
'result_differs_from_receiver'=>stHash($result)!==stHash($left),'left_unchanged'=>stState($left)===$beforeLeft,'right_unchanged'=>stState($right)===$beforeRight]]);
echo "stereo-minimal.png\nminimal-before8.png\nminimal-result8.png\nmetrics.json\ndeclared=bool actual=Imagick pixel=17,57,102 original-unchanged=1\n";
Исходный левый вид остаётся серым, а новый объект содержит анаглиф. Контрольный RGB слева (17,89,201), справа (231,57,102) даёт (17,57,102). В проверенном binding Reflection сообщает bool, но действительный результат — Imagick. Каждый ZIP самодостаточен: example.php, два собственных PNG в base64 и stereo-fixture.php. Распакуйте рядом и выполните php example.php. stRestore проверяет хэши и восстанавливает left.png/right.png. stImage/stCompare сохраняют настоящие RGB8-копии и панели без масштабирования; stState читает размеры и RGB16; stDiagnostic пишет metrics.json. Изучаемые stereoImage-вызовы находятся в основном коде. Полный helper раскрыт в последнем разделе. Среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16, Windows.
Ожидаемый вывод программы в проверенной среде
stereo-minimal.png
minimal-before8.png
minimal-result8.png
metrics.json
declared=bool actual=Imagick pixel=17,57,102 original-unchanged=1
В полном примере массив calibration хранит входные и выходные RGB. Reflection нужен только для демонстрации обнаруженного расхождения документации; чтобы создать анаглиф, он не требуется. Вторая картинка — результат метода, а не вручную добавленные цветные контуры. Исходные PNG на диске и оба прочитанных объекта не изменяются.
Разобрать ошибки пары, сохранить из функции и открыть результат

На практике пользователи путают соединение кадров с подготовкой стереопары. Сначала сравним три ошибочных ожидания: повтор одной картинки, перестановку глаз и сдвиг правого вида вниз на 8 px. Отдельно изменим page-offset — это метаданные виртуального холста, а не перемещение пикселей в этой операции.
Сравнить одинаковые виды, перепутанный порядок и вертикальную ошибку
Одинаковые входы не создают нового ракурса. При перестановке видов меняются красный и голубые контуры; вертикальный сдвиг 8 px создаёт ошибку совмещения. Изменение только page-offset на (20,8) не сдвигает пиксели при stereoImage.
<?php
declare(strict_types=1);
require __DIR__.'/stereo-fixture.php';
stRestore();
$left=new Imagick(__DIR__.'/left.png');
$right=new Imagick(__DIR__.'/right.png');
$beforeLeft=stState($left);$beforeRight=stState($right);
$normal=$left->stereoImage($right);
$same=$left->stereoImage($left);
$swapped=$right->stereoImage($left);
$shifted=new Imagick();$shifted->newImage(400,320,'#f0f0f0','png');
$shifted->compositeImage($right,Imagick::COMPOSITE_COPY,0,8);
$vertical=$left->stereoImage($shifted);
$pageOnly=clone $right;$pageOnly->setImagePage(400,320,20,8);
$pageResult=$left->stereoImage($pageOnly);
$images=['same'=>stImage($same,'controls-same8.png'),'swapped'=>stImage($swapped,'controls-swapped8.png'),'vertical'=>stImage($vertical,'controls-vertical8.png')];
$rects=stCompare([$same,$swapped,$vertical],'stereo-controls.png');
stDiagnostic('stereo-controls',['executed_api'=>'stereoImage','normal_hash'=>stHash($normal),'left_hash'=>stHash($left),'page_result_hash'=>stHash($pageResult),
'same_hash'=>stHash($same),'swapped_hash'=>stHash($swapped),'vertical_hash'=>stHash($vertical),'vertical_shift_px'=>8,'page_only_offset'=>[20,8],
'images'=>$images,'panel_keys'=>['same','swapped','vertical'],'panel_geometry'=>$rects,'source_unchanged'=>stState($left)===$beforeLeft&&stState($right)===$beforeRight,
'checks'=>['same_pair_no_new_view'=>stHash($same)===stHash($left),'swapped_changes_channels'=>stHash($swapped)!==stHash($normal),
'vertical_error_visible'=>stHash($vertical)!==stHash($normal),'page_offset_does_not_shift_pixels'=>stHash($pageResult)===stHash($normal),
'left_unchanged'=>stState($left)===$beforeLeft,'right_unchanged'=>stState($right)===$beforeRight]]);
echo "stereo-controls.png\ncontrols-same8.png\ncontrols-swapped8.png\ncontrols-vertical8.png\nmetrics.json\nsame-pair=unchanged swapped=different vertical=different page-offset=ignored\n";
Одинаковые входы не создают нового ракурса. При перестановке видов меняются красный и голубые контуры; вертикальный сдвиг 8 px создаёт ошибку совмещения. Изменение только page-offset на (20,8) не сдвигает пиксели при stereoImage. Каждый ZIP самодостаточен: example.php, два собственных PNG в base64 и stereo-fixture.php. Распакуйте рядом и выполните php example.php. stRestore проверяет хэши и восстанавливает left.png/right.png. stImage/stCompare сохраняют настоящие RGB8-копии и панели без масштабирования; stState читает размеры и RGB16; stDiagnostic пишет metrics.json. Изучаемые stereoImage-вызовы находятся в основном коде. Полный helper раскрыт в последнем разделе. Среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16, Windows.
Ожидаемый вывод программы в проверенной среде
stereo-controls.png
controls-same8.png
controls-swapped8.png
controls-vertical8.png
metrics.json
same-pair=unchanged swapped=different vertical=different page-offset=ignored
Одинаковые виды возвращают тот же RGB: новых данных о глубине не появилось. При перестановке меняется распределение каналов. При вертикальном сдвиге даже горизонтальные линии сетки расходятся по высоте. Только page-offset (20,8) в нашем опыте дал прежний результат: stereoImage вызывает StereoImage с нулевыми внутренними смещениями. Исправлять несовпадение нужно подготовкой реальных видов, а не произвольным подбором page-offset.
Теперь поместим рабочее действие в функцию makeStereoPreview. Её параметры — два пути и путь результата; return возвращает готовый объект для дальнейшего применения. Проверка stValidate требует один кадр и одинаковые размеры, затем выполняется тот же stereoImage. Записанный файл действительно открывается снова, поэтому отчёт подтверждает содержимое PNG, а не только объект до записи.
Сохранить превью из функции, перечитать PNG и обработать ошибки
Функция сохраняет готовый anaglyph-data.png. После настоящего открытия совпадают все RGB и размеры. Четыре вызова с ошибками сохранены по классу и сообщению; предварительная проверка разных размеров показана отдельно.
<?php
declare(strict_types=1);
require __DIR__.'/stereo-fixture.php';
stRestore();
$left=new Imagick(__DIR__.'/left.png');
$right=new Imagick(__DIR__.'/right.png');
$beforeLeft=stState($left);$beforeRight=stState($right);
function makeStereoPreview(string $leftFile,string $rightFile,string $outputFile):Imagick {
$a=new Imagick($leftFile);$b=new Imagick($rightFile);
stValidate($a,$b);
$preview=$a->stereoImage($b);
$preview->setImageFormat('png');$preview->setImageDepth(8);
$preview->setOption('png:color-type','2');$preview->setOption('png:bit-depth','8');
if(!$preview->writeImage($outputFile)){throw new RuntimeException('Cannot write stereo preview');}
return $preview;
}
$result=makeStereoPreview(__DIR__.'/left.png',__DIR__.'/right.png',__DIR__.'/anaglyph-data.png');
$reloaded=new Imagick(__DIR__.'/anaglyph-data.png');
$small=clone $right;$small->cropImage(399,320,0,0);
$errors['different_size']=stTry(fn()=>$left->stereoImage($small));
$errors['empty_receiver']=stTry(fn()=>(new Imagick())->stereoImage($right));
$errors['empty_argument']=stTry(fn()=>$left->stereoImage(new Imagick()));
$errors['wrong_type']=stTry(fn()=>$left->stereoImage('right.png'));
$policy=stTry(fn()=>stValidate($left,$small));
$images=['result'=>stImage($result,'workflow-result8.png'),'reloaded'=>stImage($reloaded,'workflow-reloaded8.png')];
$rects=stCompare([$result,$reloaded],'stereo-workflow.png');
$checks=['saved_RGB_exact'=>stHash($result)===stHash($reloaded),'saved_geometry'=>$reloaded->getImageGeometry()===['width'=>400,'height'=>320],
'policy_rejected_different_size'=>($policy['error']['class']??null)==='InvalidArgumentException','inputs_unchanged'=>stState($left)===$beforeLeft&&stState($right)===$beforeRight];
foreach($errors as$name=>$entry){$checks['caught_'.$name]=isset($entry['error']);}
stDiagnostic('stereo-workflow',['executed_api'=>'stereoImage','saved'=>['filename'=>'anaglyph-data.png','sha256'=>hash_file('sha256',__DIR__.'/anaglyph-data.png'),'bytes'=>filesize(__DIR__.'/anaglyph-data.png'),'state'=>stState($reloaded)],
'errors'=>$errors,'policy_error'=>$policy,'images'=>$images,'panel_keys'=>['result','reloaded'],'panel_geometry'=>$rects,'source_unchanged'=>stState($left)===$beforeLeft&&stState($right)===$beforeRight,'checks'=>$checks]);
echo "stereo-workflow.png\nworkflow-result8.png\nworkflow-reloaded8.png\nanaglyph-data.png\nmetrics.json\nsaved-RGB-exact=1 errors=4 policy-rejection=1 inputs-unchanged=1\n";
Функция сохраняет готовый anaglyph-data.png. После настоящего открытия совпадают все RGB и размеры. Четыре вызова с ошибками сохранены по классу и сообщению; предварительная проверка разных размеров показана отдельно. Каждый ZIP самодостаточен: example.php, два собственных PNG в base64 и stereo-fixture.php. Распакуйте рядом и выполните php example.php. stRestore проверяет хэши и восстанавливает left.png/right.png. stImage/stCompare сохраняют настоящие RGB8-копии и панели без масштабирования; stState читает размеры и RGB16; stDiagnostic пишет metrics.json. Изучаемые stereoImage-вызовы находятся в основном коде. Полный helper раскрыт в последнем разделе. Среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16, Windows.
Ожидаемый вывод программы в проверенной среде
stereo-workflow.png
workflow-result8.png
workflow-reloaded8.png
anaglyph-data.png
metrics.json
saved-RGB-exact=1 errors=4 policy-rejection=1 inputs-unchanged=1
| Проблема | Что получено и что делать |
|---|---|
| 399×320 вместо 400×320 | Native даёт ImagickException. В рабочей функции это раньше отклоняет собственная проверка размеров; не растягивайте только один глаз наугад. |
| Пустой получатель или аргумент | Два реальных ImagickException. Сначала успешно прочитайте оба входа. |
| Строка right.png вместо объекта | TypeError. Создайте new Imagick(...), затем передайте объект. |
| Сохранили левую картинку | Проверьте присваивание возвращённого Imagick и writeImage именно результата. |
| Файл изменил вид после конвертации | Согласуйте профили и непрозрачный фон заранее. Наш точный readback относится к PNG8 и подготовленной серой паре, а не ко всем кодерам/фотографиям. |
Проверить полный контракт, запустить архив и применить результат
Полная официальная запись и уточнение фактического результата
Imagick::stereoImage(Imagick $offset_wand): boolОфициальная страница описывает объединение левого и правого изображений стереопары. Единственный обязательный параметр offset_wand имеет тип Imagick: это второй объект с изображением. В официальной таблице дополнительное описание параметра отсутствует; это не числовое смещение, не путь к файлу и не режим смешивания. В метод не передаются x/y: заранее подготовьте оба вида. Доступность: PECL imagick 2, PECL imagick 3. Запись не отмечена устаревшей в принятом инвентаре.
Важная неточность PHP.net и stub Imagick 3.8.1: они указывают bool и true при успехе. Реальный C-код создаёт возвращаемый объект Imagick; наш запуск PHP 8.5.11 / Imagick 3.8.1 / IM 7.1.1-46 подтвердил новый объект, отличный от обоих входов. Reflection в этой же сборке сообщает bool — этого описания недостаточно, чтобы понять поведение. Рабочий вызов: $result = $left->stereoImage($right); затем сохраняйте $result. Не проверяйте результат через === true и не записывайте левый вход в надежде, что он уже преобразован.
$result = $left->stereoImage($right); // фактически Imagick в проверенной сборке
$result->writeImage(__DIR__ . "/anaglyph.png");| Часть контракта | Практический смысл |
|---|---|
| Получатель $left | Текущее изображение левого вида. Наш проект открывает по одному кадру в каждом объекте. |
| Аргумент $right | Текущее изображение правого вида с такими же размерами в пикселях. |
| RGB результата | R = R левого; G = G правого; B = B правого. Это не средняя яркость двух кадров. |
| Геометрия | Размеры должны совпадать. API не калибрует камеры, не выравнивает виды и не восстанавливает глубину. |
| Изменение данных | В нашей проверенной сборке создаётся новый объект, оба входа сохранены. |
| Ошибки | Официально ImagickException. Разные размеры и два случая пустого объекта реально дали ImagickException; строка вместо Imagick — TypeError. |
Исходники MagickCore отдельно обрабатывают alpha при определённых свойствах канала. Эти примеры используют непрозрачные sRGB-входы и не делают утверждений о произвольной прозрачности. Для фотографий сначала согласуйте цветовое пространство, экспозицию, масштаб, ориентацию и вертикальное положение; само объединение каналов этих задач не решает. Красный фильтр перед левым глазом и голубой перед правым разделяют каналы, но субъективный объём зависит от пары, очков и просмотра. Без подходящих очков видны цветные контуры.
Два различающихся применения: базовый превью-файл из готовой пары и функция сохранения с проверкой входов/настоящим readback. При ограничении обычным плоским превью используйте обычную композицию; для расположения двух видов рядом нужно соединение панелей, а не stereoImage. Контроль цветового пространства и ориентации выполняется до анаглифа.
Запуск, ограничения и следующее упражнение
Нужны PHP с Imagick и доступ на запись в распакованную папку. Все четыре ZIP содержат входы и helper; запустите php example.php. Во всех новых программах созданы настоящие PNG и metrics.json. Среда проверки закреплена: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16, Windows. Исходные фигуры созданы специально для этого мануала; внешних фотографий и шрифтов в пакетах нет.
Упражнение: коллега записал $ok = $left->stereoImage($right), проверил $ok === true и затем сохранил $left. Почему готового анаглифа нет? Ответ: в проверенной сборке $ok — объект, поэтому строгая проверка на true ложна; левый вид не изменён. Назовите переменную $result, сохраните её, затем откройте PNG и сравните. Второе упражнение: поменяйте виды местами и объясните смену цветных краёв по трём каналам, не называя её автоматическим исправлением глубины.
Выход содержит число пикселей одного вида; хранение обоих входов, результата и диагностических массивов требует дополнительной памяти по их площади. В этом учебном примере полный RGB16 экспорт нужен для доказательства маршрута каналов; обычному обработчику не обязательно делать такой аудит на каждом запросе. Камеры, их синхронизация и калибровка находятся за границами данного метода. Для анимации нужно осмысленно сопоставлять кадры — один вызов не обрабатывает автоматически все пары последовательности.
Полный вспомогательный PHP из каждого архива
<?php
declare(strict_types=1);
function stRestore():void {
$hashes=json_decode('{"left.png":"9021ff43b1b06d148cf7e35809a4a6e559f02111d22a3a71ff23da66b2cdb037","right.png":"02f25666567bfa529b0bc039ab506a044a38cc00464594b8a3f56ac1eed95ef4"}',true,512,JSON_THROW_ON_ERROR);
foreach($hashes as $name=>$hash){$raw=base64_decode((string)file_get_contents(__DIR__.'/'.$name.'.b64'),true);
if($raw===false||hash('sha256',$raw)!==$hash||file_put_contents(__DIR__.'/'.$name,$raw)===false){throw new RuntimeException('Stereo input missing or changed');}}
}
function stHash(Imagick $image):string {return hash('sha256',json_encode($image->exportImagePixels(0,0,$image->getImageWidth(),$image->getImageHeight(),'RGB',Imagick::PIXEL_SHORT),JSON_THROW_ON_ERROR));}
function stState(Imagick $image):array {return ['geometry'=>[$image->getImageWidth(),$image->getImageHeight()],'RGB16_sha256'=>stHash($image),'frames'=>$image->getNumberImages()];}
function stView(Imagick $image):Imagick {$c=clone $image;$c->stripImage();$c->setImageFormat('png');$c->setImageDepth(8);$c->setOption('png:bit-depth','8');$c->setOption('png:color-type','2');$c->setOption('png:exclude-chunk','all');return $c;}
function stImage(Imagick $image,string $filename):array {
if(!stView($image)->writeImage(__DIR__.'/'.$filename)){throw new RuntimeException('Cannot save PNG');}$read=new Imagick(__DIR__.'/'.$filename);
return stState($image)+['encoded_png'=>['filename'=>$filename,'RGB16_sha256'=>stHash($read)],'display_basis'=>'Native RGB8 clone, original size'];
}
function stCompare(array $images,string $filename):array {
$board=new Imagick();$board->newImage(400*count($images),320,'white','png');$rects=[];
foreach($images as$i=>$image){$board->compositeImage(stView($image),Imagick::COMPOSITE_COPY,400*$i,0);$rects[]=['x'=>400*$i,'y'=>0,'width'=>400,'height'=>320];}
if(!stView($board)->writeImage(__DIR__.'/'.$filename)){throw new RuntimeException('Cannot save comparison');}return $rects;
}
function stChannelMismatches(Imagick $left,Imagick $right,Imagick $result):int {
$w=$left->getImageWidth();$h=$left->getImageHeight();$a=$left->exportImagePixels(0,0,$w,$h,'RGB',Imagick::PIXEL_SHORT);$b=$right->exportImagePixels(0,0,$w,$h,'RGB',Imagick::PIXEL_SHORT);$c=$result->exportImagePixels(0,0,$w,$h,'RGB',Imagick::PIXEL_SHORT);$n=0;
foreach($c as$k=>$v){if($v!==($k%3===0?$a[$k]:$b[$k]))$n++;}return $n;
}
function stTry(callable $operation):array {try{$r=$operation();return ['type'=>get_debug_type($r),'returned'=>$r instanceof Imagick?stState($r):$r];}catch(Throwable $e){return ['error'=>['class'=>get_class($e),'message'=>$e->getMessage()]];}}
function stValidate(Imagick $left,Imagick $right):void {
if($left->getNumberImages()!==1||$right->getNumberImages()!==1){throw new InvalidArgumentException('Project expects one image per eye');}
if($left->getImageGeometry()!==$right->getImageGeometry()){throw new InvalidArgumentException('Project expects equal pixel dimensions');}
}
function stDiagnostic(string $id,array $data):void {
$doc=['schema'=>'aen-stereo-image-program-diagnostics-v1','example_id'=>$id,'runtime'=>['php'=>PHP_VERSION,'imagick'=>phpversion('imagick'),'imagemagick'=>Imagick::getVersion()]]+$data;
if(file_put_contents(__DIR__.'/metrics.json',json_encode($doc,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)."\n")===false){throw new RuntimeException('Cannot save measurements');}
}
StRestore восстанавливает ровно два известных входа по SHA. StState/stHash читают размеры и RGB16. StImage и stCompare записывают native-результаты без изменения масштаба; stChannelMismatches сверяет каждый R с левым, каждый G/B с правым. StTry сохраняет настоящие классы ошибок, stValidate — отдельное правило проекта. Ни одна из этих функций не скрывает вызов stereoImage: он показан в основном коде.
Реальные вопросы, которые помогают понять границы задачи
Обсуждения ниже служат источником практических задач; решения в этом разделе написаны самостоятельно, технические утверждения сверены с точными исходниками и запуском.
How to mix up a stereo image with EmguCV?: Два кадра камер пытаются смешать как обычные полупрозрачные изображения, не понимая выбора каналов. Показываем конкретный RGB-путь и отдельный контрольный пиксель; готовый анаглиф требует R слева и G/B справа, а не произвольного среднего.
Superimpose two web cam streams - Matlab: Разработчик хочет показать две камеры как стереовидео и смешивает задачу получения кадров с их объединением. Разделяем готовую синхронную пару и один вызов. Наш пример пишет одиночный PNG; realtime и синхронизация камер этим методом не реализованы.
Extract depth map from a 3D anaglyph image: Из анаглифа хотят получить карту глубины. Не обещаем вычисление расстояний: stereoImage лишь соединяет каналы. На собственной двухслойной модели объясняем смещения, не объявляя её полноценной реконструкцией.
Split image and name each half: Нужно получить и правильно назвать левый и правый виды из общего файла стереокамеры. Явно называем входы left.png/right.png и не смешиваем перестановку глаз со случайной нумерацией. Наш архив содержит готовую пару; извлечение из side-by-side выполняют до вызова.
Imagick compositeImage doesn't apply original width and height but smaller: После композиции воспринимаемый размер объекта не совпадает с ожиданием, и автор пытается подбирать множители масштаба. Проверяем одинаковые реальные размеры обоих видов и показываем настоящий отказ при399×320; не растягиваем один вид вслепую.
php imagick convert PNG to jpg: PNG после JPEG-конвертации даёт неожиданный чёрный фон, а не ожидаемый рисунок. Фиксируем непрозрачные RGB8-входы и PNG-выход, отдельно сохраняем/открываем результат. Не распространяем это наблюдение на произвольную прозрачность и кодеры.
Функции и методы этой темы
Откройте карточку, чтобы увидеть договор функции, примеры, ограничения и связанные инструменты.
Imagick::stereoImageСобрать красно-голубой PNG из двух подготовленных видов
PECL imagick 2, PECL imagick 3 · imagick
Imagick::stereoImage(Imagick $offset_wand): boolПолная официальная запись и уточнение фактического результата
Imagick::stereoImage(Imagick $offset_wand): boolОфициальная страница описывает объединение левого и правого изображений стереопары. Единственный обязательный параметр offset_wand имеет тип Imagick: это второй объект с изображением. В официальной таблице дополнительное описание параметра отсутствует; это не числовое смещение, не путь к файлу и не режим смешивания. В метод не передаются x/y: заранее подготовьте оба вида. Доступность: PECL imagick 2, PECL imagick 3. Запись не отмечена устаревшей в принятом инвентаре.
Важная неточность PHP.net и stub Imagick 3.8.1: они указывают bool и true при успехе. Реальный C-код создаёт возвращаемый объект Imagick; наш запуск PHP 8.5.11 / Imagick 3.8.1 / IM 7.1.1-46 подтвердил новый объект, отличный от обоих входов. Reflection в этой же сборке сообщает bool — этого описания недостаточно, чтобы понять поведение. Рабочий вызов: $result = $left->stereoImage($right); затем сохраняйте $result. Не проверяйте результат через === true и не записывайте левый вход в надежде, что он уже преобразован.
$result = $left->stereoImage($right); // фактически Imagick в проверенной сборке
$result->writeImage(__DIR__ . "/anaglyph.png");| Часть контракта | Практический смысл |
|---|---|
| Получатель $left | Текущее изображение левого вида. Наш проект открывает по одному кадру в каждом объекте. |
| Аргумент $right | Текущее изображение правого вида с такими же размерами в пикселях. |
| RGB результата | R = R левого; G = G правого; B = B правого. Это не средняя яркость двух кадров. |
| Геометрия | Размеры должны совпадать. API не калибрует камеры, не выравнивает виды и не восстанавливает глубину. |
| Изменение данных | В нашей проверенной сборке создаётся новый объект, оба входа сохранены. |
| Ошибки | Официально ImagickException. Разные размеры и два случая пустого объекта реально дали ImagickException; строка вместо Imagick — TypeError. |
Исходники MagickCore отдельно обрабатывают alpha при определённых свойствах канала. Эти примеры используют непрозрачные sRGB-входы и не делают утверждений о произвольной прозрачности. Для фотографий сначала согласуйте цветовое пространство, экспозицию, масштаб, ориентацию и вертикальное положение; само объединение каналов этих задач не решает. Красный фильтр перед левым глазом и голубой перед правым разделяют каналы, но субъективный объём зависит от пары, очков и просмотра. Без подходящих очков видны цветные контуры.
Два различающихся применения: базовый превью-файл из готовой пары и функция сохранения с проверкой входов/настоящим readback. При ограничении обычным плоским превью используйте обычную композицию; для расположения двух видов рядом нужно соединение панелей, а не stereoImage. Контроль цветового пространства и ориентации выполняется до анаглифа.
Присвоить результат вызова и проверить один цветной пиксель
Исходный левый вид остаётся серым, а новый объект содержит анаглиф. Контрольный RGB слева (17,89,201), справа (231,57,102) даёт (17,57,102). В проверенном binding Reflection сообщает bool, но действительный результат — Imagick.
<?php
declare(strict_types=1);
require __DIR__.'/stereo-fixture.php';
stRestore();
$left=new Imagick(__DIR__.'/left.png');
$right=new Imagick(__DIR__.'/right.png');
$beforeLeft=stState($left);$beforeRight=stState($right);
$result=$left->stereoImage($right);
$images=['before'=>stImage($left,'minimal-before8.png'),'result'=>stImage($result,'minimal-result8.png')];
$rects=stCompare([$left,$result],'stereo-minimal.png');
$leftPixel=new Imagick();$leftPixel->newImage(1,1,'rgb(17,89,201)','png');
$rightPixel=new Imagick();$rightPixel->newImage(1,1,'rgb(231,57,102)','png');
$pixelResult=$leftPixel->stereoImage($rightPixel);
$pixel=$pixelResult->getImagePixelColor(0,0)->getColor();
$declared=(string)(new ReflectionMethod(Imagick::class,'stereoImage'))->getReturnType();
stDiagnostic('stereo-minimal',['executed_api'=>'stereoImage','returned_type'=>get_debug_type($result),'declared_return'=>$declared,
'calibration'=>['left'=>[17,89,201],'right'=>[231,57,102],'result'=>[$pixel['r'],$pixel['g'],$pixel['b']]],
'before_left'=>$beforeLeft,'before_right'=>$beforeRight,'after_left'=>stState($left),'after_right'=>stState($right),
'images'=>$images,'panel_keys'=>['before','result'],'panel_geometry'=>$rects,'source_unchanged'=>stState($left)===$beforeLeft&&stState($right)===$beforeRight,
'checks'=>['actual_Imagick'=>$result instanceof Imagick,'declared_bool_is_not_actual'=>$declared==='bool','pixel_17_57_102'=>[$pixel['r'],$pixel['g'],$pixel['b']] === [17,57,102],
'result_differs_from_receiver'=>stHash($result)!==stHash($left),'left_unchanged'=>stState($left)===$beforeLeft,'right_unchanged'=>stState($right)===$beforeRight]]);
echo "stereo-minimal.png\nminimal-before8.png\nminimal-result8.png\nmetrics.json\ndeclared=bool actual=Imagick pixel=17,57,102 original-unchanged=1\n";
Исходный левый вид остаётся серым, а новый объект содержит анаглиф. Контрольный RGB слева (17,89,201), справа (231,57,102) даёт (17,57,102). В проверенном binding Reflection сообщает bool, но действительный результат — Imagick. Каждый ZIP самодостаточен: example.php, два собственных PNG в base64 и stereo-fixture.php. Распакуйте рядом и выполните php example.php. stRestore проверяет хэши и восстанавливает left.png/right.png. stImage/stCompare сохраняют настоящие RGB8-копии и панели без масштабирования; stState читает размеры и RGB16; stDiagnostic пишет metrics.json. Изучаемые stereoImage-вызовы находятся в основном коде. Полный helper раскрыт в последнем разделе. Среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16, Windows.
Ожидаемый вывод программы в проверенной среде
stereo-minimal.png
minimal-before8.png
minimal-result8.png
metrics.json
declared=bool actual=Imagick pixel=17,57,102 original-unchanged=1
Сохранить превью из функции, перечитать PNG и обработать ошибки
Функция сохраняет готовый anaglyph-data.png. После настоящего открытия совпадают все RGB и размеры. Четыре вызова с ошибками сохранены по классу и сообщению; предварительная проверка разных размеров показана отдельно.
<?php
declare(strict_types=1);
require __DIR__.'/stereo-fixture.php';
stRestore();
$left=new Imagick(__DIR__.'/left.png');
$right=new Imagick(__DIR__.'/right.png');
$beforeLeft=stState($left);$beforeRight=stState($right);
function makeStereoPreview(string $leftFile,string $rightFile,string $outputFile):Imagick {
$a=new Imagick($leftFile);$b=new Imagick($rightFile);
stValidate($a,$b);
$preview=$a->stereoImage($b);
$preview->setImageFormat('png');$preview->setImageDepth(8);
$preview->setOption('png:color-type','2');$preview->setOption('png:bit-depth','8');
if(!$preview->writeImage($outputFile)){throw new RuntimeException('Cannot write stereo preview');}
return $preview;
}
$result=makeStereoPreview(__DIR__.'/left.png',__DIR__.'/right.png',__DIR__.'/anaglyph-data.png');
$reloaded=new Imagick(__DIR__.'/anaglyph-data.png');
$small=clone $right;$small->cropImage(399,320,0,0);
$errors['different_size']=stTry(fn()=>$left->stereoImage($small));
$errors['empty_receiver']=stTry(fn()=>(new Imagick())->stereoImage($right));
$errors['empty_argument']=stTry(fn()=>$left->stereoImage(new Imagick()));
$errors['wrong_type']=stTry(fn()=>$left->stereoImage('right.png'));
$policy=stTry(fn()=>stValidate($left,$small));
$images=['result'=>stImage($result,'workflow-result8.png'),'reloaded'=>stImage($reloaded,'workflow-reloaded8.png')];
$rects=stCompare([$result,$reloaded],'stereo-workflow.png');
$checks=['saved_RGB_exact'=>stHash($result)===stHash($reloaded),'saved_geometry'=>$reloaded->getImageGeometry()===['width'=>400,'height'=>320],
'policy_rejected_different_size'=>($policy['error']['class']??null)==='InvalidArgumentException','inputs_unchanged'=>stState($left)===$beforeLeft&&stState($right)===$beforeRight];
foreach($errors as$name=>$entry){$checks['caught_'.$name]=isset($entry['error']);}
stDiagnostic('stereo-workflow',['executed_api'=>'stereoImage','saved'=>['filename'=>'anaglyph-data.png','sha256'=>hash_file('sha256',__DIR__.'/anaglyph-data.png'),'bytes'=>filesize(__DIR__.'/anaglyph-data.png'),'state'=>stState($reloaded)],
'errors'=>$errors,'policy_error'=>$policy,'images'=>$images,'panel_keys'=>['result','reloaded'],'panel_geometry'=>$rects,'source_unchanged'=>stState($left)===$beforeLeft&&stState($right)===$beforeRight,'checks'=>$checks]);
echo "stereo-workflow.png\nworkflow-result8.png\nworkflow-reloaded8.png\nanaglyph-data.png\nmetrics.json\nsaved-RGB-exact=1 errors=4 policy-rejection=1 inputs-unchanged=1\n";
Функция сохраняет готовый anaglyph-data.png. После настоящего открытия совпадают все RGB и размеры. Четыре вызова с ошибками сохранены по классу и сообщению; предварительная проверка разных размеров показана отдельно. Каждый ZIP самодостаточен: example.php, два собственных PNG в base64 и stereo-fixture.php. Распакуйте рядом и выполните php example.php. stRestore проверяет хэши и восстанавливает left.png/right.png. stImage/stCompare сохраняют настоящие RGB8-копии и панели без масштабирования; stState читает размеры и RGB16; stDiagnostic пишет metrics.json. Изучаемые stereoImage-вызовы находятся в основном коде. Полный helper раскрыт в последнем разделе. Среда: PHP 8.5.11 / Imagick 3.8.1 / ImageMagick 7.1.1-46 Q16, Windows.
Ожидаемый вывод программы в проверенной среде
stereo-workflow.png
workflow-result8.png
workflow-reloaded8.png
anaglyph-data.png
metrics.json
saved-RGB-exact=1 errors=4 policy-rejection=1 inputs-unchanged=1
Код и руководство с собой
Скачайте самостоятельные примеры с исходными данными, инструкциями и ожидаемыми результатами. Примеры HTTP и фрагменты для проекта помечены отдельно: им нужно указанное окружение.
Код не исполняется на сайте. Скачайте нужный пример и запускайте его в своём локальном окружении.
Материалы сверены 2026-10-12. Примеры и объяснения написаны для этого руководства; источники и обсуждения доступны в каждой главе.


