unity-game-engineuser-interfacetexture2d

Crop an image using another Rect in Unity


I have a Texture2D, applied on a RawImage, taken from a webcam, which I call "originalImage". I want to crop just a block of this originalImage in base of the position of another rect, called "cropArea". Basically cropArea is a "guide" over my originalImage where I want the user to put his hand, so that I can get another image for just the hand.

The problem is that I can't get the correct pixels of the originalImage relative to the cropArea position. The actually cropped area has an offset relative to cropArea's position I can't understand. Here's a picture showing my problem (I used a random image taken from google as originalImage Texuter2D instead of my webcam)

Crop Issue Screenshot

I have cropArea as child of originalImage in my Hyerarchy, and here's how they were both set up on my inspector when I took the screenshot above.

Images in Inspector

Here's my code (It's a code I've written to reproduce my problem, so there's no other codes involved in the function):

[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;
    }
}

Again, I've tried many combinations in my code, triyng to use localPosition or rect instead of anchoredPosition to get cropArea's position, getting different results, but never the correct one.

I think I maybe missing some relations between the position of cropArea and the pixels to get from originalImage, but I really can't get what is it. Everytime I have to deal with Unity UI in runtime it gets me headache, it's so confused and anti-intuitive.


Solution

  • Ok, so as I've written under @obywan answer, my problem was that my originalImage was being stretchet to fill the canvas, but the source to crop wasn't the RawImage, but RawImage's texture, which doesn't get stretched instead. I thank obywan for making me realize that my code was actually working fine and for bringing me to think that there might have been a dimension problem.

    I've found a working solution writing a function to resize the texture, so that I have a correspondence between texture pixels position and what my eyes actually see in the UI. Once resized the texture, I was able to use cropArea position to get pixels from the resized texture, with the wanted result.

    So, this is the resizing texture function:

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

    This is instead my Update method with ResizeTexture2D function implemented to work fine:

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