lovell / sharp

High performance Node.js image processing, the fastest module to resize JPEG, PNG, WebP, AVIF and TIFF images. Uses the libvips library.
https://sharp.pixelplumbing.com
Apache License 2.0
29.21k stars 1.29k forks source link

Making pixels with different colors transparent when comparing 2 images #3754

Open vicegold opened 1 year ago

vicegold commented 1 year ago

Hi, I got 2 images with are quite similar, but have a different background color. I want to use these different colors to make all those pixels transparent.

So when I have these 2 images: test-black test-white

the result would be this: test-transparent

I got this working with Imagick, but don't even know where to start with Sharp/libvips:

convert test-black.png test-white.png -alpha off \
          \( -clone 0,1 -compose difference -composite -negate \) \
          \( -clone 0,2 +swap -compose divide -composite \) \
          -delete 0,1 +swap -compose Copy_Opacity -composite \
          test-transparent.png

Anybody got some ideas how I could do this in Sharp?

antonmarsden commented 1 year ago

@vicegold Could potentially use the unflatten function for this. It will convert pure white to transparent. You could then invert the colours and unflatten again to deal with the black background.

Might be tricky to deal with the white in the centre star though.

lovell commented 1 year ago

The difference composite blend mode can be used to work out the absolute difference between the two images and therefore provide a mask.

https://sharp.pixelplumbing.com/api-composite

await sharp('input1.png')
  .composite([{ input: 'input2.png', blend: 'difference' }])
  .toFile('mask.png');

As @antonmarsden suggests, you can then use unflatten to convert white pixels to transparent, and then use the in composite blend mode to apply the transparent mask to the initial input.

https://sharp.pixelplumbing.com/api-operation#unflatten

await sharp('mask.png')
  .unflatten()
  .composite([{ input: 'input1.png', blend: 'in' }])
  .toFile('out.png');

There is some aliasing around the edge, but I think this is present in the original images. Are they premultipled? You might need to experiment with the premultiplied option of composite if so.

lovell commented 1 year ago

@vicegold Were you able to make any progress with this?

vicegold commented 1 year ago

@antonmarsden @lovell Sorry for the very late reply and thanks for your help!

I've come closer to the desired result, but unfortunately can't overcome the aliasing issue. The images are not premultiplied, but I've also made sure to flatten my test images before running them through sharp. The mask always turns out fine, but applying the mask results in inaccurate edges. Using the premultiplied options doesn't seem to have any effect with these images.

Here's my results.

Sources: white black

Here's the resulting mask: mask

And here's the final image with the mask applied: out

Will try to debug more to find what's causing the aliasing.

lovell commented 5 months ago

@vicegold Were you able to make any progress with this?