Open gmcmicken opened 5 months ago
Hi @gmcmicken,
The "blend alpha" menu item is implemented in nip2's macro language -- if you click Toolkits / Edit you can read (and edit) the source code. It's in the repo here:
https://github.com/libvips/nip2/blob/master/share/nip2/start/Filter.def#L1016-L1274
It dates from way back, before libvips had composite
. It'd probably be simpler to base a php solution on that.
What's your exact requirement? Do you just need to blend two images together with an offset? If you can provide some test data and desired output, I could help.
I also can't seem to do a photographic negative filter in PHP
This one at least is easy -- just $image = $image->invert();
. You can look up the operators by description here:
https://www.libvips.org/API/current/func-list.html
Though now I check I see the description for invert
is not very helpful. I'll try to improve it.
Hi @jcupitt ,
Thanks for the help so far, when I tried invert() before and thought it didn't work it must have been before I read that each method returns a new image, and maybe I wrote the file from the original.
My requirements for blending are pretty much exactly what is in nip2, settings a blend mode, and offset, and opacity. I'm layering line art drawings, one with lines, one with shading, and I'm also modifying the line one to make a third which adds weight and depth (using blur & contrast and/or other faux anti-aliasing techniques). There's no alpha in the originals. Does that help?
Ah OK, then you can just cook up your own overlay pretty easily, it should just be a few lines of code. If you can make some sample images and an expected result it'll save me quite a bit of time writing an example.
The shaded one would be the base, the outline one is the top layer with darken mode. The outline-filter would be some type of modified layer with an offset to shadow the lines. I would like to learn how to do the opacity even though I could work a solution without it, it would be good to have available.
Outline
Outline, Filter Applied
Shading
Oops forgot the result.
Hi again, this seems to work:
#!/usr/bin/env php
<?php
require __DIR__ . '/vendor/autoload.php';
use Jcupitt\Vips;
if(count($argv) != 4) {
echo("usage: ./overlay.php outline-image shading-image output-image\n");
exit(1);
}
$outline = Vips\Image::newFromFile($argv[1], ['access' => 'sequential']);
$shading = Vips\Image::newFromFile($argv[2], ['access' => 'sequential']);
$output_filename = $argv[3];
// multiply blending
$image = $outline->divide(255)->multiply($shading);
$image->writeToFile($output_filename);
So for every pixel you compute (outline / 255) * shading
, ie. multiply blending. It's a shame PHP doesn't have operator overloading -- it's usually a curse, but this is one of the rare examples of OO actually being useful.
Translation is easiest with gravity
. Use it to add (for example) 4 pixels along the right and bottom, then crop four pixels off the left and top. In effect, you move the image up and left by 4. It sounds slow, but libvips will implement it all with pointer copies internally, so it's a fast and low memory way to do it.
Maybe (untested):
$shading = $shading
->gravity("north-west", $shading->width + 4, $shading->height + 4, ['extend' => 'copy'])
->crop(4, 4, $shading->width, $shading->height);
$image = $outline->divide(255)->multiply($shading);
I noticed your PNGs are all three band -- you could use one band PNGs for a useful drop in image size.
$ vips colourspace shading.png x.png b-w
$ ls -l shading.png x.png
-rw-rw-r-- 1 john john 178501 Jun 16 16:58 shading.png
-rw-r--r-- 1 john john 104232 Jun 16 17:16 x.png
Did you want to make the outline a little wider? It's easiest to do this with a small blur and a big contrast boost.
// multiply blending $image = $outline->divide(255)->multiply($shading);
Thanks, can you give me an example how to make it semi transparent? Add band 4 and adjust to 128 or something like that? (it's not for this layer, but potentially for other layers)
Did you want to make the outline a little wider? It's easiest to do this with a small blur and a big contrast boost.
Yes I haven't settled on the exact technique yet, I have done a couple variations in photoshop I wasn't paying too much attention to the example I gave, I think it was gaussian r1.0 and legacy photoshop contrast 50.
I'm blending the text layer in too but that won't have any thickness added.
I noticed your PNGs are all three band -- you could use one band PNGs for a useful drop in image size.
They will be colored in practice, or perhaps themed using a color swap for the greys I haven't decided yet at which point to apply the color. Would it speed up processing or just the output size?
It's really easy -- if you do:
$outline->divide(255)
Your outline image is now 0 - 1 for black to white. To make the black lines in the outlines midgrey instead, you need to add eg. 0.6. But you don't want the white parts to go over 1, so you need to scale the outline down to 0 - 0.4 instead.
$opacity = 0.8;
$outline = $outline->multiply($opacity / 255)->add(1 - $opacity);
$image = $outline->multiply($shading);
$image->writeToFile($output_filename);
I haven't decided yet at which point to apply the color. Would it speed up processing or just the output size?
Ah I see. Yes, you can apply the colour near the end, it'd be a bit quicker.
Hey sorry I went missing, thanks for your help again. I did decide to go with a colour output but if I want to target the greys now how would I use a conditional. In pseudocode it would be "if red == green == blue AND greater than 200"?
Maybe (untested):
// pixels where r == g == b
$grey_mask = $image[0]->equal($image[1])->equal(image[2]);
// pixels where r > 200
$bright_mask = $image[0]->more(200);
// AND them together
$mask = $grey_mask->andimage($bright_mask);
Hmm I've tried your method on finding grey and I also tried to simplify
$image = $image->equal([ $image[0], $image[0], $image[0] ])->ifthenelse([255,255,255], $image);
but it never hits on grey for some reason...
I don't think that will work, the code I posted is probably the best solution.
Here's what happens when I try just the grey mask as you've demonstrated. It just grabs everything that isn't white including the coloured areas.
php > $image = Jcupitt\Vips\Image::newFromFile('input.png');
php > $grey_mask = $image[0]->equal($image[1])->equal($image[2]);
php > $grey_mask->writeToFile('output.png');
Oh duh, sorry. Of course it should be:
$grey_mask = $image[0]->equal($image[1])->andimage($image[1]->equal($image[2]));
(also untested)
Thanks again. It's looking pretty good, I'm trying to automate the addition of a watercolour background and I'm wondering how I would go about comparing two masks, like the overlap of mask1 and mask2 as a number of pixels or a percentage?
Sorry, could you explain what you mean in more detail? Do you want to compute the number of pixels two masks have in common?
Sorry, could you explain what you mean in more detail? Do you want to compute the number of pixels two masks have in common?
Exactly, ya, you got it.
You can write something to count the non-zero pixels in an image as:
function count_set($image)
{
$avg = $image->notequal(0)->avg();
return $image->width * $image->height * $avg / 255.0;
}
Then it'd just be:
$n_pixels_in_common = count_set($image1->andimage($image2));
I'm finding the learning curve to the methods in php steep and since I'm on a deadline though maybe I would ask for a bit of help. In nip2 there is Toolkit -> Filter -> Blend -> Blend Alpha, which works well for my purpose as it allows overlay mode, opacity, and offset. But I can't seem to replicate this in PHP.
Also, I know it's probably super straightforward but I also can't seem to do a photographic negative filter in PHP, I thought maybe maplut() could do it?