StartCoroutine (), чтобы исправить ошибку targetTexture.ReadPixels - PullRequest
1 голос
/ 27 марта 2019

Как видно из названия, у меня проблема с ошибкой в ​​строке

targetTexture.ReadPixels(new Rect(0, 0, cameraResolution.width, cameraResolution.height), 0, 0);

Ошибка:

ReadPixels был вызван для чтения пикселей из системного кадрового буфера, в то время как не внутри рамки для рисования. UnityEngine.Texture2D: ReadPixels (Rect, Int32, Int32)

Как я понял из других постов, один из способов решения этой проблемы - создать метод Ienumerator, который возвращает return, WaitForSeconds или что-то подобное, и вызвать его следующим образом: StartCoroutine(methodname), чтобы кадры загружались вовремя, чтобы будет пикселей для чтения.

То, что я не получаю, это , где в следующем коде этот метод будет наиболее целесообразным . Какая часть не загружается вовремя?

    PhotoCapture photoCaptureObject = null;
    Texture2D targetTexture = null;
    public string path = "";
    CameraParameters cameraParameters = new CameraParameters();

private void Awake()
{

    var cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
    targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);

    // Create a PhotoCapture object
    PhotoCapture.CreateAsync(false, captureObject =>
    {
        photoCaptureObject = captureObject;
        cameraParameters.hologramOpacity = 0.0f;
        cameraParameters.cameraResolutionWidth = cameraResolution.width;
        cameraParameters.cameraResolutionHeight = cameraResolution.height;
        cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;
    });
}

private void Update()
{
    // if not initialized yet don't take input
    if (photoCaptureObject == null) return;

    if (Input.GetKey("k") || Input.GetKey("k"))
    {
        Debug.Log("k was pressed");

        VuforiaBehaviour.Instance.gameObject.SetActive(false);

        // Activate the camera
        photoCaptureObject.StartPhotoModeAsync(cameraParameters, result =>
        {
            if (result.success)
            {
                // Take a picture
                photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
            }
            else
            {
                Debug.LogError("Couldn't start photo mode!", this);
            }
        });
    }
}

private static string FileName(int width, int height)
{
    return $"screen_{width}x{height}_{DateTime.Now:yyyy-MM-dd_HH-mm-ss}.png";
}

private void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
    // Copy the raw image data into the target texture
    photoCaptureFrame.UploadImageDataToTexture(targetTexture);

    Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();

    targetTexture.ReadPixels(new Rect(0, 0, cameraResolution.width, cameraResolution.height), 0, 0);
    targetTexture.Apply();

    byte[] bytes = targetTexture.EncodeToPNG();

    string filename = FileName(Convert.ToInt32(targetTexture.width), Convert.ToInt32(targetTexture.height));
    //save to folder under assets
    File.WriteAllBytes(Application.streamingAssetsPath + "/Snapshots/" + filename, bytes);
    Debug.Log("The picture was uploaded");

    // Deactivate the camera
    photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}

private void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
    // Shutdown the photo capture resource
    VuforiaBehaviour.Instance.gameObject.SetActive(true);
    photoCaptureObject.Dispose();
    photoCaptureObject = null;


}

Извините, если это считается дубликатом, например, это .


Редактировать

И этот один может быть полезен, когда я доберусь до этой точки.

Неужели мне не нужны эти три строки вообще?

Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
targetTexture.ReadPixels(new Rect(0, 0, cameraResolution.width, cameraResolution.height), 0, 0);
targetTexture.Apply();

Как написано в комментариях, разница между использованием этих трех строк и не в том, что сохраненная фотография имеет черный фон + AR-GUI. Без второй строки кода выше - фотография с AR-GUI, но с фоном - прямая трансляция с моей компьютерной веб-камеры. И действительно, я не хочу видеть компьютерную веб-камеру, но то, что видит HoloLens.

1 Ответ

1 голос
/ 28 марта 2019

Ваши три строки

Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();

targetTexture.ReadPixels(new Rect(0, 0, cameraResolution.width, cameraResolution.height), 0, 0);
targetTexture.Apply();

не имеет особого смысла для меня. Texture2D.ReadPixels предназначен для создания Снимка экрана , чтобы вы перезаписывали текстуру, которую вы только что получили от PhotoCapture, со скриншотом? (Также с неправильными размерами, так как разрешение камеры очень вероятно! = Разрешение экрана.)

Это также причина для

Как написано в комментариях, разница между использованием этих трех строк и не в том, что сохраненная фотография имеет черный фон + AR-GUI.

После выполнения

photoCaptureFrame.UploadImageDataToTexture(targetTexture);

у вас уже есть Texture2D, полученный от PhotoCapture в targetTexture.

Я думаю, что вы, вероятно, перепутали это с Texture2D.GetPixels , который используется для получения данных пикселей данного Texture2D.


Я хотел бы обрезать захваченное фото из центра в конце и думаю, что, возможно, это возможно с этой строкой кода? Начало нового прямоугольника с пикселями, отличными от 0, 0)

На самом деле вы хотите обрезать полученные Texture2D из центра, как вы упомянули в комментариях. Вы можете сделать это, используя GetPixels (int x, int y, int blockWidth, int blockHeight, int miplevel) , который используется для вырезания определенной области заданного Texture2D

public static Texture2D CropAroundCenter(Texture2D input, Vector2Int newSize)
{
    if(input.width < newSize.x || input.height < newSize.y)
    {
        Debug.LogError("You can't cut out an area of an image which is bigger than the image itself!", this);
        return null;
    }

    // get the pixel coordinate of the center of the input texture
    var center = new Vector2Int(input.width / 2, input.height / 2);

    // Get pixels around center
    // Get Pixels starts width 0,0 in the bottom left corner
    // so as the name says, center.x,center.y would get the pixel in the center
    // we want to start getting pixels from center - half of the newSize 
    //
    // than from starting there we want to read newSize pixels in both dimensions
    var pixels = input.GetPixels(center.x - newSize.x / 2, center.y - newSize.y / 2, newSize.x, newSize.y, 0);

    // Create a new texture with newSize
    var output = new Texture2D(newSize.x, newSize.y);

    output.SetPixels(pixels);
    output.Apply();

    return output;
} 

для (надеюсь) лучшего понимания, это иллюстрация того, что делает здесь перегрузка GetPixels с данными значениями:

enter image description here

и чем его использовать в

private void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
    // Copy the raw image data into the target texture
    photoCaptureFrame.UploadImageDataToTexture(targetTexture);

    // for example take only half of the textures width and height
    targetTexture = CropAroundCenter(targetTexture, new Vector2Int(targetTexture.width / 2, targetTexture.height / 2);

    byte[] bytes = targetTexture.EncodeToPNG();

    string filename = FileName(Convert.ToInt32(targetTexture.width), Convert.ToInt32(targetTexture.height));
    //save to folder under assets
    File.WriteAllBytes(Application.streamingAssetsPath + "/Snapshots/" + filename, bytes);
    Debug.Log("The picture was uploaded");

    // Deactivate the camera
    photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}

Или вы можете сделать это метод расширения в отдельном static class как

public static class Texture2DExtensions
{
    public static void CropAroundCenter(this Texture2D input, Vector2Int newSize)
    {
        if (input.width < newSize.x || input.height < newSize.y)
        {
            Debug.LogError("You can't cut out an area of an image which is bigger than the image itself!");
            return;
        }

        // get the pixel coordinate of the center of the input texture
        var center = new Vector2Int(input.width / 2, input.height / 2);

        // Get pixels around center
        // Get Pixels starts width 0,0 in the bottom left corner
        // so as the name says, center.x,center.y would get the pixel in the center
        // we want to start getting pixels from center - half of the newSize 
        //
        // than from starting there we want to read newSize pixels in both dimensions
        var pixels = input.GetPixels(center.x - newSize.x / 2, center.y - newSize.y / 2, newSize.x, newSize.y, 0);

        // Resize the texture (creating a new one didn't work)
        input.Resize(newSize.x, newSize.y);

        input.SetPixels(pixels);
        input.Apply(true);
    }
}

и используйте его вместо

targetTexture.CropAroundCenter(new Vector2Int(targetTexture.width / 2, targetTexture.height / 2));

Примечание:

UploadImageDataToTexture : Вы можете использовать этот метод, только если вы указали формат BGRA32 в CameraParameters.

К счастью, вы все равно используете это;)

Имейте в виду, что эта операция будет выполняться в главном потоке и, следовательно, будет медленной.

Однако единственной альтернативой будет CopyRawImageDataIntoBuffer и генерирование текстуры в другом потоке или во внешнем, так что я бы сказал, что можно остаться с UploadImageDataToTexture;)

и

Захваченное изображение также появится перевернуто на HoloLens. Вы можете переориентировать изображение с помощью пользовательского шейдера.

при переворачивании они на самом деле означают, что Y-Axis текстуры вверх ногами. X-Axis правильно.

Для переворачивания текстуры по вертикали вы можете использовать второй метод расширения:

public static class Texture2DExtensions
{
    public static void CropAroundCenter(){....}

    public static void FlipVertically(this Texture2D texture)
    {
        var pixels = texture.GetPixels();
        var flippedPixels = new Color[pixels.Length];

        // These for loops are for running through each individual pixel and 
        // write them with inverted Y coordinates into the flippedPixels
        for (var x = 0; x < texture.width; x++)
        {
            for (var y = 0; y < texture.height; y++)
            {
                var pixelIndex = x + y * texture.width;
                var flippedIndex = x  + (texture.height - 1 - y) * texture.width;

                flippedPixels[flippedIndex] = pixels[pixelIndex];
            }
        }

        texture.SetPixels(flippedPixels);
        texture.Apply();
    }
}

и используйте его как

targetTexture.FlipVertically();

Результат: (Я использовал FlipVertically и обрезал до половины размера каждую секунду для этого примера и данной текстуры, но он должен работать одинаково для снятого изображения.)

enter image description here

Источник изображения: http://developer.vuforia.com/sites/default/files/sample-apps/targets/imagetargets_targets.pdf


Update

К вашей проблеме с кнопкой:

Не использовать

if (Input.GetKey("k") || Input.GetKey("k"))

Прежде всего, вы проверяете одно и то же условие дважды. И затем GetKey запускает каждый кадр , пока клавиша остается нажатой. Вместо этого используйте

if (Input.GetKeyDown("k"))

, который срабатывает только один раз. Я думаю, что была проблема с Vuforia и PhotoCapture, так как ваша оригинальная версия запускалась так часто, и, возможно, у вас были некоторые параллельные процессы PhotoCapture ...

...