将Unity ARCore中的AcquireCameraImageBytes()保存到存储器中作为图像

5
使用Unity和ARCore的新版本1.1,API提供了一些获取相机信息的新方法。然而,我找不到任何好的例子将其保存为本地存储的文件,例如jpg格式。
ARCore示例提供了一个很好的示例,在此处检索相机数据,然后进行一些操作:https://github.com/google-ar/arcore-unity-sdk/blob/master/Assets/GoogleARCore/Examples/ComputerVision/Scripts/ComputerVisionController.cs#L212 该类中有一些检索相机数据的示例,但没有关于保存数据的内容。
我看到了这个:如何使用Unity ARCore SDK拍摄和保存图片/屏幕截图? 它使用了旧的API方式获取数据,并且对保存也没有详细说明。
我理想中希望的是一种方法,能够通过Unity将API中Frame.CameraImage.AcquireCameraImageBytes()的数据转换为存储在磁盘上的jpg格式。

更新

我后来主要是通过查看ARCore github页面上的这个问题https://github.com/google-ar/arcore-unity-sdk/issues/72#issuecomment-355134812并修改下面Sonny的答案才解决了这个问题,所以公平起见,我接受了他的回答。

如果有其他人也在尝试这个,我必须执行以下步骤:

  1. Add a callback to the Start method to run your OnImageAvailable method when the image is available:

    public void Start()
    {
        TextureReaderComponent.OnImageAvailableCallback += OnImageAvailable;
    }
    
  2. Add a TextureReader (from the computer vision example provided with the SDK) to your camera and your script

  3. Your OnImageAvailable should look a bit like this:

    /// <summary>
    /// Handles a new CPU image.
    /// </summary>
    /// <param name="format">The format of the image.</param>
    /// <param name="width">Width of the image, in pixels.</param>
    /// <param name="height">Height of the image, in pixels.</param>
    /// <param name="pixelBuffer">Pointer to raw image buffer.</param>
    /// <param name="bufferSize">The size of the image buffer, in bytes.</param>
    private void OnImageAvailable(TextureReaderApi.ImageFormatType format, int width, int height, IntPtr pixelBuffer, int bufferSize)
    {
        if (m_TextureToRender == null || m_EdgeImage == null || m_ImageWidth != width || m_ImageHeight != height)
        {
            m_TextureToRender = new Texture2D(width, height, TextureFormat.RGBA32, false, false);
            m_EdgeImage = new byte[width * height * 4];
            m_ImageWidth = width;
            m_ImageHeight = height;
        }
    
        System.Runtime.InteropServices.Marshal.Copy(pixelBuffer, m_EdgeImage, 0, bufferSize);
    
        // Update the rendering texture with the sampled image.
        m_TextureToRender.LoadRawTextureData(m_EdgeImage);
        m_TextureToRender.Apply();
    
        var encodedJpg = m_TextureToRender.EncodeToJPG();
        var path = Application.persistentDataPath;
    
        File.WriteAllBytes(path + "/test.jpg", encodedJpg);
    }
    
2个回答

6
在Unity中,可以将原始图像数据加载到纹理中,然后使用UnityEngine.ImageConversion.EncodeToJPG将其保存为JPG格式。示例代码:
public class Example : MonoBehaviour
{
    private Texture2D _texture;
    private TextureFormat _format = TextureFormat.RGBA32;

    private void Awake()
    {
        _texture = new Texture2D(width, height, _format, false);
    }

    private void Update()
    {
        using (var image = Frame.CameraImage.AcquireCameraImageBytes())
        {
            if (!image.IsAvailable) return;

            // Load the data into a texture 
            // (this is an expensive call, but it may be possible to optimize...)
            _texture.LoadRawTextureData(image);
            _texture.Apply();
        }
    }

    public void SaveImage()
    {
        var encodedJpg = _texture.EncodeToJPG();
        File.WriteAllBytes("test.jpg", encodedJpg)
    }
}

然而,我不确定TextureFormat是否对应于与Frame.CameraImage.AcquireCameraImageBytes()兼容的格式。(我熟悉Unity但不熟悉ARCore.)请参阅Unity有关TextureFormat的文档,以及它是否与ARCore的ImageFormatType兼容。
另外,请测试代码是否足够高效适用于您的应用程序。
编辑:正如用户@Lece所解释的那样,请使用File.WriteAllBytes保存编码数据。我已经更新了我的代码示例,因为我最初省略了这一步。
编辑#2:有关ARCore的完整答案,请参见问题帖子的更新。这里的评论也可能有用-Jordan指定“主要部分是使用计算机视觉sdk示例中的纹理读取器”。

这非常有帮助,我不得不从这里拿一些代码:https://github.com/google-ar/arcore-unity-sdk/issues/72#issuecomment-355134812,尽管现在它只保存了一个黑色的正方形而不是数据。 - Jordan Robinson
你能在屏幕上看到Texture2D的彩色图像吗?我想缩小问题范围,确定是AcquireCameraImageBytes()步骤、LoadRawTextureData()Apply()步骤,还是最终的EncodeToJpg()WriteAllBytes()步骤出了问题。 - sonnyb
@JordanRobinson,您解决了黑方块的问题吗?我也遇到了相同的问题。 - midnightcoffee
1
@midnightcoffee 是的,如果您查看原始问题中的更新,那就是我所采用的方法,并解决了黑色方块的问题。主要部分是使用计算机视觉sdk示例中的纹理阅读器,链接在这里:https://github.com/google-ar/arcore-unity-sdk/tree/master/Assets/GoogleARCore/Examples/ComputerVision - Jordan Robinson
1
我修好了,不确定原因是什么,但现在它可以工作了,谢谢! 然而,我很困惑,我们不是从AR Core中以YUV格式获取图像(_在这里提到_(https://developers.google.com/ar/reference/unity/struct/GoogleARCore/CameraImageBytes)的 ARCore相机图像以YUV-420-888格式从CPU可访问的数据。 )吗?将其加载到RGBA32纹理中而不进行转换,这该如何工作? - midnightcoffee
显示剩余2条评论

1

由于我不熟悉ARCore,因此我将保持通用性。

  1. 使用LoadRawTextureData()Apply()将字节数组加载到您的Texture2D中。
  2. 使用EncodeToJPG()对纹理进行编码。
  3. 使用File.WriteAllBytes(path + ".jpg", encodedBytes)保存编码数据。

1
我已经在我的答案中更新了示例代码,加入了您的第三步,这是我之前忘记的。谢谢。 - sonnyb
@sonny 没问题。你在我还没打完之前就提交了,但是由于最后一个关键部分,我还是决定发布它。总体上是相同的想法,所以一切都好 :) - Lece

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接