@plac

Нехватка памяти при загрузке изображений из памяти устройства?

Добрый день! Возникла проблема при загрузке изображений из памяти устройства в ран тайме. Класс создает папки в соответствии с проектами в приложении, пользователь в эти папки помещает картинки и пишет их названия через запятую в поле, приложение находит картинки в папке. Дальше мы читаем их побитово через File.ReadAllBytes(imagePath), создаем текстуру через LoadImage(), создаем спрайт из этой текстуры и выгружаем в компонент Image.
Трудность в том что картинки могут быть очень большими (> 8 мб) и их может быть много (до 25) и очевидно что с таким подходом памяти не хватает.
Ресайзил текстуру в 10 раз перед созданием спрайта, толка нет, ошибка.

Пытался асинхронно грузить каждую картинку, толку тоже никакого. Подскажите пожалуйста, какие есть варианты решения. Возможно на верно оформил асинхронность. Можно ли как то грузить превью изображения? Как тут правильно оформить асинхронность? Хотелось бы с примером, спасибо!

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;

public class PhotosController : MonoBehaviour
{
    private ModelController _modelController;
    private string _startPath;
    private List<string> _formatsData;
    private List<string> _catalogsPaths;
    private List<List<string>> _projectsCatalogTitle;
    private string _sourceCatalog;

    private int _timerUpdate;
    private int _timerStartValue;
    void Start()
    {
        _timerStartValue = 300;
        _timerUpdate = _timerStartValue;


        _startPath = "/storage/emulated/0/Android";
        _modelController = GameObject.FindObjectOfType<ModelController>();
        _sourceCatalog = "/archeoPhotos";

        _formatsData = new List<string>() { "png" , "jpg", "jpeg" };
        UpdateState(); // Обновляем состояние

    }

    public Texture2D GetPhoto(string value, string project, out bool isFound, float resize = 1f)
    {
        isFound = false;
        Texture2D resultPhoto = new Texture2D(1024, 768); // Создаем текстуру
        string pathFindCatalog = "";

        //Debug.Log("Вызов фотографии " + value + " | " + project);

        int controller = 0;
        while (controller < _projectsCatalogTitle[0].Count)
        {
            if (_projectsCatalogTitle[0][controller] == project)
            {
                DirectoryInfo workDirectory = new DirectoryInfo(_catalogsPaths[controller + 1]);
                pathFindCatalog = workDirectory.FullName;
            }
            controller++;
        }

        if (pathFindCatalog != "")
        {
            DirectoryInfo catalog = new DirectoryInfo(pathFindCatalog);
            FileInfo[] files = new FileInfo[0];
            foreach (string format in _formatsData)
            {
                files = catalog.GetFiles(value + "." + format);
                if (files.Length > 0) { break; }
            }

            if(files.Length > 0) // Проверка что файл найден
            {
                byte[] dataPhoto = getImageByte(files[0].FullName); // Побитово читаем картинку
                resultPhoto.LoadImage(dataPhoto); // Загружаем побитово в текстуру
                isFound = true;
            }
        }
        float resizeWidth = resultPhoto.width * resize;
        float resizeHeight = resultPhoto.height * resize;
        resultPhoto = this.ScaleTexture(resultPhoto, (int)resizeWidth, (int)resizeHeight);
        return resultPhoto; // Возвращаем текстуру
    }

    // Побитово читаем файл картинки
    private byte[] getImageByte(string imagePath)
    {
        byte[] imgByte = File.ReadAllBytes(imagePath);
        return imgByte;
    }

    // Изменение размера
    private Texture2D ScaleTexture(Texture2D source, int targetWidth, int targetHeight)
    {
        Texture2D result = new Texture2D(targetWidth, targetHeight, source.format, true);
        Color[] rpixels = result.GetPixels(0);
        float incX = (1.0f / (float)targetWidth);
        float incY = (1.0f / (float)targetHeight);
        for (int px = 0; px < rpixels.Length; px++)
        {
            rpixels[px] = source.GetPixelBilinear(incX * ((float)px % targetWidth), incY * ((float)Mathf.Floor(px / targetWidth)));
        }
        result.SetPixels(rpixels, 0);
        result.Apply();
        return result;
    }

    private void UpdateState()
    {
        // Собираем массив необходимых путей
        _catalogsPaths = new List<string>();
        _projectsCatalogTitle = new List<List<string>>() { new List<string>(), new List<string>() };
        _catalogsPaths.Add(_startPath + _sourceCatalog);

        List<ProjectData> projectsData = new List<ProjectData>();
        projectsData = _modelController.LoadProjectsData().ProjectsArrayModal;
        // Перебираем проекты, в массив создаем пути
        foreach (ProjectData project in projectsData)
        {
            _catalogsPaths.Add(_startPath + _sourceCatalog + "/" + project.Title + " (" + project.Id + ")");

            _projectsCatalogTitle[0].Add(project.Id);
            _projectsCatalogTitle[1].Add(project.Title + " (" + project.Id + ")");
        }


        CatalogsExistsControl(); // Добавляем необходимые каталоги
        RenameExcessCatalogs(); // Удаляем лишние каталоги

    }

    // Проверяем наличие нужных каталогов
    private void CatalogsExistsControl()
    {
        foreach(string path in _catalogsPaths)
        {
            DirectoryInfo catalog = new DirectoryInfo(path);
            if(!catalog.Exists)
            {
                catalog.Create();
            }
        }
    }

