Saltar al contenido

Cómo convertir RGB -> YUV -> RGB (en ambos sentidos)

Te doy la bienvenida a nuestro sitio, aquí encontrarás la solucíon de lo que buscas.

Solución:

Sí, existen transformaciones invertibles.

equasys GmbH publicó transformaciones invertibles de RGB a YUV, YCbCr e YPbPr, junto con explicaciones de para qué situación es apropiada cada una, de qué se trata realmente esta sujeción y enlaces a referencias. (Como una buena respuesta SO).

Para mi propia aplicación (imágenes jpg, no voltajes analógicos), YCbCr era apropiado, así que escribí código para esas dos transformaciones. De hecho, los valores de ida y vuelta diferían en menos de 1 parte en 256, para muchas imágenes; y las imágenes de antes y después eran visualmente indistinguibles.

La conversión de espacio de color de PIL YCbCr -> RGB recibe crédito por mencionar la página web de equasys.

Otras respuestas, que sin duda podrían mejorar la precisión y concisión de equasys:

  • https://code.google.com/p/imagestack/ incluye funciones rgb_to_x y x_to_rgb, pero no intenté compilarlas y probarlas.

  • La respuesta de Cory Nelson se vincula al código con funciones similares, pero dice que la inversión no es posible en general, lo que contradice las equasys.

  • El código fuente de FFmpeg, OpenCV, VLFeat o ImageMagick.

Edición de 2019: Aquí está el código C de github, mencionado en mi comentario.

void YUVfromRGB(double& Y, double& U, double& V, const double R, const double G, const double B)

  Y =  0.257 * R + 0.504 * G + 0.098 * B +  16;
  U = -0.148 * R - 0.291 * G + 0.439 * B + 128;
  V =  0.439 * R - 0.368 * G - 0.071 * B + 128;

void RGBfromYUV(double& R, double& G, double& B, double Y, double U, double V)

  Y -= 16;
  U -= 128;
  V -= 128;
  R = 1.164 * Y             + 1.596 * V;
  G = 1.164 * Y - 0.392 * U - 0.813 * V;
  B = 1.164 * Y + 2.017 * U;

RGB a YUV y viceversa

Hay un bonito diagrama en Wikipedia sobre el tema de YUV que muestra el diseño de YUV420p. Sin embargo, si eres como yo, quieres NV21, a veces llamado YUV420sp, que entrelaza los componentes V y U en un solo plano, por lo que en este caso ese diagrama es incorrecto, pero te da la intuición de cómo funciona.

Este formato (NV21) es el formato de imagen estándar en la vista previa de la cámara de Android. Imagen plana YUV 4: 2: 0, con muestras de Y de 8 bits, seguida de un plano V / U intercalado con muestras de croma submuestreadas de 2×2 de 8 bits.

Entonces, una gran cantidad de código que he visto comienza a codificarse literalmente según esta especificación sin tener en cuenta Endianess. Además, tienden a admitir solo YUV a RGB y solo uno o dos formatos. Sin embargo, quería algo un poco más confiable y resulta que el código C ++ tomado del repositorio de código fuente de Android funciona. Es prácticamente C ++ y debería usarse fácilmente en cualquier proyecto.

Código JNI / C ++ que toma una imagen RGB565 y la convierte a NV21

Desde Java en este caso, pero fácilmente C o C ++, pasa una matriz de bytes que contiene la imagen RGB565 y genera una matriz de bytes NV21.

#include 
#include 
#include 

#include "Converters.h"



#define JNI(X) JNIEXPORT Java_algorithm_ImageConverter_##X

#ifdef __cplusplus
extern "C" 
#endif

