Sharp: How to generate image from stream of raw input?

Created on 16 Feb 2019  路  7Comments  路  Source: lovell/sharp

I am trying to create an image (ideally to be output as a png) from raw pixel data, but I'm struggling to understand the desired format

const rs = Readable();
  const transformer = sharp({
    raw: {
      width,
      height,
      channels: 3,
    },
  });
  rs.pipe(transformer).pipe(createWriteStream(outputPath));

I have tried setting _read on rs to push Uint8Arrays, Buffers, and other types representing RGB data, but I inevitably get the Error: Unexpected data on Writable Stream error

I'm stumped, any help would be greatly appreciated

bug ready-to-ship

Most helpful comment

v0.22.0 now available.

All 7 comments

Hello, this approach should be supported but sadly, as you point out, it looks like it isn't currently working. There are no unit tests for stream-based input of raw pixel data so this could be a regression, oops.

Luckily a small rejig of the logic relating to raw input in _createInputDescriptor should fix this. Happy to accept/help with a PR if you're able.

Thanks for the quick reply, I鈥檒l start working on it today

@lovell Hey, I've got sharp building locally, tests pass and all that, ready to take a stab at this

But I'm a stream noob and I'm not sure how this change should go in

  1. Do we need a new branch in _createInputDescriptor with new functionality in is to check for a readable stream or is the intended input a buffer (coming from the stream)?
  2. If we are expecting a buffer, do we need to set this.options.input.buffer on instantiation? It seems to be referred to often in the path that's currently followed, but I'm unclear on how and where it's set in this case
  3. Again assuming that we're passing buffers through the stream to sharp, what should be in the buffers? I was guessing it would be Uint8Arrays like [0, 0, 0] (for three channels), but again, I have no idea what the intention is

Fantastic, thanks for looking into this.

When Stream-based input is expected, inputDescriptor.buffer becomes an array ready for chunks to be pushed to in _write() - there's an internal _isStreamInput() helper function to test for this.

There's probably scope for simplification (and definitely a need for testing) but something like the following might work:

  } else if (!is.defined(input) && is.object(containerOptions) && containerOptions.allowStream) {
    // Stream
    inputDescriptor.buffer = [];
+ } else if (is.object(input) && is.object(input.raw) && is.object(containerOptions) && containerOptions.allowStream) {
+    // Raw Stream
+    inputDescriptor.buffer = [];
  } else {
    throw new Error('Unsupported input ' + typeof input);

For anyone having trouble loading a raw stream, you can can do it by using null in the constructor.

Example:

const pipeline = sharp(null, { raw }).jpeg();
inStream.pipe(pipeline).pipe(outStream);

Commit 83cdb55 adds a test for this, plus fixes the problem.

This will be in v0.22.0 - thanks for reporting!

v0.22.0 now available.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zilions picture zilions  路  3Comments

Andresmag picture Andresmag  路  3Comments

henbenla picture henbenla  路  3Comments

natural-law picture natural-law  路  3Comments

iq-dot picture iq-dot  路  3Comments