How to improve the image FPS rate. Now is single digit of FPS. It doesn’t seems normal with current refresh rate though.
Code explaination:
Backend threads calling c++/cli functions below and event trigger back on UI C# functions, the images is pre-allocated so can test performance of WPF:
Main idea is to use re-use and update to WriteableBitmap backbuffer to reduce overhead of memory allocation, to improve display FPS.
<code>void CppCli::UpdatePointer2Display() {
while (running) {
int stride, rows, cols, bufferSize;
stride = cvObj->images[imageIndex].step[0];
rows = cvObj->images[imageIndex].rows;
cols = cvObj->images[imageIndex].cols;
bufferSize = cvObj->images[imageIndex].total() * cvObj->images[imageIndex].elemSize();
pin_ptr<Byte> pinnedBuffer = cvObj->images[imageIndex].ptr();
int curFrameRate = frameRate->CalculateFrameRate();
ImageUpdated_2(this, gcnew ImageUpdateEventArgs_2(IntPtr(pinnedBuffer), stride, rows, cols, bufferSize, curFrameRate));
imageIndex++;
if (imageIndex >= cvObj->images.size())
{
imageIndex = 0;
}
frameRate->IncrementFrameCount();
}
}
</code>
<code>void CppCli::UpdatePointer2Display() {
while (running) {
int stride, rows, cols, bufferSize;
stride = cvObj->images[imageIndex].step[0];
rows = cvObj->images[imageIndex].rows;
cols = cvObj->images[imageIndex].cols;
bufferSize = cvObj->images[imageIndex].total() * cvObj->images[imageIndex].elemSize();
pin_ptr<Byte> pinnedBuffer = cvObj->images[imageIndex].ptr();
int curFrameRate = frameRate->CalculateFrameRate();
ImageUpdated_2(this, gcnew ImageUpdateEventArgs_2(IntPtr(pinnedBuffer), stride, rows, cols, bufferSize, curFrameRate));
imageIndex++;
if (imageIndex >= cvObj->images.size())
{
imageIndex = 0;
}
frameRate->IncrementFrameCount();
}
}
</code>
void CppCli::UpdatePointer2Display() {
while (running) {
int stride, rows, cols, bufferSize;
stride = cvObj->images[imageIndex].step[0];
rows = cvObj->images[imageIndex].rows;
cols = cvObj->images[imageIndex].cols;
bufferSize = cvObj->images[imageIndex].total() * cvObj->images[imageIndex].elemSize();
pin_ptr<Byte> pinnedBuffer = cvObj->images[imageIndex].ptr();
int curFrameRate = frameRate->CalculateFrameRate();
ImageUpdated_2(this, gcnew ImageUpdateEventArgs_2(IntPtr(pinnedBuffer), stride, rows, cols, bufferSize, curFrameRate));
imageIndex++;
if (imageIndex >= cvObj->images.size())
{
imageIndex = 0;
}
frameRate->IncrementFrameCount();
}
}
Code below is UI C# side:
<code>private void Cli_ImageUpdated_2(object sender, ImageUpdateEventArgs_2 e)
{
Dispatcher.Invoke(() =>
{
// initialize first if different size or haven't
if (bitmap == null || bitmap.Width != e.cols || bitmap.Height != e.rows)
{
bitmap = new WriteableBitmap(e.cols, e.rows, 96, 96, PixelFormats.Bgra32, null);
MyImageControl.Source = bitmap;
}
// for copy and update to WriteableBitmap
bitmap.Lock();
try
{
unsafe
{
byte* displayBuffer = (byte*)bitmap.BackBuffer.ToPointer();
IntPtr nativeBuffer = (IntPtr)e.imgPtr;
byte* sourceImage = (byte*)nativeBuffer.ToPointer();
int sourceStride = e.stride;
int targetStride = bitmap.BackBufferStride;
for (int y = 0; y < e.rows; y++)
{
byte* srcRow = sourceImage + (y * sourceStride);
byte* destRow = displayBuffer + (y * targetStride);
for (int x = 0; x < (e.cols * 4); x++)
{
destRow[x] = srcRow[x];
}
}
}
bitmap.AddDirtyRect(new Int32Rect(0, 0, e.cols, e.rows));
}
finally
{
bitmap.Unlock();
}
FrameRate.Text = e.frameRate.ToString();
});
}
</code>
<code>private void Cli_ImageUpdated_2(object sender, ImageUpdateEventArgs_2 e)
{
Dispatcher.Invoke(() =>
{
// initialize first if different size or haven't
if (bitmap == null || bitmap.Width != e.cols || bitmap.Height != e.rows)
{
bitmap = new WriteableBitmap(e.cols, e.rows, 96, 96, PixelFormats.Bgra32, null);
MyImageControl.Source = bitmap;
}
// for copy and update to WriteableBitmap
bitmap.Lock();
try
{
unsafe
{
byte* displayBuffer = (byte*)bitmap.BackBuffer.ToPointer();
IntPtr nativeBuffer = (IntPtr)e.imgPtr;
byte* sourceImage = (byte*)nativeBuffer.ToPointer();
int sourceStride = e.stride;
int targetStride = bitmap.BackBufferStride;
for (int y = 0; y < e.rows; y++)
{
byte* srcRow = sourceImage + (y * sourceStride);
byte* destRow = displayBuffer + (y * targetStride);
for (int x = 0; x < (e.cols * 4); x++)
{
destRow[x] = srcRow[x];
}
}
}
bitmap.AddDirtyRect(new Int32Rect(0, 0, e.cols, e.rows));
}
finally
{
bitmap.Unlock();
}
FrameRate.Text = e.frameRate.ToString();
});
}
</code>
private void Cli_ImageUpdated_2(object sender, ImageUpdateEventArgs_2 e)
{
Dispatcher.Invoke(() =>
{
// initialize first if different size or haven't
if (bitmap == null || bitmap.Width != e.cols || bitmap.Height != e.rows)
{
bitmap = new WriteableBitmap(e.cols, e.rows, 96, 96, PixelFormats.Bgra32, null);
MyImageControl.Source = bitmap;
}
// for copy and update to WriteableBitmap
bitmap.Lock();
try
{
unsafe
{
byte* displayBuffer = (byte*)bitmap.BackBuffer.ToPointer();
IntPtr nativeBuffer = (IntPtr)e.imgPtr;
byte* sourceImage = (byte*)nativeBuffer.ToPointer();
int sourceStride = e.stride;
int targetStride = bitmap.BackBufferStride;
for (int y = 0; y < e.rows; y++)
{
byte* srcRow = sourceImage + (y * sourceStride);
byte* destRow = displayBuffer + (y * targetStride);
for (int x = 0; x < (e.cols * 4); x++)
{
destRow[x] = srcRow[x];
}
}
}
bitmap.AddDirtyRect(new Int32Rect(0, 0, e.cols, e.rows));
}
finally
{
bitmap.Unlock();
}
FrameRate.Text = e.frameRate.ToString();
});
}
With 25MB images it’s only a single digit FPS.
Tested with new bitmapSource passed to UI, which obviously not an options.