    // Переименуем лишние каталоги
    private void RenameExcessCatalogs()
    {
        DirectoryInfo sourceCatalog = new DirectoryInfo(_startPath + _sourceCatalog);
        foreach(DirectoryInfo subDirectory in sourceCatalog.GetDirectories())
        {
            bool isRename = true;
            foreach(string projectTitle in _projectsCatalogTitle[1])
            {
                if (subDirectory.Name == projectTitle || subDirectory.Name.Contains("(не используется)"))
                {
                     isRename = false;
                }
            }


            if (isRename)
            {
                if(!subDirectory.Name.Contains("(не используется)"))
                {
                    Directory.Move(subDirectory.FullName, subDirectory.FullName + " (не используется)");
                }
            }
        }
    }


    void FixedUpdate()
    {
        _timerUpdate--;
        if (_timerUpdate == 0)
        {
            UpdateState();
            _timerUpdate = _timerStartValue;
        }
    }
}


using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class ResultPhotoBlockView : MonoBehaviour
{
    private ResultPhotoBlockController _controllerInstantiate;
    protected GameObject _grideImageBar;
    private Button _updatePhotoLevelButton;
    protected SceneController _sceneController;
    protected PhotosController _photosController;
    private GameObject _emptyMessage;

    public UnityEvent onClickUpdatePhotoButton;

    void Awake()
    {
        _sceneController = GameObject.FindObjectOfType<SceneController>();
        _photosController = GameObject.FindObjectOfType<PhotosController>();
        _controllerInstantiate = this.gameObject.GetComponent<ResultPhotoBlockController>();

        _grideImageBar = this.gameObject.GetComponentInChildren<searchGrideImageBar>().gameObject;
        _updatePhotoLevelButton = this.gameObject.GetComponentInChildren<searchUpdatePhotoBackgroundButton>().gameObject.transform.GetChild(0).gameObject.GetComponent<Button>();

        onClickUpdatePhotoButton = new UnityEvent();
        _updatePhotoLevelButton.onClick.AddListener(delegate { onClickUpdatePhotoButton.Invoke(); });
        _emptyMessage = this.gameObject.GetComponentInChildren<searchPhotoEmpty>().gameObject;

    }

    public virtual void DisplayPhoto(string photoName)
    {
        this.RemoveEmptyMessage();

        var prefab = Resources.Load("UnitPhoto") as GameObject;
        GameObject photoObject = Instantiate(prefab, _grideImageBar.transform);

        bool isFound; // Нашли ли фото
        Texture2D workTexture = _photosController.GetPhoto(photoName, _sceneController.GetIdCurrentProject(), out isFound, 0.25f);
        if (!isFound) // Загружаем заглушку если фото нет
        {
            workTexture = Resources.Load("notFound") as Texture2D;
        }
        Sprite preview = Sprite.Create(workTexture, new Rect(0.0f, 0.0f, workTexture.width, workTexture.height), new Vector2(1.0f, 1.0f), 20.0f);
        photoObject.transform.Find("Background").GetComponent<Image>().sprite = preview;
        //photoObject.transform.Find("Background").GetComponent<Image>().SetNativeSize();
    }

    public void ClearPhotoContent()
    {
        for (int index = 0; index < _grideImageBar.transform.childCount; index++)
        {
            if (_grideImageBar.transform.GetChild(index).gameObject != _emptyMessage) {
                _grideImageBar.transform.GetChild(index).gameObject.SetActive(false);
                Destroy(_grideImageBar.transform.GetChild(index).gameObject); // GridLayout жалуется если удаляем
            }
        }
        this.AddEmptyMessage();
    }

    public void AddEmptyMessage()
    {
        _emptyMessage.SetActive(true);
    }

    public void RemoveEmptyMessage()
    {
        // Добавляем плашку об отсудствии фотографий
        _emptyMessage.SetActive(false);
    }
}
  • Вопрос задан
  • 91 просмотр
Пригласить эксперта
Ответы на вопрос 1
402d
@402d
начинал с бейсика на УКНЦ в 1988
FileChannel channel = escPosEmulator.randomAccessFile.getChannel();
MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, buf.getRowBytes()* escPosEmulator.output_height , buf.getRowBytes()*height);
 buf.copyPixelsToBuffer(map);
 escPosEmulator.output_height += buf.getHeight();


Есть у меня эмулятор. После того как он обработал команды и отрисовал буфер строки, я его сохраняю в файл.

Когда обработка завершена
if (escPosEmulator.output_height > 0) {
    Bitmap out = Bitmap.createBitmap(escPosEmulator.max_dots, escPosEmulator.output_height, Bitmap.Config.ARGB_8888);
    FileChannel channel = escPosEmulator.randomAccessFile.getChannel();
    MappedByteBuffer map = channel.map(FileChannel.MapMode.READ_WRITE, 0, out.getRowBytes() * out.getHeight());
    out.copyPixelsFromBuffer(map);
    channel.close();
                                   
 }


получаю картинку обратно в память.
В результате я работаю обычно с 32 пикселями в высоту.
Суммарную высоту программно ограничил в 32 тысячи пикселей. А то потом штатные просмотровщики виснут.

Это нативные функции java под андроидом. Поищите аналоги сишарпа для прямой работы с файлом и байтовым представлением битмапа .
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы