question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How do I overlay successive images without resorting to using buffers?

See original GitHub issue

Hi,

I’ve just discovered sharp. I’m eternally grateful!

I’m trying to overlay many images onto a single image in order to create an image map.

I’ve gotten this far…

sharp({
  create: {
    width: 512,
    height: 512,
    channels: 4,
    background: {
      r: 255,
      g: 255,
      b: 255,
      alpha: 255
    }
  }
}).png()
.overlayWith(grassImagePath, {
  top: 0,
  left: 0
})
.pipe(response)

…but naïvely trying to overlay successive images…

sharp({
  create: {
    width: 512,
    height: 512,
    channels: 4,
    background: {
      r: 255,
      g: 255,
      b: 255,
      alpha: 255
    }
  }
}).png()
.overlayWith(grassImagePath, {
  top: 0,
  left: 0
})
.overlayWith(concreteImagePath, {
  top: 0,
  left: 0
})
.pipe(response)

…does not seem to work.

I’ve found a way around this by using buffers:

sharp({
  create: {
    width: 512,
    height: 512,
    channels: 4,
    background: {
      r: 255,
      g: 255,
      b: 255,
      alpha: 255
    }
  }
}).png()
.toBuffer()
.then(function(buffer) {
  sharp(buffer)
  .overlayWith(grassImagePath, {
    top: 0,
    left: 0
  })
  .toBuffer()
  .then(function(buffer) {
    sharp(buffer)
    .overlayWith(concreteImagePath, {
      top: 256,
      left: 0
    })
    .pipe(response)
  })
});

I can remove the pyramid with some asynchronous utility function, after all this will be working on an array of images, but I still feel that using buffers like this is wasteful.

So my question is: is there a way to apply successive overlays without resorting to this kind of trickery?

Issue Analytics

  • State:closed
  • Created 6 years ago
  • Comments:5 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
ghostcommented, Oct 1, 2017

Your comment on https://github.com/lovell/sharp/issues/405 seems to do things the same way I have done. Namely returning a buffer via a promise then constructing a fresh sharp instance with it.

I’m happy with this. I have (effectively):

const necessary = require('necessary');

const { asynchronousUtilities } = necessary,
      { whilst } = asynchronousUtilities;

function respond(response) {
  createImageMap(function(buffer) {
    const context = {
      paths: paths,
      buffer: buffer
    };

    whilst(overlayImageCallback, function() {
      const { buffer } = context;

      sharp(buffer).pipe(response);
    }, context);
  });
}

function createImageMap(callback) {
  const width = 512,
        height = 512,
        channels = 4,
        background = { r: 255, g: 255, b: 255, alpha: 255 },
        options = {
          width: width,
          height: height,
          channels: channels,
          background: background
          };

  sharp({
    create: options ///
  })
  .png()
  .toBuffer()
  .then(callback);
}

function overlayImageCallback(next, done, context, index) {
  const { buffer, paths } = context,
        pathsLength = paths.length,
        lastIndex = pathsLength - 1;

  if (index > lastIndex) {
    done();

    return;
  }

  const top = 0,
        left = index * 256, /// nonsense obviously
        options = {
          top: top,
          left: left
        },
        path = paths[index];

  sharp(buffer)
  .overlayWith(path, options)
  .toBuffer()
  .then(function(buffer) {
    context.buffer = buffer;  ///

    next();
  });
}

Once I’ve created the loop it’s pretty tidy. I have to update the context with the fresh buffer every time (near the bottom) but apart from that it’s fine.

Thanks for the help. Feel free to close!

0reactions
simeylacommented, Jun 26, 2018

Until #405 is complete it makes for cleaner code to do the following and use await instead of a nasty promise chain. Then when the feature is completed you can just kill off those two lines.

 sharpImage.overlayWith(.....);

 var tempBuffer = await compressedImage.toBuffer({ resolveWithObject: true });
 sharpImage = sharp(tempBuffer.data);
Read more comments on GitHub >

github_iconTop Results From Across the Web

Overlay Layers (Standard Feature Analysis)—ArcGIS Pro
Overlays the geometries from multiple layers into one single layer. Overlay can be used to combine, erase, modify, or update spatial features.
Read more >
Overlay Layers—Portal for ArcGIS
The Overlay Layers tool combines two layers into a single layer using one of five methods: Intersect, Erase, Union, Identity, or Symmetric Difference....
Read more >
Lecture 56: Buffering and Overlay Analysis in GIS - YouTube
This lecture is on buffering and overlay analysis in GIS, which is a highly significant element of GIS that is used to analyse...
Read more >
Overlaying greyscale color channels - python - Stack Overflow
... easy to do some real color blending of the two images without resorting to some hacky canvas buffer tricks that I've seen...
Read more >
Multi Criteria Overlay Analysis (QGIS3) - QGIS Tutorials and Tips
You can do the overlay analysis on vector layers using geoprocessing tools such as buffer, dissolve, difference and intersection.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found