在 Unity 3D 中使用 EmguCV 进行图像拼接

时间:2021-03-10 14:47:07

标签: unity3d parallel-processing emgucv image-stitching

我希望在 Unity 中使用 EmguCV 插件在运行时实现全景图像拼接,针对移动设备。到目前为止,我可以使用高级 Stitcher API(Stitcher.stitch 方法),但是当源图像太大或太多时,该过程非常耗费资源。 我试过的:

  • 我基本上使用了手机相机拍摄的 22 张图像 (每个分辨率约为 2220x180)
  • 我成功地在一个单独的线程中实现了拼接(使用 Unity Jobs 系统),但还是太重/耗时太长/ 不稳定。我正在使用 OpenCL 加速,这有助于 速度,但在内存方面似乎有点不稳定(偶尔 在随后的尝试中崩溃)。
  • 我想我会在每对一双后缝合 图像,而不是将它们全部加载到拼接器之后 它们都被占用了,但这引入了左侧的已知问题 中间结果逐渐模糊(由于
    结果混合),因此拼接器无法拼接更多图像
    在某个时候不再。
  • 我知道在 OpenCV 中我可以传递可选的 ROI 输入图像,这可能会减少处理工作量 和不想要的模糊效果,但我无法完成这项工作 使用 EmguCV(Stitcher.Stitch 没有这种超载 具有可选 ROIS 的方法)。我尝试将 ROI 应用于输入 Mats 在将它们传递给 Stich 调用之前,但这并没有 似乎也可以工作(使用 IntPtr 和 ROI 从 现有的 Mat 只允许设置单个 ROI,而在此 在这种情况下,为每个源 Mat/Image 设置 2 个 ROI 是有意义的)。
  • 我也尝试先拍摄所有图像,然后递归分离 它们成对并单独缝合每一对,依此类推 似乎工作得很好(使用自适应减少 每次下一次迭代的 PanoConfidenceThreshold 进行模糊处理 考虑在内),但遗憾的是它在第三次迭代后崩溃了(当时 点我有 5 个中间拼接结果,由原件制成 22 幅图像)。我在这里没有想法了。我想试试 使用 EstimateTransform 然后 ComposePano 的其他 Stitcher API,如 它似乎有一个重载,允许我传递一些图像蒙版 随着输入图像,但坦率地说,我不明白应该如何 我为它们指定这些为所需的格式 IArrayOfArrays??它们毕竟不是投资回报率吗?

但我仍然不确定如何正确使用并行化:/

