-->

c# saving very large bitmaps as jpegs (or any othe

2020-08-22 03:29发布

问题:

I am currently handling very large images, which are basically generated by stitching together many smaller images (e.g. panorama or photo mosaic software). In order to avoid out-of-memory exceptions (in memory are only "maps" of how to arrange the smaller images), I wrote some code saving these images line by line as bitmaps using BinaryWriter and LockBits. So far, so good.

The problem is now that I would like to save these images as Jpegs (or PNGs) as well. Since I am pretty new to c# I can only think of two ways for now:

1) Similar to the bitmap saving procedure. Generating some jpeg header and saving the big images line by line, compressing them somehow before. I have no idea how to perform the compression though.

2) Streaming the already saved bitmap into the memory and saving it as encoded jpeg.

Since the second approach seemed easier, I tried something like this:

FileStream fsr =
    new FileStream("input.bmp", FileMode.Open, FileAccess.Read);
FileStream fsw =
    new FileStream("output.jpg", FileMode.CreateNew, FileAccess.Write);

EncoderParameters encoderParameters = new EncoderParameters(1);
encoderParameters.Param[0] =
    new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 80L);

Bitmap bmp = new Bitmap(fsr);
bmp.Save(fsw, GetEncoder(ImageFormat.Jpeg), encoderParameters);
bmp.Dispose();

The problem is now that the save-method tries to completely load the bitmap into memory first, causing an out-of-memory exception.

I would be more than happy for any suggestions on how to solve or circumvent this problem!

回答1:

Note that JPEG compression has some important non-trivial drawbacks :

1) JPEG is lossy, so you will not regenerate the same image. This might be perfectly acceptable, but if you need to exactly regenerate source image, then PNG is the way to go.

2) JPEG is aligned on 8-pixels boundaries. If you stich together images which sizes are not multiple of 8, either in width or height, you will get some strong artefacts at image boundaries

Considering your use case, i would rather recommend to not stitch images together, and use a standard compression algorithm, such as PNG or Zlib, on each smaller image. You can still store the resulting compressed streams into a single file. The advantage is that you can then directly "jump" at the position of the small image you want to extract, which will save * a lot * of memory.

The disadvantage is that you cannot "visually preview" all the smaller images into a big single one (you will need to create a program for this usage).



回答2:

I suspect you can't do this in .NET, but you can always call into a C library to do the heavy lifting. I haven't used it myself, but I suggest you look at the GraphicsMagick library. It's a C library with licensing that allows for both open source and commercial use and it looks like it might do what you need.



回答3:

(since title says "or any other compressed format")

TIFF format has a notion of tiles and stripes. Both can be used to avoid having all the big image in memory at any time. Maybe tiles will come in handy for you since you are stitching small images.

You might try LibTiff.Net (free, commercial-friendly, open-source, written in C# only) for the task.

There is an article Basic introduction to the capabilities of the library with information about strip- and tile-oriented image IO (in the second part of the article).

Disclaimer: I work for the company.