7

I am looking for some advice on how to construct a very low memory image resizing program that will be run as a child process of my nodejs application in linux.

The solution I am looking for is a linux executable that will take a base64 string image (uploaded from a client) using stdin, resizing the photo to a specified size and then pumping the resulting image data back through stdout.

I've looked into image magick and it might be what I end up using, but I figured I would ask and see if anyone had a suggestion.

Suggestions of libraries or examples of pre compiled executables in C/C++ would be greatly appreciated. Also a helpful answer would include general strategies for low memory image resizing.

Thank you

2
  • What does "as low memory as possible" mean? How much memory can you afford to let it use? 100KB? 1MB? More? Less? Is it always allowed to use as much memory as the image itself takes? At some point, lowering memory usage implies compromises in performance or image quality. When is that trade-off acceptable? Commented Nov 30, 2010 at 10:49
  • 3
    I think the problem is that somebody might upload a 40000x30000 jpeg file that's just a few kb, and DoS the server by having it allocate 1.2 GB... Commented Dec 1, 2010 at 5:04

6 Answers 6

9

Depending on the image formats you want to support, it's almost surely possible to perform incremental decoding and scaling by decoding only a few lines at a time and discarding the data once you write the output. However it may require writing your own code or adapting an existing decoder library to support this kind of operation.

It's also worth noting that downsizing giant jpegs can be performed efficiently by simply skipping the high-frequency coefficients and using a smaller IDCT. For example, to decode at half width and half height, discard all but the upper-left quadrant of the coefficients (horizontal and vertical frequency < 4) and use a 4x4 IDCT on them instead of the usual 8x8. Both the libjpeg decoder and the libavcodec decoder support this operation for power-of-2 scalings (1/2, 1/4, or 1/8). This type of approach might make incremental decoding/scaling unnecessary.

You can try it out with djpeg -scale 1/4 < src.jpg | cjpeg > dest.jpg. If you want a fixed output size, you'll probably first scale by whichever of 1/2, 1/4, or 1/8 puts you closest to the desired size without going to low, then performing interpolation to go the final step, e.g. djpeg -scale 1/4 < src.jpg | convert pnm:- -scale 640x480 dest.jpg.

Sign up to request clarification or add additional context in comments.

4 Comments

Does djpeg support stdin/out?
Yes. In the examples in my answer it's using stdin and stdout. :-)
Sorry for my naiveté. I understand now. Do you think pumping bits from one process (nodejs) to another (perhaps djpeg, cjpeg) will in any way use more memory than doing that processing in the same process? I wouldn't want to do that if it will cost the system 2x the memory.
It might use more (if both processes have fully copies of the image at once, 2x) or less (if piping the data keeps the image as a whole from ever being held in memory), but probably about the same (if djpeg writes the image out as it decodes it without ever retaining it in memory, and convert loads the whole thing in memory to scale it, which is the way I think they work..)
6

When working on very large images, such as 0.25 GPix and larger, ImageMagick uses ~2 GB ram, even when using djpeg to decode the JPEG image first.

This command chain will resize JPEG images of about any size using only ~3 MB ram:

djpeg my-large.jpg | pnmscale -xysize 16000 16000 | cjpeg > scaled-large.jpg 

1 Comment

This is exactly what I did. Good call, it was easy to implement and its performing really well. I think R had the same idea though.
1

GraphicsMagick is generally a better version of ImageMagick, I'd take a look at that. If you really need something fast, you probably want to drop to something like libjpeg - while you say you want something that's non-blocking IO, the operation you want to do is relatively CPU-bound (i.e decoding the image, then trying to resize it).

1 Comment

Ya, it's all CPU/mem. Makes sense.
1

if anything this is just a sample following what he described:

import sys from PIL import Image import binascii import cStringIO x,y = sys.stdin.readline().strip().split(' ') x,y = int(x), int(y) img = Image.open(cStringIO.StringIO(binascii.b2a_base64(sys.stdin.read())).resize(x,y) img.save(sys.stdout, format="png") 

as that has to read the input, decode it, resize, and encode, and write it out there is no way to reduce the size of the memory used to less then the size of the input image

2 Comments

Keep in mind that there will be 2 processes sharing this data. I think there is a way, as R mentions, that pumping small chunks over std IO instead of the entire file, like in your example. That way the memory footprint can be smaller. Thoughts?
yes it would be but i don't think nodejs can use such an image format for the most part unless your images are huge even a 10kx10k 8bit per component RGB image is only 286.2MB, there is normally plenty of memory and if there isn't you don't handle such large images if you got a format which like jpeg you can decode a scaled down version then you should use that, but you'll probably need to scale down again if the decoded reduction is too big so you can't stream the resizing
1

Nothing can beat Intel Integrated Performance Primitives in terms of performance. If you can afford it I strongly recommend to use it.

Otherwise just implement your own resizing routine. Lanczos gives quite good results albeit it won't be tremendously fast.

Edit: I strongly suggest you NOT to use Image Magick or Graphics Magick. They are both great libraries, but designed for completely different purpose - handling many file formats, depths, pixel formats, etc. They sacrifice performance and memory effectiveness for the things I've mentioned.

2 Comments

Thanks for the answer. Am I correct in assuming IIPP gains most performance over other solutions by supporting multi-threading?
It's also vectorized, which is number 1 reason it's so fast. And yes it also supports multi-threading which increases performance even further.
0

You might need this: https://github.com/zhangyuanwei/node-images

Cross-platform image decoder(png/jpeg/gif) and encoder(png/jpeg) for Nodejs

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.