Convertir RGB a escala de grises

Una de las operaciones más habituales que se hace en visión por computador es convertir una imagen a color a una en escala de grises. Es una operación bastante simple pero hay múltiples opciones para real izarlo. Todas se basan en lo mismo, combinar los valores de los tres canales RGB para obtener un solo canal. Aunque hay distintos formatos, para este post voy a poner el caso de que los pixeles en color están en codificados como RGB con un byte por cada canal y que la información en escala de grises emplead un soo canal de un byte. Como vemos hemos de reducir tres bytes a solo uno.

Vamos usar una función que nos servirá como para la mayoría de los casos, en ella se le pasan los tres canales y los pesos asignados a cada uno, se multiplica cada canal por su correspondiente peso y se suman. Al ser un ejemplo no se realiza ningún tipo de verificación de los valores que se pasan ni del resultado.

function RGBtoGS(r,g,b,kr,kg,kb){
 return kr*r + kg*g + kb*b;
}

El caso más sencillo y rápido es tomar el valor de uno solo de los canales. Esto se puede usar cuando uno de los canales tiene más información o sufre menos el ruido. Hay que tener en cuenta que se pierde la información de los otros dos canales.

RGBtoGS(r,g,b,1,0,0); //devolver el canal rojo como gris
RGBtoGS(r,g,b,0,1,0); //devolver el canal verde como gris
RGBtoGS(r,g,b,0,0,1); //devolver el canal azul como gris

Una variación de esta técnica consiste en no coger siempre el mismo canal si no en coger el más o el menos brillante. Tienen la ventaja de ser rápido y de aportar más información que el caso anterior. Si la imagen es muy oscura (subexpuesta) elegir el canal más brillante puede ayudar a sacar detalles que de otra forma quedan ocultos en las zonas oscuras, si es demasiado luminosa (sobreexpuesta) el menos brillante puede aportar información que de otra manera quedaría quemada. Y ya que estamos podemos optar por el punto medio y calcular la media de la suma entre el canal más y el menos luminoso de cada píxel.

function greaterRGBtoGS(r,g,b){
  if(r > g && r > b){
    return r;
  } else if(g > r && g > b){
    return g;
  } else{
    return b;
  }
}
function lesserRGBtoGS(r,g,b){
if(r &lt; g &amp;&amp; r <b> r &amp;&amp; g &gt; b){
    return g;
  }else{
    return b;
  }
}

function averageRGBtoGS(r,g,b){
  return (greaterRGBtoGS(r,g,b)+lesserRGBtoGS(r,g,b))/2
}

Sin embargo la manera más habitual de hacerlo es ponderar los tres valores con tres constantes. La forma más fácil que se nos ocurre es simplemente calcular la media de los tres valores (o multiplicar cada uno por 0.33). Pero esta solución no es acorde con la realidad en la que por diversos motivos (desde físicos a biológicos como que nuestros ojos no son igual de sensibles a cada componente) cada canal no aporta lo mismo. Para ello existen distintas recomendaciones de que valores usar para ponderar cada canal. Incluyo algunos ejemplos.

RGBtoGS(r,g,b,0.33,0.33,0.33); //media
RGBtoGS(r,g,b,0.2126,0.7152,0.0722); //CIE 1931
RGBtoGS(r,g,b,0.299,0.587,0.114); // rec601 luma
RGBtoGS(r,g,b,0.2627,0.6780,0.593); // ITU-R BT.2100

Aunque lo recomendado es ponderar los tres canales, desde mi limitada experiencia el de máximo brillo me ha dado buenos resultados sobre todo con imágenes obtenidas con cámaras de móviles y portátiles. Además tiene la ventaja de ser muy rápido.