来自Unity3D的OpenCV dll调用导致FPS下降

我想在Unity3D中识别ArUco标记并将GameObject附加到它们的位置。我知道Asset Store中有一些软件包,但是随着其他人开始使用它,我正在寻找免费的现有解决方案或自己尝试。

ArucoUnity的检测工作像一个超级按钮,但是访问rvecstvecs的数据时Unity崩溃。当调用c ++方法Get(int i)时,在Vec3d类的au_cv_Vec3d_get(CppPtr,i,CppPtr);中的某处发生了错误

OpenCv plus Unity此实现似乎不完整,因为EstimatePoseSingleMarker没有功能或没有类似的功能来获取rvecstvecs。同样最近一次更新是在2019年1月。

由于Unity(C#)提供了访问非托管代码的可能性,因此我遵循了this很棒的教程,并且恳求我能够将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;,但是由于其他项目也必须这样做(至少我猜是这样),所以我想知道是否还有其他原因。 如果我无法解决此问题,则必须寻找替代方法。

huangchaoqi 回答:来自Unity3D的OpenCV dll调用导致FPS下降

只需添加到/建立在Mars' answer上:

对于线程问题,我实际上将使用线程节省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's answer在编程方面更为可靠,因此我将隐藏该部分。上面已经解释了基本问题,而derHugo的解决方法比我的伪代码要好得多:)

本文链接:https://www.f2er.com/3076893.html

大家都在问