这是我的并行作业尝试代码(Unity C#):

public struct ParallelStitchingJob : IJobParallelFor {
            public NativeArray<Vector2Int> outputSize;
            public NativeArray<IntPtr> outputData;
            public NativeArray<int> outputStep;
            public NativeArray<bool> failure;

            [ReadOnly]
            public NativeList<IntPtr> inputData;

            [ReadOnly]
            public NativeList<Vector2Int> inputSize;

            [ReadOnly]
            public NativeList<int> inputStep;

            public float panoConfidenceThreshold;

            public int iteration;

            public void Execute (int outputIndex) {
                bool jobFailed = false;
                int inputIndex = outputIndex * 2;
                int imgsCount = outputIndex == outputData.Length - 1 ? (inputData.Length % 2 == 1 ? 3 : 2) : 2;
                //Debug.LogFormat ("Partial stitching job started (output index = {0}, input index = {1}, input data length = {3}, images count = {2})", outputIndex, inputIndex, imgsCount, inputData.Length);            

                Mat[] srcMatArray = new Mat[imgsCount];
                Mat result = new Mat ();

                try {
                    for (int i = 0; i < imgsCount; i++) {
                        Mat m = createIntermediateMatFromData (inputData[inputIndex + i], inputSize[inputIndex + i], inputStep[inputIndex + i]);
                        srcMatArray[i] = m;
                        // Debug.LogFormat("input Mat[{2}] (width: {0}, height: {1})", m.Cols, m.Rows, i);
                        /* if(Application.isEditor){
                            SaveIntermediateMatToFile (m, "StitchingPair" + inputIndex + "_img" + i);
                        } */
                    }
                } catch (Exception e) {
                    jobFailed = true;
                    Debug.LogWarningFormat ("Error creating source images for prallel stitching job. {0}", e.Message);
                    result.Dispose ();
                }

                if (!jobFailed) {
                    using (Stitcher stitcher = new Stitcher (Mode.Panorama))
                    using (VectorOfMat vms = new VectorOfMat (srcMatArray)) {
                        stitcher.PanoConfidenceThresh = panoConfidenceThreshold;

                        Status status = stitcher.Stitch (vms, result);

                        if (status == Status.Ok) {
                            outputSize[outputIndex] = new Vector2Int (result.Cols, result.Rows);
                            outputData[outputIndex] = result.DataPointer;
                            outputStep[outputIndex] = result.Step;
                        } else {
                            //Debug.LogWarningFormat ("Hierarchical stitching job failed (output index = {0}, status = {1})", outputIndex, status);
                            jobFailed = true;
                            result.Dispose ();
                        }
                    }
                }

                failure[outputIndex] = jobFailed;

                foreach (Mat m in srcMatArray) {
                    m.Dispose ();
                }

                /* if (!jobFailed && Application.isEditor) {
                    string output = string.Format ("Intermediate stitching output (iteration {0}, output index {1}", iteration, outputIndex);
                    SaveIntermediateMatToFile (result, output);
                } */
            }
        }

这是调用它的 Unity 协程:

IEnumerator parallelStitchingCR (List<Texture2D> sourceImages, StitchingFinishedDelegate handler) {
            CvInvoke.UseOpenCL = UseOpenCL;
            CvInvoke.UseOptimized = UseOptimized;

            bool failure = false;

            NativeList<IntPtr> inputData = new NativeList<IntPtr> (Allocator.Persistent);
            NativeList<Vector2Int> inputSize = new NativeList<Vector2Int> (Allocator.Persistent);
            NativeList<int> inputStep = new NativeList<int> (Allocator.Persistent);

            Mat[] initialMatArray = new Mat[sourceImages.Count];

            try {
                for (int i = 0; i < sourceImages.Count; i++) {
                    Texture2D tex = sourceImages[i];
                    Mat m = createIntermediateMatFromInputTexture (tex);
                    inputData.Add (m.DataPointer);
                    inputStep.Add (m.Step);
                    inputSize.Add (new Vector2Int (m.Cols, m.Rows));
                    initialMatArray[i] = m;
                }
                //SaveDataSnapshot (inputData, inputSize, inputStep);
            } catch (Exception e) {
                Debug.LogWarningFormat ("Hierarchical stitching error: {0}\n{1}", e.Message, e.StackTrace);
                failure = true;
            }

            destroyInputImages (sourceImages);

            if (!failure) {

                int iterationsCount = getParallelStitchingIterationsCount (inputData.Length);

                for (int i = 0; i < iterationsCount; i++) {
                    float thresh = 1 - i * ((1 - minPanoConfidenceThreshold) / (iterationsCount - 1));
                    //float thresh = PanoConfidenceThreshold;
                    int outputDataLength = (int) Mathf.Floor (inputData.Length / 2);
                    NativeArray<IntPtr> outputData = new NativeArray<IntPtr> (outputDataLength, Allocator.Persistent);
                    NativeArray<Vector2Int> outputSize = new NativeArray<Vector2Int> (outputDataLength, Allocator.Persistent);
                    NativeArray<int> outputStep = new NativeArray<int> (outputDataLength, Allocator.Persistent);
                    NativeArray<bool> jobFailure = new NativeArray<bool> (outputDataLength, Allocator.Persistent);
                    Debug.LogFormat ("Starting hierarchical stitching job (iteration {0}, confidence threshold = {1})", i, thresh);

                    ParallelStitchingJob job = new ParallelStitchingJob () {
                        inputData = inputData,
                        inputSize = inputSize,
                        inputStep = inputStep,
                        panoConfidenceThreshold = thresh,
                        outputData = outputData,
                        outputSize = outputSize,
                        outputStep = outputStep,
                        failure = jobFailure,
                        iteration = i
                    };

                    JobHandle handle = job.Schedule (outputDataLength, 1);

                    yield return new WaitUntil (() => handle.IsCompleted);
                    handle.Complete ();

                    Debug.LogFormat("Failures array at iteration {0}: {1}", i, string.Join(", ", jobFailure.ToArray()));

                    if (jobFailure.ToArray ().Any (f => f == true))
                        failure = true;

                    disposeMatsFromData (inputData, inputSize, inputStep);

                    if (!failure) {
                        inputData.Clear ();
                        inputSize.Clear ();
                        inputStep.Clear ();

                        inputData.CopyFrom (outputData.ToArray ());
                        inputSize.CopyFrom (outputSize.ToArray ());
                        inputStep.CopyFrom (outputStep.ToArray ());

                        /* if(Application.isEditor){
                            SaveDataSnapshot(inputData, inputSize, inputStep, "StitchingIntermediateOutput_iteration" + i + "_img");
                        } */

                        disposeMatsFromData (outputData, outputSize, outputStep);
                    }

                    outputData.Dispose ();
                    outputSize.Dispose ();
                    outputStep.Dispose ();
                    jobFailure.Dispose ();

                    if (i == 0) {
                        foreach (Mat m in initialMatArray) {
                            m.Dispose ();
                        }
                    }

                    if (failure) {
                        Debug.LogWarningFormat ("Hierarchical stitching failed at iteration {0}", i);
                        break;
                    }else{
                        Debug.LogFormat("Hierarchical stitching iteration {0} succeeded", i);
                    }
                }
            }

            Texture2D resultTex = null;

            if (!failure) {
                resultTex = ImageStitching.createTextureFromData (inputData[0], inputSize[0], inputStep[0]);
            }

            inputData.Dispose ();
            inputSize.Dispose ();
            inputStep.Dispose ();

            handler (!failure, resultTex);
            GC.Collect();
        }

显然,我对计算机视觉算法和/或 OpenCV/EmguCV 架构以及一般的并行化缺乏更深入的了解。

我的主要问题是:

  1. 尝试完成运行时全景图是否有意义 首先在 Unity 中在移动设备上拼接?我假设如果 原生移动应用可以做到,那么它应该是可能的。
  2. 尝试在统一作业中调用 EmguCV API 有意义吗?一世 怀疑这可能是这里的主要问题。
  3. 如果前两个问题中的任何一个得到肯定回答,如何 正确优化拼接?我必须潜入低级吗 拼接 API 将图像一一拼接,然后 SOMEHOW(?) 根据结果​​构建全景投影?

任何建议将不胜感激!

0 个答案:

没有答案