You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

从XRCpuImage转换并旋转Texture2D至RawImage,运行约30秒后崩溃

问题排查:AR图像旋转后运行崩溃

问题描述

通过XRCpuImage获取相机帧,转换为Texture2D后显示在RawImage上,图像出现旋转偏移。使用自定义rotateTexture函数旋转后显示正常,但应用运行约30秒后崩溃。

代码片段

using System;
using Unity.Collections.LowLevel.Unsafe;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
using Unity.Collections;

public class CPU_RawImage : MonoBehaviour
{
    Texture2D m_CameraTexture;
    GameObject DR_layer;
       
    [SerializeField]
    [Tooltip("The ARCameraManager which will produce frame events.")]
    ARCameraManager m_CameraManager;  

    /// <summary>
    /// Get or set the <c>ARCameraManager</c>.
    /// </summary>
    public ARCameraManager cameraManager
    {
        get => m_CameraManager;
        set => m_CameraManager = value;  
    }

    [SerializeField]
    RawImage m_RawCameraImage;

    /// <summary>
    /// The UI RawImage used to display the image on screen.
    /// </summary>
    public RawImage rawCameraImage
    {
        get => m_RawCameraImage;
        set => m_RawCameraImage = value;
    }

    XRCpuImage.Transformation m_Transformation = XRCpuImage.Transformation.MirrorX;
    
    void OnEnable()
    {
        if (m_CameraManager != null)
        {
            m_CameraManager.frameReceived += OnCameraFrameReceived;
        }
    }

    void OnDisable()
    {
        if (m_CameraManager != null)
        {
            m_CameraManager.frameReceived -= OnCameraFrameReceived;
        }
    }

    //Rotate Funktion
    private Texture2D rotateTexture(Texture2D originalTexture, bool clockwise)
    {
        Color32[] original = originalTexture.GetPixels32();
        Color32[] rotated = new Color32[original.Length];
        int w = originalTexture.width;
        int h = originalTexture.height;

        int iRotated, iOriginal;

        for (int j = 0; j < h; ++j)
        {
            for (int i = 0; i < w; ++i)
            { 
                iRotated = (i + 1) * h - j - 1;
                iOriginal = clockwise ? original.Length - 1 - (j * w + i) : j * w + i;
                rotated[iRotated] = original[iOriginal];          
            }
        }

        Texture2D rotatedTexture = new Texture2D(h, w);
        rotatedTexture.SetPixels32(rotated); 
        rotatedTexture.Apply();
        return rotatedTexture;
    }

    unsafe void UpdateCameraImage(int[] pixel)
    {
        if (!cameraManager.TryAcquireLatestCpuImage(out XRCpuImage image))  
        {
            return;
        }

        var format = TextureFormat.RGBA32;

        if (m_CameraTexture == null || m_CameraTexture.width != image.width || m_CameraTexture.height != image.height)
        {
            m_CameraTexture = new Texture2D(image.width, image.height, format, false);
        }

        var conversionParams = new XRCpuImage.ConversionParams(image, format, m_Transformation);
        var rawTextureData = m_CameraTexture.GetRawTextureData<byte>();
        try
        {
            image.Convert(conversionParams, new IntPtr(rawTextureData.GetUnsafePtr()), rawTextureData.Length);
        }
        finally
        {
            image.Dispose();
        }

        m_CameraTexture.Apply();

        //rotate Texture clockwise
        //m_CameraTexture = rotateTexture(m_CameraTexture, true);

        m_RawCameraImage.texture = m_CameraTexture;   
    }     

    void Start(){
        DR_layer = GameObject.Find("RawImage");
    }

    void OnCameraFrameReceived(ARCameraFrameEventArgs eventArgs)
    {
        int[] pixel = {200,1000};
        UpdateCameraImage(pixel);
    }
}

崩溃原因及修复方案

1. 核心问题:内存泄漏

启用rotateTexture调用后,每帧都会创建新的Texture2D对象,但旧的Texture2D未被销毁。AR相机通常以30-60帧/秒运行,30秒内会生成数千个未回收的纹理对象,导致内存耗尽触发崩溃。

2. 优化方案:

方案一:使用XRCpuImage内置旋转(推荐)

无需自定义旋转函数,直接修改XRCpuImage.Transformation参数,让图像在转换阶段直接完成旋转和镜像,效率更高且无内存问题:

// 根据需求组合旋转和镜像选项,示例为水平镜像+顺时针旋转90度
XRCpuImage.Transformation m_Transformation = XRCpuImage.Transformation.MirrorX | XRCpuImage.Transformation.Rotate90Clockwise;

XRCpuImage.Transformation支持的组合选项:

  • Rotate90Clockwise / Rotate180 / Rotate90CounterClockwise:旋转方向
  • MirrorX / MirrorY:镜像翻转

方案二:复用旋转后的Texture2D

如果必须保留自定义旋转逻辑,需复用纹理对象,避免重复创建:

// 新增成员变量存储旋转后的纹理
Texture2D m_RotatedTexture;

private Texture2D rotateTexture(Texture2D originalTexture, bool clockwise)
{
    int w = originalTexture.width;
    int h = originalTexture.height;
    // 仅在尺寸变化时重新创建纹理,否则复用已有对象
    if (m_RotatedTexture == null || m_RotatedTexture.width != h || m_RotatedTexture.height != w)
    {
        if (m_RotatedTexture != null) Destroy(m_RotatedTexture);
        m_RotatedTexture = new Texture2D(h, w);
    }

    Color32[] original = originalTexture.GetPixels32();
    Color32[] rotated = new Color32[original.Length];

    int iRotated, iOriginal;
    for (int j = 0; j < h; ++j)
    {
        for (int i = 0; i < w; ++i)
        { 
            iRotated = (i + 1) * h - j - 1;
            iOriginal = clockwise ? original.Length - 1 - (j * w + i) : j * w + i;
            rotated[iRotated] = original[iOriginal];          
        }
    }

    m_RotatedTexture.SetPixels32(rotated); 
    m_RotatedTexture.Apply();
    return m_RotatedTexture;
}

// 在UpdateCameraImage中直接赋值旋转后的纹理,无需替换原m_CameraTexture
m_RawCameraImage.texture = rotateTexture(m_CameraTexture, true);

方案三:销毁旧纹理(临时应急)

若需临时保留原有逻辑,至少在创建新纹理前销毁旧对象:

// 在rotateTexture返回前销毁传入的原纹理(需确保原纹理不再被使用)
Destroy(originalTexture);

此方案会导致原m_CameraTexture频繁销毁重建,性能较差,仅作临时应急使用。

3. 额外优化点

  • 移除无用的pixel参数:UpdateCameraImage中的int[] pixel未被使用,可删除简化代码。
  • 删除无效变量:DR_layer变量未被使用,可移除Start方法中的DR_layer = GameObject.Find("RawImage");语句。

内容的提问来源于stack exchange,提问作者znerl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 01:55:25