SaveLossyImage and threading

In an attempt to speed up a recording application that uses SaveLossyImage, I use a task to do the saving. However, this does not have the expected result, still I cannot get to the same speeds as when saving .bmps. Am I overlooking something? Is there some way to get the AxCVImage object to offload the compression to another thread, so as not to block the main image processing.

Any suggestions?

1 Like

Unfortunately thread-safety of the serialization of image files cannot be guaranteed as this is not something that not all of the underlying libraries offer. For example jpeg relies on the libjpg which is expressly not thread safe. Is lossy compression a requirement (e.g. due to disc space limitations)? Because if it isn’t: By and large the fastest way to store :cvb: images to files is the *.bmp format…

1 Like

I had indeed noticed that the storing of .bmp images is orders of magnitude faster. But yeah, the required storage capacity quickly gets out of hand with .bmps.

An alternative would be to save the images to an .avi-file using H.264 compression with Movie2 from :cvb:.
If this is an option for you, have a look at the Movie2 Example at %CVB%Tutorial\Movie2
A licence for this encoder can be ordered from Stemmer Imaging.
Encoding the video can also be speeded up with hardware acceleration (Intel Quicksync / Nvidia Cuda) and by adjusting the quality factor of the encoder (1 - 50, where 1 is the best quality). We tested the compression of H.264 with 8Bit images and archived following compression rates:

  • Quality factor 10: 10:1
  • Quality factor 16: 50:1
  • Quality factor 22: 200:1

At which resolution/framerate are you recording?

7600*3500, 2 herz per camera, 2 cameras. SaveLossyImage takes somewhere around 500-600(!!) ms per image.

Alright since H.264 only supports 4096x4096 MAX, the only alternative is H.265.
We haven’t tested that yet, but according to the manufacturer, H.265 supports videos up to 8K resolution.

Hmm, It seems that it’s not actually the compression that is the culprit. Using the bitmap.save function from windows, saving images of that size takes about 100ms. Odd!

Ok, so using the tutorial sample c# CSIMG2Bitmap, and calling the standard windows codec it’s possible to get this to run in multiple threads. The suprising thing is that it wasn’t actually the jpeg encoding that was the problem for these big images, the conversion from cvImage to Bmp was the bottleneck.

Hi @CvK,

we’d have to have a detailed look, but what you write seems consistent with the measurements I’ve done for this post. With your roughly 26 MPixel images I’d expect roughly 200 ms on my machine for saving these (haven’t tested so far), which is somewhat more than the 100ms you’re measuring.

Saving bitmap files internally uses C++'s std::fstream for input/output. On VC these are unfortunaly slower than the C methods fopen and its cousins (interestingly, this doesn’t seem to be the case for the gcc) which is probably your limiting factor for BMP writing - at least in those scenarios where the memory layout allows for direct streaming. As soon as the memory has to be reordered, reordering becomes the bottleneck.

If copying from the :cvb: image to the System.Drawing.Bitmap now is your bottleneck there is in fact a way for your code to become faster: Even in the case of linear VPATs the CSIMG2Bitmap tutorial copies pixel by pixel. However, there are scenarios where you can get a lot faster by copying entire lines or even the whole block of pixels (basically the x increment has to equal the pixel’s size and the y increment has to equal the target bitmap’s stride for this to be possible - so you’ll need to check the return values of GetLinearAccess for compliance with these requirements). Copying blocks of unmanaged memory in C# requires some additional DllImport magic but it’s feasible and I’d expect the copying of the pixel data to become faster faster by a factor of somewhere between 2 and 3 if you can at least do it line by line.

2 Likes

Handing off the conversion to different tasks was good enough for the current application. I always get this sense of foreboding dread whenever I read the words “Copying blocks of unmanaged memory in c#”. Anyhow, it works now, and thanks for providing the extra elaboration / extra credit approach.

With the threading approach I managed to get about 3 fps for 2 rgb cameras, so not too shabby I’d say! If later it turns out that we need a performance boost I’ll venture into the lands of unmanaged memory copying. If I’m not back in three days… send rescue parties :wink:

1 Like

:scream:
We’re talking RGB here? Bloody h…, so you’re streaming about 450 MB/s to jpg? No worries, we’ll send Chuck Norris for you :+1:

1 Like

Hmm, looking back on the formulation of my question. I perhaps should’ve mentioned the “detail” about it being RGB.