I was adding JP2 support to a server app I'd written, so I'd put together a .NET wrapper class for the openJPEG JPEG2k CODEC.
I was testing it on some images from my Canon 5D2, and it worked great doing one at a time. However, in production it was going to have to parallelize the work because it would be doing thousands of images in a batch.
So I converted the code from:
To
The first worked great. The 2nd imploded everytime; and if I changed it to 2 threads it imploded some of the time.
It took me most of a day of tinkering to work out what had happened: I'd run out of 32-bit address space, and the library was failing with an Out of Memory situation, and on occasion it was taking the .NET framework with it.
I'd essentially wrapped the whole library, and I hadn't even considered the implication of holding 20 Mpix images in 24bpp RAW format in RAM, then copying the data to a 96bpp buffer for compression, the need for internal CODEC working-space equal to the size of the frame buffer, plus memory sufficient to hold the compressed codestream. For reference, the working set needed to compress 1 image is about 700 MB.
The other issue was that .NET really discourages you from writing to "unmanaged" memory. My first go at the wrapper, allocated a 96bpp managed buffer, and then copied it to the CODEC's 96bpp input buffer. That way round, the working set was over 1 GB per image!
I was testing it on some images from my Canon 5D2, and it worked great doing one at a time. However, in production it was going to have to parallelize the work because it would be doing thousands of images in a batch.
So I converted the code from:
Code:
foreach (BatchInfo bi in BatchList) {
using (Bitmap bmp = (Bitmap) Bitmap.FromFile (bi.sourceFile) ) {
JP2.CompressToFile(cParams, bmp, bi.destFile); }
}
To
Code:
ParallelOptions pOpt = new ParallelOptions();
pOpt.MaxDegreeOfParallelism = 4;
Parallel.ForEach (BatchList, pOpt, (bi) => {
using (Bitmap bmp = (Bitmap) Bitmap.FromFile (bi.sourceFile)) {
OPJWrapper.CompressToFile(cParams, bmp, bi.destFile, OPJCODECType.JP2); }
});
The first worked great. The 2nd imploded everytime; and if I changed it to 2 threads it imploded some of the time.
It took me most of a day of tinkering to work out what had happened: I'd run out of 32-bit address space, and the library was failing with an Out of Memory situation, and on occasion it was taking the .NET framework with it.
I'd essentially wrapped the whole library, and I hadn't even considered the implication of holding 20 Mpix images in 24bpp RAW format in RAM, then copying the data to a 96bpp buffer for compression, the need for internal CODEC working-space equal to the size of the frame buffer, plus memory sufficient to hold the compressed codestream. For reference, the working set needed to compress 1 image is about 700 MB.
The other issue was that .NET really discourages you from writing to "unmanaged" memory. My first go at the wrapper, allocated a 96bpp managed buffer, and then copied it to the CODEC's 96bpp input buffer. That way round, the working set was over 1 GB per image!
Last edited: