使用 PHP 的 GD 库进行乘法过滤器

问题描述 投票:0回答:7

我尝试过使用GD库来模拟Photoshop的乘法效果,但我还没有找到可行的解决方案。

根据维基百科,乘法混合模式:

[...] 将顶层每个像素的数字与底层相应像素的数字相乘。结果是一张较暗的图片。

有谁知道使用 PHP 实现这一点的方法吗?任何帮助将不胜感激。

php filter gd photoshop
7个回答
13
投票

您需要获取图像的每个像素,然后将每个 RGB 值乘以您的背景颜色/255(这是 Photoshop 公式)。例如,带有红色背景颜色乘法滤镜的 JPG 文件,保存为 PNG 文件以获得更好的效果:

<?php 
$filter_r=216;
$filter_g=0;
$filter_b=26;
$suffixe="_red";
$path=YOURPATHFILE;

if(is_file($path)){
    $image=@imagecreatefromjpeg($path);
    $new_path=substr($path,0,strlen($path)-4).$suffixe.".png";

    $imagex = imagesx($image);
    $imagey = imagesy($image);
    for ($x = 0; $x <$imagex; ++$x) {
        for ($y = 0; $y <$imagey; ++$y) {
            $rgb = imagecolorat($image, $x, $y);
            $TabColors=imagecolorsforindex ( $image , $rgb );
            $color_r=floor($TabColors['red']*$filter_r/255);
            $color_g=floor($TabColors['green']*$filter_g/255);
            $color_b=floor($TabColors['blue']*$filter_b/255);
            $newcol = imagecolorallocate($image, $color_r,$color_g,$color_b);
            imagesetpixel($image, $x, $y, $newcol);
        }
    }

    imagepng($image,$new_path);
}
?>

5
投票

我一直在寻找两个图像之间的乘法混合,但找不到任何本机 PHP 解决方案。看来(目前)唯一的方法是“手动”逐像素设置像素。这是我的代码,它在两个图像之间进行乘法混合,假设图像大小相同。如果您愿意,您可以调整它以处理不同的尺寸。

function multiplyImage($dst,$src)
{
    $ow = imagesx($dst);
    $oh = imagesy($dst);

    $inv255 = 1.0/255.0;

    $c = imagecreatetruecolor($ow,$oh);
    for ($x = 0; $x <$ow; ++$x) 
    {
        for ($y = 0; $y <$oh; ++$y) 
        {
            $rgb_src = imagecolorsforindex($src,imagecolorat($src, $x, $y));
            $rgb_dst = imagecolorsforindex($dst,imagecolorat($dst, $x, $y));
            $r = $rgb_src['red'] * $rgb_dst['red']*$inv255;
            $g = $rgb_src['green'] * $rgb_dst['green']*$inv255;
            $b = $rgb_src['blue'] * $rgb_dst['blue']*$inv255;
            $rgb = imagecolorallocate($c,$r,$g,$b);
            imagesetpixel($c, $x, $y, $rgb);
        }
    }
    return $c;
}

函数返回图像对象,因此您应该确保在使用完它后执行 imagedestroy。

应该有一个使用覆盖本机PHP混合的解决方法,这表明目标图像的50%灰色像素将受到源像素的影响。理论上,如果您确实需要混合两个黑白图像(无灰色调),如果您设置目标图像的对比度,使白色变为 50% 灰色,然后在其上叠加混合源图像,应该给出你类似于乘法的东西。但对于彩色图像或灰度图像,这不起作用 - 上述方法似乎是唯一的选择。


3
投票

当我需要在 GD 中混合两个图像时,我被引入了这个线程。似乎没有专门用于此目的的代码,所以我将其留在这里以供将来访问此页面的访问者使用。

这是

colivier
答案的一个分支,支持两个图像的多重混合。

两个图像不需要具有相同的大小,但叠加图像将被调整大小并裁剪为底层的大小。我做了一个

fit
辅助函数来做到这一点,但不用为此烦恼。

imagecolorat
返回基色,即使是具有透明度的 PNG。也就是说,50% 的黑色(可见为 (128, 128, 128))将返回为 (0, 0, 0, 64) 64,即 alpha 值。此代码考虑了半透明度并将半透明颜色转换为可见颜色值。

// bottom layer
$img1 = imagecreatefromjpeg(realpath(__DIR__.'/profilePic.jpg'));

// top layer
$img2 = imagecreatefrompng(realpath(__DIR__.'/border2.png'));
imagealphablending($img2, false);
imagesavealpha($img2, true);

