Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Uncaught RuntimeError: Aborted(OOM) when writeToBuffer #34

Closed
hurryhuang1007 opened this issue Dec 8, 2022 · 2 comments
Closed

Uncaught RuntimeError: Aborted(OOM) when writeToBuffer #34

hurryhuang1007 opened this issue Dec 8, 2022 · 2 comments
Labels
question Further information is requested

Comments

@hurryhuang1007
Copy link

Hello, I want to use this library to complete the following tasks: combine multiple small images into one large image in the browser and download it to the user's local.
So I write a demo, here are some main code snippets:

const urls = [have 1000 urls]
console.log('urls.length', urls.length)

const WIDTH = 1000
const HEIGHT = 720

const colNum = ~~Math.sqrt(urls.length)
const rowNum = ~~(urls.length / colNum)

window.startMerge = async () => {
  const images = await Promise.all(
    urls.map(async (url, index) => {
      return vips.Image.newFromBuffer(await fetch(url).then(res => res.arrayBuffer()))
    })
  )
  // merge images
  let mergeImage = await vips.Image.black(WIDTH * colNum, HEIGHT * rowNum)
  for (let i = 0; i < rowNum; i++) {
    for (let j = 0; j < colNum; j++) {
      const image = images[i * colNum + j]
      mergeImage = await mergeImage.insert(image, j * WIDTH, i * HEIGHT)
      // dispose image
      image.delete()
    }
  }
  console.log('mergeImage', mergeImage)

  const buffer = await mergeImage.writeToBuffer('.jpg') // OOM happened
  // const blob = new Blob([buffer], { type: 'image/jpeg' })
  // const url = URL.createObjectURL(blob)
  // const img = document.createElement('img')
  // img.src = url
  // img.style.position = 'fixed'
  // img.style.top = '0'
  // img.style.left = '0'
  // img.style.zIndex = '9999'
  // document.body.appendChild(img)
}

Does this mean that large files need to be streamed out? Instead of the overall output as it is now.

@kleisauke kleisauke added the question Further information is requested label Dec 13, 2022
@kleisauke
Copy link
Owner

The issue with image.insert() is that libvips has to recurse very deeply when it evaluates the pipeline containing a large amount of images, causing an OOM error or a stack overflow.

If your images are a regular grid, you might consider using vips.Image.arrayjoin() instead, which would be faster and use much less memory. For example:

const urls = [...];

const images = await Promise.all(
  urls.map(async url => 
    vips.Image.newFromBuffer(await fetch(url).then(res => res.arrayBuffer()), '', {
      access: vips.Access.sequential // 'sequential'
    })
  )
);

const im = vips.Image.arrayjoin(images, {
  across: Math.floor(Math.sqrt(urls.length)), // number of images per row
  shim: 10, // space between images, in pixels
  background: [0, 0, 0] // background colour
});

const buffer = im.writeToBuffer('.jpg');

@kleisauke
Copy link
Owner

I hope this information helped. Please feel free to re-open if questions remain.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants