Изображение PHP: сгенерированное изображение имеет правую и нижнюю черную рамку на рабочем сервере, но не на моей локальной машине

Это приложение написано мной, я протестировал его на своем локальном компьютере, и изображение, сгенерированное моим приложением, отлично работает без ошибок на моем локальном сервере разработки. Мое приложение представляет собой API, пользователь использует его для создания мозаичного изображения. Он в основном использует библиотеку PHP Image GD.

Проблема: сгенерированное изображение имеет правую и нижнюю черные границы шириной в один пиксель, но это происходит только на рабочем сервере, а не на моем локальном сервере. Граница создается только тогда, когда изображение прозрачное (в моем случае: «контур», «инверсия», «черный» тип изображения. Посмотрите на код ниже). Но иногда граница в основном есть, а иногда нет.

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

Вот фрагмент кода моего приложения, на который стоит посмотреть с подозрением:

$src = $img->filePath;
$src_outline = $img->filePathComplements['outline'];
$src_invert = $img->filePathComplements['invert'];
$src_black = $img->filePathComplements['black'];
$info_text = is_array($img->info) ? join($img->info, ', ') : (is_string($img->info) ? $img->info : '');

$w = $img->widthOriginal;
$h = $img->heightOriginal;
$x = $img->fit->x + $this->packer->getPageMarginLeft() + $this->packer->getMarginLeft() +
        $this->packer->getVerticalBorderWidth() + $this->packer->getVerticalBordefOffset();
$y = $img->fit->y + $this->packer->getPageMarginTop() + $this->packer->getMarginTop();
$info_y = $y + $h + $this->packer->getImageInfoMargin();

// Create main and complement images
$image_main = imagecreatefrompng($src);
$image_outline = imagecreatefrompng($src_outline);
$image_invert = imagecreatefrompng($src_invert);
$image_black = imagecreatefrompng($src_black);

list($w_px_original, $h_px_original) = getimagesize($src);

$image_main_resampled = Image::imageCreateTrueColorTransparent($w, $h);
$image_outline_resampled = Image::imageCreateTrueColorTransparent($w, $h);
$image_invert_resampled = Image::imageCreateTrueColorTransparent($w, $h);
$image_black_resampled = Image::imageCreateTrueColorTransparent($w, $h);

// Resample images from original dimension to DPI-based dimension
imagecopyresampled($image_main_resampled, $image_main, 0, 0, 0, 0, $w, $h, $w_px_original, $h_px_original);
imagecopyresampled($image_outline_resampled, $image_outline, 0, 0, 0, 0, $w, $h, $w_px_original, $h_px_original);
imagecopyresampled($image_invert_resampled, $image_invert, 0, 0, 0, 0, $w, $h, $w_px_original, $h_px_original);
imagecopyresampled($image_black_resampled, $image_black, 0, 0, 0, 0, $w, $h, $w_px_original, $h_px_original);

// Add image to all containers
// Parameters are: Destination image, source image, destination starting coordinates (x, y),
// source starting coordinates (x, y), source dimension (width, height).
imagecopy($container_main, $image_main_resampled, $x, $y, 0, 0, $w, $h);
imagecopy($container_outline, $image_outline_resampled, $x, $y, 0, 0, $w, $h);
imagecopy($container_invert, $image_invert_resampled, $x, $y, 0, 0, $w, $h);
imagecopy($container_black, $image_black_resampled, $x, $y, 0, 0, $w, $h);

// Add info to main and outline images
$info = Image::imageDrawTextBordered($w, $info_h, INFO_FONT_SIZE, INFO_BORDER_SIZE, $info_text);
imagecopy($container_main, $info, $x, $info_y, 0, 0, $w, $info_h);
imagecopy($container_outline, $info, $x, $info_y, 0, 0, $w, $info_h);

И Image::imageCreateTrueColorTransparent() это:

/**
 * Creates and returns image resource of a true color transparent image.
 * @param $width
 * @param $height
 * @return resource
 */
public static function imageCreateTrueColorTransparent($width, $height) {
    $im = imagecreatetruecolor($width, $height);

    imagealphablending($im, false);
    imagesavealpha($im, true);

    $transparent = imagecolorallocatealpha($im, 0, 0, 0, 127);
    imagefill($im, 0, 0, $transparent);

    return $im;
}

Пример результата с моего локального компьютера (щелкните, чтобы просмотреть в исходном размере): Пример результата с моей локальной машины

Пример результата с рабочего сервера (щелкните, чтобы просмотреть в исходном размере): Пример результата с рабочего сервера

Я проводил некоторые исследования здесь, в Stackoverflow, и я получил эти два потока, которые сказали, что проблема была сгенерирована функцией imagecopyresampled(). Тем не менее, я не уверен в этом, так как мое приложение безупречно работает на моей локальной машине. Это список веток обсуждения:

Любая помощь будет оценена по достоинству, пожалуйста, уточните, если вы знаете, что вызывает это, и / или вы когда-либо сталкивались с этим. Заранее спасибо.


person Rizki Pratama    schedule 01.03.2019    source источник
comment
Это известная проблема при передискретизации изображения. Когда изображение передискретизируется, новый пиксель должен быть рассчитан на основе соседних пикселей исходного изображения. Артефакт в основном возникает на краю из-за меньшего количества соседних пикселей, доступных для вычисления цвета новых пикселей. Даже фотошоп иногда себя так ведет. Обходной путь, который вы можете попробовать, - это изменить размер изображения немного больше, например, на 2 пикселя больше, чем фактический размер, и обрезать его или выполнить повторную выборку, как обычно, но затем вы удалите черную рамку, нарисовав цвет, который соответствует цвету фона.   -  person Zamrony P. Juhara    schedule 01.03.2019
comment
@ZamronyP.Juhara ZamronyP.Juhara - это общая проблема? Если да, то почему этого не происходит на моем компьютере? Влияет ли версия библиотеки или характеристики машины на результат передискретизации, особенно в этом случае?   -  person Rizki Pratama    schedule 05.03.2019
comment
Это зависит от входного изображения и алгоритма передискретизации изображения.   -  person Zamrony P. Juhara    schedule 05.03.2019


Ответы (1)


Эта функция изменяет размер изображения независимо от его формата или наличия альфа-канала/прозрачности.

Чтобы избежать проблемы, связанной с передискретизацией, создается дополненная версия исходного изображения, чтобы крайний справа столбец пикселей и строка пикселей ниже содержали достаточно данных для правильного отображения цветов.

Размер отступа рассчитывается независимо для двух осей на основе разницы в размерах между исходным изображением и изображением с измененным размером.

Например, изображение размером 256 x 128 пикселей, размер которого необходимо изменить до 16 x 16 пикселей, требует следующего заполнения:

256 / 16 = 16   columns on the right
128 / 16 = 8    rows on the bottom

это связано с тем, что цвет каждого пикселя измененного изображения будет рассчитываться на прямоугольнике 16x8 пикселей исходного изображения (в простейшем случае билинейной фильтрации).

Дополненное изображение

/** Resize an image resource.
 *
 * @param resource $src_image Original image resource.
 * @param int $dest_x Destination image x position.
 * @param int $dest_y  Destination image y position.
 * @param int $dest_width Destination width.
 * @param int $dest_height Destination height.
 * @param int $src_width Source width (can be less than full-width to get a subregion).
 * @param int $src_height Source height (can be less than full-height to get a subregion).
 * @return false|resource Resized image as resource, false on error.
 */
function resize_resource($src_image, $dest_x, $dest_y, $dest_width, $dest_height, $src_width, $src_height) {

    $img_width = imagesx($src_image);
    $img_height = imagesy($src_image);

    // Create a padded version of source image cloning rows/columns of pixels from last row/column
    // to ensure full coverage of right and bottom borders after rescaling.

    // Compute padding sizes.
    $pad_width = (int)ceil($img_width / $dest_width);
    $pad_height = (int)ceil($img_height / $dest_height);

    $padded = imagecreatetruecolor($img_width + $pad_width, $img_height + $pad_height);
    if ($padded === false) return false;

    imagealphablending($padded, false);

    $transparent = imagecolorallocatealpha($padded, 0, 0, 0, 127);
    imagefill($padded, 0, 0, $transparent);
    imagecopy($padded, $src_image, 0, 0, 0, 0, $img_width, $img_height);

    // Clone last column.
    for ($i = 0; $i < $pad_width; ++$i)
        imagecopy($padded, $src_image, $i + $img_width, 0, $img_width - 1, 0, 1, $img_height);

    // Clone last row.
    for ($i = 0; $i < $pad_height; ++$i)
        imagecopy($padded, $src_image, 0, $i + $img_height, 0, $img_height - 1, $img_width, 1);

    // Fill remaining padding area on bottom-right with color of bottom-right original image pixel.
    $pad_pixel = imagecolorat($padded, $img_width - 1, $img_height - 1);
    $pad_color = imagecolorallocatealpha($padded, ($pad_pixel >> 16) & 0xFF,
        ($pad_pixel >> 8) & 0xFF, $pad_pixel & 0xFF, ($pad_pixel >> 24) & 0x7F);
    imagefilledrectangle($padded, $img_width, $img_height,
        $img_width + $pad_width - 1, $img_height + $pad_height - 1, $pad_color);

    // Create new rescaled image.

    $new = imagecreatetruecolor($dest_width, $dest_height);
    if ($new === false) return false;

    imagealphablending($new, false);
    $transparent = imagecolorallocatealpha($new, 0, 0, 0, 127);
    imagefill($new, 0, 0, $transparent);

    imagecopyresampled($new, $padded, 0, 0, $dest_x, $dest_y, $dest_width, $dest_height, $src_width, $src_height);

    return $new;
}

ПРИМЕЧАНИЕ: для корректного отображения прозрачности в финальном изображении необходимо правильно использовать следующий код непосредственно перед записью на диск:

$transparent = imagecolorallocatealpha($img, 0, 0, 0, 127);
imagecolortransparent ($img, $transparent);

в случае изображений с индексированными цветами или следующий код:

imagesavealpha($img, true);

в случае изображений с альфа-каналом. Где $img — ресурс изображения с измененным размером, возвращаемый вышеописанной функцией.

Этот метод позволяет передискретизировать даже небольшие изображения, не создавая смещения по отношению к исходному изображению.

person Marco Sacchi    schedule 26.07.2019