kleisauke / wasm-vips

libvips for the browser and Node.js, compiled to WebAssembly with Emscripten.
https://kleisauke.github.io/wasm-vips/
MIT License
485 stars 25 forks source link

High Memory Usage with wasm-vips Image Resizing #75

Open jimzer opened 2 hours ago

jimzer commented 2 hours ago

High Memory Usage with wasm-vips Image Resizing

Praise for wasm-vips

First, I want to express my appreciation for the wasm-vips library. It's been a game-changer for my project. The ease of installation and setup in Docker is remarkable, especially compared to other image processing libraries I've tried. The performance and functionality are excellent, and it's made image processing in my web application significantly smoother. Thank you for creating such a powerful and user-friendly tool!

Description

While using wasm-vips in my project, I've encountered an issue with high memory usage when resizing images in my Node.js server. Despite the overall great performance, the server's memory consumption increases significantly and doesn't fully recover after processing images.

Current Behavior

Code Sample

import Vips from "wasm-vips";
// ... (other imports)

const vips = await Vips();
vips.config();

// ... (rest of the server setup)

.get(
    "/images/static/:uuid",
    zValidator(
        "query",
        z.object({
            w: z.coerce.number().int(),
            q: z.coerce.number().int().min(1).max(100).default(90),
        }),
    ),
    cache({
        cacheName: "my-app",
        cacheControl: "max-age=3600",
    }),
    async (c) => {
        const { w, q } = c.req.valid("query");
        const uuid = c.req.param("uuid");
        const media = (
            await db.select().from(medias).where(eq(medias.uuid, uuid))
        )?.[0];
        if (!media) {
            throw new HTTPException(404, { message: "Media not found" });
        }
        const blob = (
            await downloadMultipleFiles(SETTINGS.r2.bucket, [media.uuid])
        )?.[0];
        const processedImage = vips.Image.newFromBuffer(blob.content)
            .thumbnailImage(w)
            .writeToBuffer(".webp", { Q: q });

        c.header("Content-Type", "image/webp");
        return c.body(processedImage);
    },
)

Questions

  1. Am I using wasm-vips correctly for image resizing?
  2. Are there any settings or best practices I can apply to mitigate this high memory usage?
  3. Is this level of memory consumption expected when using wasm-vips for image processing?

Additional Information

Possible Solutions Tried

I would greatly appreciate any insights or suggestions on how to reduce memory consumption while still leveraging wasm-vips for image processing. Thank you again for this fantastic library, and for any help you can provide with this issue!

kleisauke commented 2 hours ago

Did you see https://github.com/kleisauke/wasm-vips/issues/13#issuecomment-1073246828? Also, please avoid using thumbnailImage, if possible. It can't do any of the shrink-on-load tricks, so it's significantly slower.

@@ -31,9 +31,9 @@ vips.config();
     const blob = (
       await downloadMultipleFiles(SETTINGS.r2.bucket, [media.uuid])
     )?.[0];
-    const processedImage = vips.Image.newFromBuffer(blob.content)
-      .thumbnailImage(w)
-      .writeToBuffer(".webp", { Q: q });
+    const im = vips.Image.thumbnailBuffer(blob.content, w);
+    const processedImage = im.writeToBuffer(".webp", { Q: q });
+    im.delete();

     c.header("Content-Type", "image/webp");
     return c.body(processedImage);

Are you processing many different images? If so, you could consider disabling the operation cache via vips.Cache.max(0), see: https://github.com/libvips/libvips/blob/master/doc/Developer-checklist.md#disable-the-libvips-operation-cache-if-you-dont-need-it

kleisauke commented 2 hours ago

Note that I would recommend just using sharp if you only want to target Node.js. It also comes with a WebAssembly build these days, see: https://github.com/kleisauke/wasm-vips/issues/43.