$imagex = imagesx($img1);
$imagey = imagesy($img1);

$imagex2 = imagesx($img2);
$imagey2 = imagesy($img2);

// Prereq: Resize img2 to match img1, cropping beyond the aspect ratio
$w1 = max(min($imagex2, $imagex), $imagex);
$h1 = max(min($imagey2, $imagey), $imagey);

$w_using_h1 = round($h1 * $imagex2 / $imagey2);
$h_using_w1 = round($w1 * $imagey2 / $imagex2);

if ($w_using_h1 > $imagex) {
    fit($img2, $imagex, $imagey, 'HEIGHT', true);
}
fit($img2, $imagex, $imagey, 'WIDTH', true);

// Actual multiply filter
for ($x = 0; $x < $imagex; ++$x) {
    for ($y = 0; $y < $imagey; ++$y) {
        $rgb1 = imagecolorat($img1, $x, $y);
        $rgb2 = imagecolorat($img2, $x, $y);
        $idx1 = imagecolorsforindex($img1, $rgb1);
        $idx2 = imagecolorsforindex($img2, $rgb2);

        // Shift left 8, then shift right 7
        // same as multiply by 256 then divide by 128
        // approximate multiply by 255 then divide by 127
        // This is basically multiply by 2 but, expanded to show that
        // we are adding a fraction of white to the translucent image
        // $adder = ($idx2['alpha'] << 8 >> 7);
        $adder = ($idx2['alpha'] << 1);
        $rmul = min(255, $idx2['red']   + $adder);
        $gmul = min(255, $idx2['green'] + $adder);
        $bmul = min(255, $idx2['blue']  + $adder);

        $color_r = floor($idx1['red'] * $rmul / 255);
        $color_g = floor($idx1['green'] * $gmul / 255);
        $color_b = floor($idx1['blue'] * $bmul / 255);

        $newcol = imagecolorallocatealpha($img1, $color_r, $color_g, $color_b, 0);
        imagesetpixel($img1, $x, $y, $newcol);
    }
}
imagejpeg($img1, __DIR__.'/out.jpg');



/**
 * Fits an image to a $w x $h canvas
 * 
 * @param type $w Target width
 * @param type $h Target height
 * @param int $fit_which Which dimension to fit
 * @param bool $upscale If set to true, will scale a smaller image to fit the given dimensions
 * @param bool $padded If set to true, will add padding to achieve given dimensions
 * 
 * @return Image object
 */
function fit(&$img, $w, $h, $fit_which = 'BOTH', $upscale = false, $padded = true) {

    if (!in_array($fit_which, array('WIDTH', 'HEIGHT', 'BOTH'))) {
        $fit_which = 'BOTH';
    }
    $w0 = imagesx($img);
    $h0 = imagesy($img);

    if (!$upscale && $w0 <= $w && $h0 <= $h)
        return $this;

    if ($padded) {
        $w1 = max(min($w0, $w), $w);
        $h1 = max(min($h0, $h), $h);
    }
    else {
        $w1 = min($w0, $w);
        $h1 = min($h0, $h);
    }
    $w_using_h1 = round($h1 * $w0 / $h0);
    $h_using_w1 = round($w1 * $h0 / $w0);

    // Assume width, crop height
    if ($fit_which == 'WIDTH') {
        $w2 = $w1;
        $h2 = $h_using_w1;
    }
    // Assume height, crop width
    elseif ($fit_which == 'HEIGHT') {
        $w2 = $w_using_h1;
        $h2 = $h1;
    }
    elseif ($fit_which == 'BOTH') {
        if (!$padded) {
            $w2 = $w = min($w, $w_using_h1);
            $h2 = $h = min($h, $h_using_w1);
        }
        else {
            // Extend vertically
            if ($h_using_w1 <= $h) {
                $w2 = $w1;
                $h2 = $h_using_w1;
            }
            // Extend horizontally
            else {
                $w2 = $w_using_h1;
                $h2 = $h1;
            }
        }
    }

    $im2 = imagecreatetruecolor($w, $h);
    imagealphablending($im2, true);
    imagesavealpha($im2, true);

    $transparent = imagecolorallocatealpha($im2, 255, 255, 255, 127);
    imagefill($im2, 0, 0, $transparent);

    imagealphablending($img, true);
    imagesavealpha($img, true);
    // imagefill($im, 0, 0, $transparent);

    imagecopyresampled($im2, $img, ($w - $w2) / 2, ($h - $h2) / 2, 0, 0, $w2, $h2, $w0, $h0);

    $img = $im2;    
}

