我想在Unity3D中识别ArUco标记并将GameObject附加到它们的位置。我知道 Assets 商店中有软件包,但是随着其他人开始使用它,我正在寻找免费的现有解决方案或自己尝试。
ArucoUnity检测工作就像一个 super 按钮,但是访问rvecs
,tvecs
的数据时Unity崩溃。当调用c++方法Get(int i)
时,在Vec3d
类的au_cv_Vec3d_get(CppPtr, i, CppPtr);
中发生错误。
OpenCv plus Unity此实现似乎不完整,因为没有EstimatePoseSingleMarker
或类似的函数来获取rvecs
和tvecs
。另外最后一次更新是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
}
}
}