我有一个Texture2D,应用于从网络摄像头获取的RawImage,我称之为“originalImage”。我想在另一个名为“cropArea”的矩形位置的基础上裁剪这个原始图像的一个块。基本上,cropArea 是我的原始图像的“指南”,我希望用户将手放在其中,这样我就可以获得另一张手的图像。
问题是我无法获得原始图像相对于cropArea位置的正确像素。实际裁剪的区域相对于 CropArea 的位置有一个偏移,我无法理解。这是一张显示我的问题的图片(我使用从谷歌获取的随机图像作为原始图像 Texuter2D 而不是我的网络摄像头)
我在层次结构中将cropArea作为originalImage的子级,下面是当我截取上面的屏幕截图时它们在我的检查器上的设置方式。
这是我的代码(这是我为了重现问题而编写的代码,因此该函数中没有涉及其他代码):
[SerializeField] private RawImage originalImage;
[SerializeField] private RawImage cropArea;
[SerializeField] private RawImage croppedImage;
void Update() {
if (Input.GetKeyDown(KeyCode.S))
{
Texture2D croppedTexture = new Texture2D((int)cropArea.rectTransform.rect.width, (int)cropArea.rectTransform.rect.height);
Texture2D originalTexture = (Texture2D)originalImage.mainTexture;
croppedTexture.SetPixels(originalTexture.GetPixels((int)cropArea.rectTransform.anchoredPosition.x, (int)cropArea.rectTransform.anchoredPosition.y, (int)cropArea.rectTransform.rect.width, (int)cropArea.rectTransform.rect.height));
croppedTexture.Apply();
croppedImage.texture = croppedTexture;
}
}
我再次在代码中尝试了多种组合,尝试使用 localPosition 或 rect 而不是 anchoredPosition 来获取cropArea 的位置,得到不同的结果,但从来没有正确的结果。
我想我可能错过了cropArea的位置和从originalImage中获取的像素之间的一些关系,但我真的不知道它是什么。 每次我必须在运行时处理 Unity UI 时,这都会让我头疼,它是如此令人困惑和反直觉。
好的,正如我在 @obywan 答案下所写的那样,我的问题是我的原始图像被拉伸以填充画布,但裁剪的源不是 RawImage,而是 RawImage 的纹理,它不会被拉伸。 我感谢 obywan 让我意识到我的代码实际上运行良好,并让我认为可能存在尺寸问题。
我找到了一个可行的解决方案,编写一个函数来调整纹理大小,这样我就可以在纹理像素位置和我的眼睛在 UI 中实际看到的内容之间建立对应关系。 一旦调整了纹理的大小,我就可以使用cropArea位置从调整大小的纹理中获取像素,并获得想要的结果。
所以,这就是调整纹理大小的函数:
Texture2D ResizeTexture2D(Texture2D originalTexture, int resizedWidth, int resizedHeight)
{
RenderTexture renderTexture = new RenderTexture(resizedWidth, resizedHeight, 32);
RenderTexture.active = renderTexture;
Graphics.Blit(originalTexture, renderTexture);
Texture2D resizedTexture = new Texture2D(resizedWidth, resizedHeight);
resizedTexture.ReadPixels(new Rect(0, 0, resizedWidth, resizedHeight), 0, 0);
resizedTexture.Apply();
return resizedTexture;
}
这是我的 Update 方法,实现了 ResizeTexture2D 函数,可以正常工作:
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
Texture2D croppedTexture = new Texture2D((int)cropArea.rectTransform.rect.width, (int)cropArea.rectTransform.rect.height);
Texture2D originalTexture = (Texture2D)originalImage.mainTexture;
Texture2D originalTextureResized = ResizeTexture2D(originalTexture, (int)originalImage.rectTransform.rect.width, (int)originalImage.rectTransform.rect.height);
croppedTexture.SetPixels(originalTextureResized.GetPixels((int)cropArea.rectTransform.anchoredPosition.x, (int)cropArea.rectTransform.anchoredPosition.y, (int)cropArea.rectTransform.rect.width, (int)cropArea.rectTransform.rect.height));
croppedTexture.Apply();
croppedImage.texture = croppedTexture;
}
}
ResizeTexture2D 中的第四行应该是:
Texture2D resizedTexture = new Texture2D(resizedWidth, resizedHeight);
如果您可以将纹理装箱为精灵。然后 Sprite.Create 可以使用其 rect 参数为您处理裁剪:
void Update()
{
if (Input.GetKeyDown(KeyCode.S))
{
Texture2D originalTexture = (Texture2D)originalImage.mainTexture;
var textureCropArea = Rect.MinMaxRect(cropArea.rectTransform.anchoredPosition.x, cropArea.rectTransform.anchoredPosition.y, cropArea.rectTransform.rect.width, cropArea.rectTransform.rect.height);
croppedImage.texture = Sprite.Create(originalTexture, textureCropArea, Vector2.one / 2, 100).texture;
}
}
为什么它只适用于作物区域(00)的左下锚点而不适用于中间(0.5)
Texture2D originalTexture = originalImage.texture as Texture2D;
if (originalTexture == null)
{
Debug.LogError("Original image does not have a valid Texture2D.");
return;
}
Vector2 cropSize = cropArea.rect.size;
Vector2 cropPosition = cropArea.anchoredPosition;
// Adjust for the pivot (0.5, 0.5)
cropPosition -= cropSize / 2;
float widthRatio = originalTexture.width / originalImage.rectTransform.rect.width;
float heightRatio = originalTexture.height / originalImage.rectTransform.rect.height;
int x = Mathf.Clamp((int)(cropPosition.x * widthRatio), 0, originalTexture.width);
int y = Mathf.Clamp((int)(cropPosition.y * heightRatio), 0, originalTexture.height);
int width = Mathf.Clamp((int)(cropSize.x * widthRatio), 0, originalTexture.width - x);
int height = Mathf.Clamp((int)(cropSize.y * heightRatio), 0, originalTexture.height - y);
// Create a new texture for the cropped area
Texture2D croppedTexture = new Texture2D(width, height);
// Set pixels in the cropped texture
croppedTexture.SetPixels(originalTexture.GetPixels(x, y, width, height));
croppedTexture.Apply();
// Update the cropped image with the new texture
croppedImage.texture = croppedTexture;