0
投票

您尝试过使用php手册吗?

对于希望对图像应用“乘法”效果(例如 Photoshop 中的图像)(通常是黑白图像)的用户,可以使用 IMG_FILTER_COLORIZE 滤镜来实现。

<?php
function multiplyColor(&$im, $color = array(255, 0, 0)) {
   //get opposite color
   $opposite = array(255 - $color[0], 255 - $color[1], 255 - $color[2]);

   //now we subtract the opposite color from the image
   imagefilter($im, IMG_FILTER_COLORIZE, -$opposite[0], -$opposite[1], -$opposite[2]);
}
?>

0
投票

如果与 png 图像和 alpha 一起使用一定很好并且效果很好

    $filter_r=215;
    $filter_g=5;
    $filter_b=5;
    $alpha=70;
    $suffixe="_red";
    $path="./img/foto_220_590.png";
    if(is_file($path)){
        $image=imagecreatefrompng($path);
        $new_path=substr($path,0,strlen($path)-4).$suffixe.".png";

         echo $imagex = imagesx($image);
         echo $imagey = imagesy($image);
        for ($x = 0; $x <$imagex; ++$x) {
            for ($y = 0; $y <$imagey; ++$y) {
                $rgb = imagecolorat($image, $x, $y);
                $TabColors=imagecolorsforindex ( $image , $rgb );
                $color_r=floor($TabColors['red']*$filter_r/255);
                $color_g=floor($TabColors['green']*$filter_g/255);
                $color_b=floor($TabColors['blue']*$filter_b/255);
                //$newcol = imagecolorallocate($image, $color_r,$color_g,$color_b);
                // this new alpha
                $newcol = imagecolorallocatealpha($image, $color_r,$color_g,$color_b,$alpha);
                imagesetpixel($image, $x, $y, $newcol);
            }
        }
         imagepng($image,$new_path);


0
投票

我更新了 @colivier 脚本,以便能够叠加两个图像,而不仅仅是带有颜色的图像:

/**
 * Multiply $pathToDst and $pathToSrc to $resultPath
 *
 * @param string $pathToDst
 * @param string $pathToSrc
 * @param string $resultPath
 */
function multiply($pathToDst, $pathToSrc, $resultPath) {
    switch (pathinfo($pathToDst, PATHINFO_EXTENSION)) {
        case "gif" :
            $resourceDst = imagecreatefromgif($pathToDst);
            break;
        case "png" :
            $resourceDst = imagecreatefrompng($pathToDst);
            break;
        default :
            $resourceDst = imagecreatefromjpeg($pathToDst);
            break;
    }

    switch (pathinfo($pathToSrc, PATHINFO_EXTENSION)) {
        case "gif" :
            $resourceSrc = imagecreatefromgif($pathToSrc);
            break;
        case "png" :
            $resourceSrc = imagecreatefrompng($pathToSrc);
            break;
        default :
            $resourceSrc = imagecreatefromjpeg($pathToSrc);
            break;
    }

    for ($x = 0; $x < 400; ++$x) {
        for ($y = 0; $y < 400; ++$y) {
            $TabColorsFlag = imagecolorsforindex($resourceDst, imagecolorat($resourceDst, $x, $y));
            $TabColorsPerso = imagecolorsforindex($resourceSrc, imagecolorat($resourceSrc, $x, $y));

            $color_r = floor($TabColorsFlag['red'] * $TabColorsPerso['red'] / 255);
            $color_g = floor($TabColorsFlag['green'] * $TabColorsPerso['green'] / 255);
            $color_b = floor($TabColorsFlag['blue'] * $TabColorsPerso['blue'] / 255);
            imagesetpixel($resourceDst, $x, $y, imagecolorallocate($resourceSrc, $color_r, $color_g, $color_b));
        }
    }

    imagepng($resourceDst, $resultPath, 0);
    imagedestroy($resourceDst);
    imagedestroy($resourceSrc);
}

0
投票

从 PHP 7.2.0 开始,您可以使用带有 imagelayereffect 的 IMG_EFFECT_MULTIPLY 过滤器。

<?php

imagelayereffect($image, IMG_EFFECT_MULTIPLY);
// implement imagecopy()

?>
© www.soinside.com 2019 - 2024. All rights reserved.