Crop una imagen usando otro Rect in Unity

Tengo una Texture2D, aplicada en una RawImage, tomada de una webcam, que llamo "originalImage". Quiero recortar sólo un bloque de este original Imagen en base a la posición de otro rect, llamado "cropArea". Basically cropArea es una "guía" sobre mi original Imágen donde quiero que el usuario ponga su mano, para que pueda conseguir otra imagen por sólo la mano.

El problema es que no puedo conseguir los píxeles correctos de la imagen original en relación con la posición de la cosecha. La zona de cultivo en realidad tiene un offset relativo a la posición de CultivoArea que no puedo entender. Aquí hay una imagen que muestra mi problema (Usé una imagen aleatoria tomada de google como originalImage Texuter2D en lugar de mi webcam)

Crop Edición Captura

Tengo cosechaÁrea como hijo de original Imagen en mi Jerarquía, y así es como ambos fueron establecidos en mi inspector cuando tomé la captura de pantalla arriba.

Images in Inspector

Aquí está mi código (Es un código que he escrito para reproducir mi problema, así que no hay otros códigos involucrados en la función):

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

De nuevo, he probado muchas combinaciones en mi código, triyng para usar localPosition o rectificado en lugar de AncladoPosition para obtener la posición de Cultivo, obtener resultados diferentes, pero nunca el correcto.

Creo que tal vez me falte algunas relaciones entre la posición de cultivoArea y los píxeles para obtener de originalImage, pero realmente no puedo conseguir lo que es. Cada vez Tengo que lidiar con Unity UI en tiempo de ejecución me da dolor de cabeza, es tan confundido y anti-intuitivo.

Pregunta hecha hace 3 años, 4 meses, 29 días - Por techinnovator


6 Respuestas:

  • Ok, así como he escrito bajo la respuesta de @obywan, mi problema era que mi original La imagen estaba siendo estirada para llenar el lienzo, pero la fuente de cultivo no era la RawImage, sino la textura de RawImage, que no se estira en su lugar. Agradezco a Obywan por hacerme darme cuenta de que mi código estaba funcionando bien y por traerme a pensar que podría haber habido un problema de dimensión.

    He encontrado una solución de trabajo escribiendo una función para cambiar el tamaño de la textura, de modo que tengo una correspondencia entre la posición de píxeles de textura y lo que mis ojos realmente ven en la interfaz de usuario. Una vez redimensionado la textura, fui capaz de usar la posición de cultivoArea para obtener píxeles de la textura del tamaño, con el resultado deseado.

    Así que esta es la función de textura redimensionante:

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

    Este es mi método de actualización con la función ResizeTexture2D implementada para trabajar bien:

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

    Respondida el Dec 17, 2020 a las 11:26 - por syntaxsavvy

    Votos positivos: 0 | Votos negativos: 0

  • Tu guión funciona bien. Sólo tienes que ajustar la jerarquía y los pivotes. Puse el área de cultivo bajo la imagen principal y fijé el pivote del área de cultivo a 0,0 así como anclas min-max a 0,0,0,0.

    Ver imágenes. (puedes hacer preguntas en comentarios si hay alguna) enter image description here

    enter image description here

    enter image description here

    Respondida el Dec 17, 2020 a las 11:32 - por rubyrover

    Votos positivos: 0 | Votos negativos: 0

  • 4a línea en ResizeTexture2 D should be: Texture2D resizedTexture = new Texture2D(resizedWidth, resizedHeight);

    Respondida el Dec 17, 2020 a las 11:37 - por algoarchitect

    Votos positivos: 0 | Votos negativos: 0

  • Si estás bien boxeando la textura como Sprite. Entonces... Sprite. Crear puede manejar el cultivo para usted utilizando su argumento de 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;
        }
    }
    

    Respondida el Dec 17, 2020 a las 11:42 - por quantumcoder403a

    Votos positivos: 0 | Votos negativos: 0

  • El problema que estás experimentando al recortar la imagen puede deberse a la forma en que estás manejando las coordenadas y tamaños de los RectTransforms en relación con los píxeles de la imagen original. Para lograr un recorte preciso, necesitas realizar algunas conversiones y ajustes en las coordenadas.

    Aquí te dejo una posible solución para tu código, teniendo en cuenta las posiciones y tamaños de los RectTransforms y realizando las conversiones necesarias:

    ```csharp [SerializeField] private RawImage originalImage; [SerializeField] private RawImage cropArea; [SerializeField] private RawImage croppedImage;

    void Update() { if (Input.GetKeyDown(KeyCode.S)) { // Obtener la textura original y las dimensiones de CropArea Texture2D originalTexture = (Texture2D)originalImage.mainTexture; Rect cropRect = cropArea.rectTransform.rect;

        // Convertir las coordenadas locales de CropArea a posiciones globales
        Vector3 cropAreaGlobalPosition = cropArea.rectTransform.TransformPoint(cropRect.position);
    
        // Calcular el offset entre la posición global de CropArea y la posición de la imagen original
    

    Respondida el Dec 18, 2020 a las 11:24 - por Gpt

    Votos positivos: 0 | Votos negativos: 0

  • El problema que estás experimentando al recortar una imagen usando otro Rect en Unity se debe a un malentendido en cómo se calculan las coordenadas de recorte. Aquí tienes una explicación del problema y una solución:

    Problema:

    Las coordenadas de recorte que utilizas en tu código (int)cropArea.rectTransform.anchoredPosition.x, (int)cropArea.rectTransform.anchoredPosition.y representan la posición anclada del Rect cropArea en relación con su padre, que es originalImage. Estas coordenadas no reflejan la posición absoluta de cropArea en el espacio de pantalla, que es lo que necesitas para recortar la imagen original.

    Solución:

    Para obtener las coordenadas de recorte correctas, debes convertir las coordenadas ancladas de cropArea a coordenadas absolutas en el espacio de pantalla. Esto se puede hacer utilizando el método RectTransform.GetWorldCorners de la siguiente manera:

    
    Vector3[] corners = new Vector3[4];
    cropArea.rectTransform.GetWorldCorners(corners);
    

    Las esquinas representan las cuatro esquinas del Rect cropArea en el espacio de pantalla. Las coordenadas de recorte que necesitas son las coordenadas de la esquina inferior izquierda, que es corners[0].

    Actualiza tu código para usar las coordenadas absolutas de recorte:

    
    Texture2D croppedTexture = new Texture2D((int)cropArea.rectTransform.rect.width, (int)cropArea.rectTransform.rect.height);
    Texture2D originalTexture = (Texture2D)originalImage.mainTexture;
    
    Vector3[] corners = new Vector3[4];
    cropArea.rectTransform.GetWorldCorners(corners);
    
    croppedTexture.SetPixels(originalTexture.GetPixels((int)corners[0].x, (int)corners[0].y, (int)cropArea.rectTransform.rect.width, (int)cropArea.rectTransform.rect.height));
    croppedTexture.Apply();
    croppedImage.texture = croppedTexture;
    

    Este código debería producir un recorte correcto de la imagen original en base a la posición de cropArea.

    Consejo:

    Para depurar problemas como este, es útil visualizar las coordenadas de recorte y asegurarse de que son las esperadas. Puedes utilizar el método Debug.Log para imprimir las coordenadas en la consola.

    Respondida el Dec 18, 2020 a las 11:29 - por Gemini

    Votos positivos: 0 | Votos negativos: 0