Unity3D的OpenCV dll调用导致FPS下降

OpenCV dll calls from Unity3D lead to FPS drop

本文关键字:FPS 下降 调用 OpenCV dll Unity3D      更新时间:2023-10-16

我想在Unity3D中识别ArUco标记,并在它们的位置上附加一个GameObject。我知道资产商店里有软件包,但当其他人使用它时,我正在寻找一个免费的现有解决方案,或者自己尝试。

ArucoUnity检测效果很好,但Unity在访问rvecstvecs的数据时崩溃。当c++方法au_cv_Vec3d_get(CppPtr, i, CppPtr);被称为时,错误发生在Vec3d类的Get(int i)中的某个位置

OpenCv加Unity这个实现似乎并不完整,因为不存在EstimatePoseSingleMarker或类似的函数来获得rvecstvecs。最后一次更新也是在2019年1月。

由于Unity(C#(提供了访问非托管代码的可能性,我遵循了这篇很棒的教程,并将cv::VideoCaputre流转发给了Unity。唯一出现的问题是FPS下降到35-40左右,而通常我会达到90-100左右。

C#代码:

void Update()
{
MatToTexture2D();
image.texture = tex;
}
void MatToTexture2D()
{
OpenCVInterop.GetRawImageBytes(pixelPtr);
//Update the Texture2D with array updated in C++
tex.SetPixels32(pixel32);
tex.Apply();
}

C++代码:

extern "C" void __declspec(dllexport) __stdcall  GetRawImageBytes(unsigned char* data)
{
_capture >> _currentFrame;
cv::Mat resizedMat(camHeight, camWidth, _currentFrame.type());
cv::resize(_currentFrame, resizedMat, resizedMat.size(), cv::INTER_CUBIC);
//Convert from RGB to ARGB 
cv::Mat argb_img;
cv::cvtColor(resizedMat, argb_img, cv::COLOR_BGR2BGRA);
std::vector<cv::Mat> bgra;
cv::split(argb_img, bgra);
std::swap(bgra[0], bgra[3]);
std::swap(bgra[1], bgra[2]);
std::memcpy(data, argb_img.data, argb_img.total() * argb_img.elemSize());
}

原因似乎是第一行_capture >> _currentFrame;,但由于其他项目也必须这样做(至少我想是这样(,我想知道是否还有其他原因。如果我不能设法解决这个问题,我必须寻找其他方法。

只是添加到火星上/在火星上建造的答案:

对于线程问题,我实际上会使用线程保存ConcurrentStack<Color32[]>。堆栈是"后进|先出"的,因此返回的第一个项始终是线程添加的最后一个图像数据。

  • 线程使用Push(pixel32)添加带有图像数据的新条目
  • Update中,仅使用最新条目(TryPop(来更新纹理
  • 您忽略的其余部分(Clear(

所以类似的东西

// the OpenCV thread will add(push) entries
// the Unity main thread will work on the entries
private ConcurrentStack<Color32[]> stack = new ConcurrentStack<Color32[]>();
public RawImage image;
public Texture2D tex;
private Thread thread;
void Start()
{
// Wherever you get your tex from
tex = new Texture2D(...);
// it should be enough to do this only once
// the texture stays the same, you only update its content
image.texture = tex;
}
// do things in OnEnable so everytime the object gets enabled start the thread
void OnEnable()
{
stack.Clear();
if(thread != null)
{
thread.Abort();
}
thread = new Thread(MatToTexture2D);
thread.Start();
}
void Update()
{
// here in the main thread work the stack
if (stack.TryPop(out var pixels32))
{
// Only use SetPixels and Apply when really needed
tex.SetPixels32(pixels32);
tex.Apply();
}
// Erase older data
stack.Clear();
}
// Make sure to terminate the thread everytime this object gets disabled
private void OnDisable()
{
if(thread == null) return;
thread.Abort();
thread = null;
}
// Runs in a thread!
void MatToTexture2D()
{
while(true)
{
try
{
// Do what you already have
OpenCVInterop.GetRawImageBytes(pixelPtr);
// However you convert the pixelPtr into Color32
Color32[] pixel32 = GetColorArrayFromPtr(pixelPtr);
// Now add this data to the stack
stack.Push(pixel32);
}
catch (ThreadAbortException ex) 
{ 
// This exception is thrown when calling Abort on the thread
// -> ignore the exception since it is produced on purpose
} 
}
}

如果我没有记错的话,用于获取图像(_capture >> _currentFrame;(的C++调用是阻塞/同步的,这意味着代码在实际检索图像之前不会继续。您可能希望异步运行MatToTexture2D代码。※这意味着你的帧速率将高于你的图像检索率。

根据需要连续运行MatToTexture2D函数,更新tex。然后继续将纹理设置为最新的tex,这可能是连续2-3帧的相同值。


编辑:@对于编程方面,derHugo的答案要可靠得多,所以我将隐藏这一部分。上面解释了基本问题,derHugo的工作比我的伪代码好得多:(