Esta conversion se realiza calculando un equivalente «E» formado a partir de los tres planos de la imagen a color. En su forma mas sencilla se establece este equivalente como el promedio, es decir:

Podemos codificarlo de la siguiente manera:
from PIL import Image
foto = Image.open('ruta_y_nombre_del_archivo')
datos = foto.getdata()
#para el calculo del promedio se utilizara la division entera con el operador de division doble "//" para evitar decimales
promedio = [(datos[x][0] + datos[x][1] + datos[x][2]) // 3 for x in range(len(datos))]
imagen_gris = Image.new('L', foto.size)
imagen_gris.putdata(promedio)
imagen_gris.save('ruta_y_nombre_del_archivo')
foto.close()
imagen_gris.close()
Utilizando la imagen del logo de este blog revisaremos el resultado del codigo anterior:


La subjetiva iluminacion, propia del modelo RGB hace que utilizando el promedio las imagenes con un valor grande en la componente de rojo y/o verde tengan una apariencia obscura. El efecto contrario sucede donde el contenido del plano azul es grande, mostrando en su version a escala de grises una apariencia muy clara. Con el objetivo de solventar esto se considera como una mejor aproximacion calcular una combinacion lineal de todos los planos, definida como:

Donde WR, WG y WB son los coeficientes que definen la transformacion, los cuales de acuerdo al criterio utilizado en la TV para señales a color se consideran como:
WR=0.299 WG=0.587 WB=0.114
Justamente este es el metodo de conversion que provee el modulo Image de la biblioteca que hemos estado utilizando, el codigo es:
from PIL import Image
foto = Image.open('ruta_y_nombre_del_archivo')
imagen_gris = foto.convert('L')
imagen_gris.save('ruta_y_nombre_del_archivo')
foto.close()
imagen_gris.close()

Otra alternativa sugerida en ITU-BT.709 para la codificacion digital del color considera:
WR=0.2125 WG=0.7154 WB=0.072
Una consideracion de importancia es la distorsion Gamma producida en las señales de TV que afecta de manera no lineal por lo que en lugar de los valores mostrados anteriormente se podria hacer uso de un nuevo set de coeficientes :
WR=0.309 WG=0.609 WB=0.082
Para cualquiera de los dos casos anteriores podriamos codificarlo asi:
from PIL import Image
foto = Image.open('ruta_y_nombre_del_archivo')
datos = list(foto.getdata())
#tomando el valor de los coeficientes de ITU-BT.709
grises = [round((0.2125 * datos[x][0]) + (0.7154 * datos[x][1]) + (0.072 * datos[x][2])) for x in range(len(datos))]
imagen_gris = Image.new('L', foto.size)
imagen_gris.putdata(grises)
imagen_gris.save('ruta_y_nombre_del_archivo')
foto.close()
imagen_gris.close()

Se observa que la imagen por estos dos ultimos metodos es menos oscura que utilizando el promedio.
Peace
-Raziel
