异步读取图像文件

Posted

技术标签:

【中文标题】异步读取图像文件【英文标题】:Read an image file asynchronously 【发布时间】:2019-02-20 19:15:57 【问题描述】:

我想,我尝试使用 C# 中的 async/await 来实现。但是,我仍然注意到此实现存在显着滞后。使用分析器,我非常有信心是 FileStream.ReadAsync() 方法需要很长时间才能完成,而不是异步执行并阻止游戏更新。

我注意到这种方式比仅使用 File.ReadAllBytes 有更多的延迟,这很奇怪。我不能使用仍然会导致一些滞后的方法,并且我不想停止帧速率。

这是一些代码。

// Static class for reading the image.
class AsyncImageReader

    public static async Task<byte[]> ReadImageAsync(string filePath)
        
            byte[] imageBytes;
            didFinishReading = false;
            using (FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true))
            
                imageBytes = new byte[fileStream.Length];
                await fileStream.ReadAsync(imageBytes, 0, (int) fileStream.Length);

            

            didFinishReading = true;

            return imageBytes;
        
 

    // Calling function that is called by the OnCapturedPhotoToDisk event. When this is called the game stops updating completely rather than 
    public void AddImage(string filePath)
    
        Task<byte[]> readImageTask = AsyncImageReader.ReadImageAsync(filePath);
        byte [] imageBytes = await readImageTask;
        // other things that use the bytes....
    

【问题讨论】:

您的代码甚至无法编译,因为您的 AddImage 方法应该是异步的 你的文件有多大? 请注意,async 方法始终在调用线程上运行,直到到达实际的 await 语句(实际上意味着它是 IO 绑定的)。另请注意,当此await 完成时,工作将返回到同一(调用)线程。从理论上讲,这可能是滞后的原因。 @touseefbsb 我知道 IDE 告诉我的。这是代码的副本。 @PoulBak 文件不是很大,但 CPU 很垃圾。尝试与 Hololens 合作 【参考方案1】:

您不需要 Task 来异步加载图像。您可以使用 Unity 的 UnityWebRequestDownloadHandlerTexture API 进行加载。 UnityWebRequestTexture.GetTexture 简化了这一点,因为它会自动为您设置 DownloadHandlerTexture。这是在引擎盖下的另一个线程中完成的,但您使用协程来控制它。

public RawImage image;

void Start()

    StartCoroutine(LoadTexture());


IEnumerator LoadTexture()

    string filePath = "";//.....
    UnityWebRequest uwr = UnityWebRequestTexture.GetTexture(filePath);
    yield return uwr.SendWebRequest();

    if (uwr.isNetworkError || uwr.isHttpError)
    
        Debug.Log(uwr.error);
    
    else
    
        Texture texture = ((DownloadHandlerTexture)uwr.downloadHandler).texture;
        image.texture = texture;
    


您还可以使用Thread API 在另一个线程中加载文件。更好的是,使用ThreadPool API。他们中的任何一个都应该工作。加载图像字节数组后,要将字节数组加载到Texture2D,您必须在主线程中执行此操作,因为您不能在主线程中使用 Unity 的 API。

要在加载完图像字节后从另一个线程使用 Unity 的 API,您需要一个包装器来执行此操作。下面的示例使用 C# ThreadPool 和我的 UnityThread API 完成,简化了从另一个线程使用 Unity 的 API。你可以从这里获取UnityThread

public RawImage image;

void Awake()

    //Enable Callback on the main Thread
    UnityThread.initUnityThread();


void ReadImageAsync()

    string filePath = "";//.....

    //Use ThreadPool to avoid freezing
    ThreadPool.QueueUserWorkItem(delegate
    
        bool success = false;

        byte[] imageBytes;
        FileStream fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);

        try
        
            int length = (int)fileStream.Length;
            imageBytes = new byte[length];
            int count;
            int sum = 0;

            // read until Read method returns 0
            while ((count = fileStream.Read(imageBytes, sum, length - sum)) > 0)
                sum += count;

            success = true;
        
        finally
        
            fileStream.Close();
        

        //Create Texture2D from the imageBytes in the main Thread if file was read successfully
        if (success)
        
            UnityThread.executeInUpdate(() =>
            
                Texture2D tex = new Texture2D(2, 2);
                tex.LoadImage(imageBytes);
            );
        
    );

【讨论】:

以上是关于异步读取图像文件的主要内容,如果未能解决你的问题,请参考以下文章

与completionBlock异步下载和保存文件时出现错误的CollectionView单元格图像

Exif.js 读取图像的元数据

从图像文件中提取感兴趣区域而不读取整个图像

将drawable中的图像作为文件读取

为啥程序读取图像文件后分辨率会变小?

如何使用 Python 读取图像文件? [关闭]