Skip to content

Instantly share code, notes, and snippets.

@matthewrdev
Created April 5, 2023 04:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save matthewrdev/53d02656639bc931d20d8fc37fad5edf to your computer and use it in GitHub Desktop.
Save matthewrdev/53d02656639bc931d20d8fc37fad5edf to your computer and use it in GitHub Desktop.
Copies an OpenCV Mat frame from BGR format into an RGBA buffer.
using System;
using System.Diagnostics;
using OpenCvSharp;
namespace MyApp
{
public static class VideoFrameDecoderHelper
{
/// <summary>
/// Performs a pointer arithmetic based decoding operation from the <paramref name="frame"/> into the frame buffer.
/// <para/>
/// Assumes that the <paramref name="frameBuffer"/> is initialised and at the correct size.
/// </summary>
/// <param name="frame"></param>
/// <param name="frameBuffer"></param>
/// <exception cref="ArgumentNullException"></exception>
public static void DecodeInto(Mat frame, ref byte[] frameBuffer)
{
if (frame is null)
{
throw new ArgumentNullException(nameof(frame));
}
if (frameBuffer is null)
{
throw new ArgumentNullException(nameof(frameBuffer));
}
var imageHeight = frame.Rows;
var imageWidth = frame.Cols;
const int destinationBytesPerPixel = 4; // Output frame pixels are in RGBA format.
const int sourceBytesPerPixel = 3; // OpenCV frame pixels are in BGR format.
var imageSizeInBytes = imageHeight * imageWidth * destinationBytesPerPixel;
long sourceBytesPerRow = frame.Step();
long destinationBytesPerRow = imageWidth * destinationBytesPerPixel;
// TODO: Pixel type detection?
byte r, g, b, a;
a = 0;
unsafe
{
// The following code block performs a buffer copy of the open CV frame using pointer dereferencing (rather than C#'s array derefencing operator).
// This code AOT compiles in release mode and will run 30x-60x faster than in debug mode.
// Debug mode may be 50-60ms, release may be between 1-2ms.
fixed (byte* destinationFramePointer = &frameBuffer[0])
{
byte* sourceFramePointer = frame.DataPointer;
for (int currRow = 0; currRow < imageHeight; currRow++)
{
long currRowOffset = sourceBytesPerRow * currRow;
long sourceRowOffset = sourceBytesPerRow * currRow;
long destinationRowOffset = destinationBytesPerRow * currRow;
for (int currCol = 0; currCol < imageWidth; currCol++)
{
b = *(sourceFramePointer + sourceRowOffset + (sourceBytesPerPixel * currCol));
g = *(sourceFramePointer + sourceRowOffset + (sourceBytesPerPixel * currCol + 1));
r = *(sourceFramePointer + sourceRowOffset + (sourceBytesPerPixel * currCol + 2));
a = 0;
*(destinationFramePointer + destinationRowOffset + (destinationBytesPerPixel * currCol)) = r;
*(destinationFramePointer + destinationRowOffset + (destinationBytesPerPixel * currCol + 1)) = g;
*(destinationFramePointer + destinationRowOffset + (destinationBytesPerPixel * currCol + 2)) = b;
*(destinationFramePointer + destinationRowOffset + (destinationBytesPerPixel * currCol + 3)) = a;
}
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment