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

ArucoUnity检测工作就像一个 super 按钮,但是访问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;第一行,但是由于其他项目也必须这样做(至少我猜是这样),所以我想知道是否还有其他原因。
如果我无法解决此问题,则必须寻找替代方法。

最佳答案

只需添加到/建立在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
            }
        }
    }
    

    08-17 14:53