void JNI(RGB565ToNV21)(JNIEnv *env, jclass *, jbyteArray aRGB565in, jbyteArray aYUVout, jint width, jint height) 
    //get jbyte array into C space from JVN
    jbyte *rgb565Pixels = env->GetByteArrayElements(aRGB565in, NULL);
    jbyte *yuv420sp = env->GetByteArrayElements(aYUVout, NULL);

    size_t pixelCount = width * height;

    uint16_t *rgb = (uint16_t *) rgb565Pixels;

    // This format (NV21) is the standard picture format on Android camera preview. YUV 4:2:0 planar
    // image, with 8 bit Y samples, followed by interleaved V/U plane with 8bit 2x2 subsampled
    // chroma samples.

    int uvIndex = pixelCount;
    for (int row = 0; row < height; row++) 
        for (int column = 0; column < width; column++) 
            int pixelIndex = row * width + column;
            uint8_t y = 0;
            uint8_t u = 0;
            uint8_t v = 0;

            chroma::RGB565ToYUV(rgb[pixelIndex], &y, &u, &v);
            yuv420sp[pixelIndex] = y;
            if (row % 2 == 0 && pixelIndex % 2 == 0) 
#if __BYTE_ORDER == __LITTLE_ENDIAN
                yuv420sp[uvIndex++] = u;
                yuv420sp[uvIndex++] = v;
#else
                yuv420sp[uvIndex++] = v;
                yuv420sp[uvIndex++] = u;
#endif
            
        
    

    //release temp reference of jbyte array
    env->ReleaseByteArrayElements(aYUVout, yuv420sp, 0);
    env->ReleaseByteArrayElements(aRGB565in, rgb565Pixels, 0);


#ifdef __cplusplus

#endif

Converters.h

Como verá en el encabezado, hay muchas opciones de conversión diferentes disponibles hacia / desde cualquier cantidad de formatos.

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef HW_EMULATOR_CAMERA_CONVERTERS_H
#define HW_EMULATOR_CAMERA_CONVERTERS_H
#include 
#ifndef __BYTE_ORDER
#error "could not determine byte order"
#endif
/*
 * Contains declaration of framebuffer conversion routines.
 *
 * NOTE: RGB and big/little endian considerations. Wherever in this code RGB
 * pixels are represented as WORD, or DWORD, the color order inside the
 * WORD / DWORD matches the one that would occur if that WORD / DWORD would have
 * been read from the typecasted framebuffer:
 *
 *      const uint32_t rgb = *reinterpret_cast(framebuffer);
 *
 * So, if this code runs on the little endian CPU, red color in 'rgb' would be
 * masked as 0x000000ff, and blue color would be masked as 0x00ff0000, while if
 * the code runs on a big endian CPU, the red color in 'rgb' would be masked as
 * 0xff000000, and blue color would be masked as 0x0000ff00,
 */
namespace chroma  (r))
/* Build RGB32 dword from red, green, and blue bytes. */
#define RGB32(r, g, b) static_cast((((static_cast(b) << 8) ; /* namespace chroma */
#endif  /* HW_EMULATOR_CAMERA_CONVERTERS_H */

Converters.cpp

/*
 * Copyright (C) 2011 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/*
 * Contains implemenation of framebuffer conversion routines.
 */
