StartCoroutine()修复了targetTexture.ReadPixels错误

问题描述 投票:1回答:1

正如标题所示,我在行中发生错误时遇到问题

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

错误:

调用ReadPixels从系统帧缓冲区读取像素,而不是在绘图帧内。 UnityEngine.Texture2D:ReadPixels(Rect,Int32,Int32)

正如我从其他帖子中了解到的,解决此问题的一种方法是制作一个Ienumerator方法,该方法会产生返回新的WaitForSeconds或其他东西,并将其称为:StartCoroutine(methodname),以便及时加载帧以便读取像素-ish。

我没有得到的是在下面的代码中这个方法最有意义的地方。哪个部分无法及时加载?

    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;


}

很抱歉,如果这与this重复,例如。


编辑

当我到达那一点时,this可能会有用。

这样我根本不需要这三条线吗?

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看到的东西。

c# unity3d hololens
1个回答
1
投票

你的三行

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);

你已经从Texture2DPhotoCapture收到了targetTexture

我想你可能把它与用于获取给定Texture2D.GetPixels的像素数据的Texture2D混淆了。


我想最后从中心裁剪拍摄的照片,我想这个代码行可能有可能吗?在0,0以外的其他像素处开始新的rect

你真正想要的是如你在评论中提到的那样从中心裁剪收到的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); }

而不是用它

extension method

或者你可以使它成为一个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

注意:

CopyRawImageDataIntoBuffer:如果您在CameraParameters中指定了BGRA32格式,则只能使用此方法。

幸运的是你无论如何都要使用它;)

请记住,此操作将在主线程上发生,因此很慢。

然而,唯一的选择是UploadImageDataToTexture并在另一个线程或外部生成纹理,所以我会说可以留在Y-Axis;)

拍摄的图像也会在HoloLens上翻转。您可以使用自定义着色器重定向图像。

通过翻转它们实际上意味着纹理的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();

并使用它

enter image description here

结果:(对于此示例和给定的纹理,我使用FlipVertically和cropp每秒的大小的一半,但它对于拍摄的图片应该相同。)

http://developer.vuforia.com/sites/default/files/sample-apps/targets/imagetargets_targets.pdf

图像来源:if (Input.GetKey("k") || Input.GetKey("k"))


更新

你的按钮问题:

不要用

GetKey

首先,你要检查完全相同的条件两次。并且当钥匙保持按下时,if (Input.GetKeyDown("k")) 会向每一帧发射。而是使用

qazxswpoi

它只发射一次。我猜Vuforia和PhotoCapture存在问题,因为您的原始版本经常被解雇,也许您有一些并发的PhotoCapture流程......

© www.soinside.com 2019 - 2024. All rights reserved.