#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_Converter"
#include "Converters.h"
namespace chroma 
    static void _YUV420SToRGB565(const uint8_t* Y,
                                 const uint8_t* U,
                                 const uint8_t* V,
                                 int dUV,
                                 uint16_t* rgb,
                                 int width,
                                 int height)
    
        const uint8_t* U_pos = U;
        const uint8_t* V_pos = V;
        for (int y = 0; y < height; y++) 
            for (int x = 0; x < width; x += 2, U += dUV, V += dUV) 
                const uint8_t nU = *U;
                const uint8_t nV = *V;
                *rgb = YUVToRGB565(*Y, nU, nV);
                Y++; rgb++;
                *rgb = YUVToRGB565(*Y, nU, nV);
                Y++; rgb++;
            
            if (y & 0x1) 
                U_pos = U;
                V_pos = V;
             else 
                U = U_pos;
                V = V_pos;
            
        
    
    static void _YUV420SToRGB32(const uint8_t* Y,
                                const uint8_t* U,
                                const uint8_t* V,
                                int dUV,
                                uint32_t* rgb,
                                int width,
                                int height)
    
        const uint8_t* U_pos = U;
        const uint8_t* V_pos = V;
        for (int y = 0; y < height; y++) 
            for (int x = 0; x < width; x += 2, U += dUV, V += dUV) 
                const uint8_t nU = *U;
                const uint8_t nV = *V;
                *rgb = YUVToRGB32(*Y, nU, nV);
                Y++; rgb++;
                *rgb = YUVToRGB32(*Y, nU, nV);
                Y++; rgb++;
            
            if (y & 0x1) 
                U_pos = U;
                V_pos = V;
             else 
                U = U_pos;
                V = V_pos;
            
        
    
    void YV12ToRGB565(const void* yv12, void* rgb, int width, int height)
    
        const int pix_total = width * height;
        const uint8_t* Y = reinterpret_cast(yv12);
        const uint8_t* U = Y + pix_total;
        const uint8_t* V = U + pix_total / 4;
        _YUV420SToRGB565(Y, U, V, 1, reinterpret_cast(rgb), width, height);
    
    void YV12ToRGB32(const void* yv12, void* rgb, int width, int height)
    
        const int pix_total = width * height;
        const uint8_t* Y = reinterpret_cast(yv12);
        const uint8_t* V = Y + pix_total;
        const uint8_t* U = V + pix_total / 4;
        _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast(rgb), width, height);
    
    void YU12ToRGB32(const void* yu12, void* rgb, int width, int height)
    
        const int pix_total = width * height;
        const uint8_t* Y = reinterpret_cast(yu12);
        const uint8_t* U = Y + pix_total;
        const uint8_t* V = U + pix_total / 4;
        _YUV420SToRGB32(Y, U, V, 1, reinterpret_cast(rgb), width, height);
    
/* Common converter for YUV 4:2:0 interleaved to RGB565.
 * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
 */
    static void _NVXXToRGB565(const uint8_t* Y,
                              const uint8_t* U,
                              const uint8_t* V,
                              uint16_t* rgb,
                              int width,
                              int height)
    
        _YUV420SToRGB565(Y, U, V, 2, rgb, width, height);
    
/* Common converter for YUV 4:2:0 interleaved to RGB32.
 * y, u, and v point to Y,U, and V panes, where U and V values are interleaved.
 */
    static void _NVXXToRGB32(const uint8_t* Y,
                             const uint8_t* U,
                             const uint8_t* V,
                             uint32_t* rgb,
                             int width,
                             int height)
    
        _YUV420SToRGB32(Y, U, V, 2, rgb, width, height);
    
    void NV12ToRGB565(const void* nv12, void* rgb, int width, int height)
    
        const int pix_total = width * height;
        const uint8_t* y = reinterpret_cast(nv12);
        _NVXXToRGB565(y, y + pix_total, y + pix_total + 1,
                      reinterpret_cast(rgb), width, height);
    
    void NV12ToRGB32(const void* nv12, void* rgb, int width, int height)
    
        const int pix_total = width * height;
        const uint8_t* y = reinterpret_cast(nv12);
        _NVXXToRGB32(y, y + pix_total, y + pix_total + 1,
                     reinterpret_cast(rgb), width, height);
    
    void NV21ToRGB565(const void* nv21, void* rgb, int width, int height)
    
        const int pix_total = width * height;
        const uint8_t* y = reinterpret_cast(nv21);
        _NVXXToRGB565(y, y + pix_total + 1, y + pix_total,
                      reinterpret_cast(rgb), width, height);
    
    void NV21ToRGB32(const void* nv21, void* rgb, int width, int height)
    
        const int pix_total = width * height;
        const uint8_t* y = reinterpret_cast(nv21);
        _NVXXToRGB32(y, y + pix_total + 1, y + pix_total,
                     reinterpret_cast(rgb), width, height);
    
; /* namespace chroma */

Una vez que lo sujetes, habrás terminado. Se vuelven de un color diferente y no puedes volver atrás. He escrito algo de mi propio código para convertir entre todos esos y más si desea ver, pero no ayudará a revertir los colores fijados a sus originales.

Comentarios y calificaciones de la guía

Agradecemos que quieras sustentar nuestro análisis dejando un comentario y valorándolo te estamos agradecidos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *