Pandas – imagen a DataFrame

Quiero convertir una imagen RGB en un DataFrame, de modo que tenga las coordenadas de cada píxel y su valor RGB.

xy red green blue 0 0 0 154 0 0 1 1 0 149 111 0 2 2 0 153 0 5 3 0 1 154 0 9 4 1 1 154 10 10 5 2 1 154 0 0 

Puedo extraer el RGB en un DataFrame muy fácilmente

 colourImg = Image.open("test.png") colourPixels = colourImg.convert("RGB") colourArray = np.array(colourPixels.getdata()) df = pd.DataFrame(colourArray, columns=["red","green","blue"]) 

Pero no sé cómo obtener las coordenadas X e Y allí. Podría escribir un bucle, pero en una imagen grande que toma mucho tiempo.

Trate de usar np.indices desafortunadamente, termina con una matriz donde la coordenada es la primera dimensión, pero puede hacer un poco de np.moveaxis para arreglar eso.

 colourImg = Image.open("test.png") colourPixels = colourImg.convert("RGB") colourArray = np.array(colourPixels.getdata()).reshape(colourImg.size + (3,)) indicesArray = np.moveaxis(np.indices(colourImg.size), 0, 2) allArray = np.dstack((indicesArray, colourArray)).reshape((-1, 5)) df = pd.DataFrame(allArray, columns=["y", "x", "red","green","blue"]) 

No es el más bonito, pero parece funcionar (edita: se corrigió x, y es el camino equivocado).

He nombrado las coordenadas ‘col’ y ‘row’ para que sean explícitas y evitar confusiones si la coordenada x se refiere al número de columna o al número de fila de su matriz de píxeles original:

 A = colourArray # Create the multiindex we'll need for the series index = pd.MultiIndex.from_product( (*map(range, A.shape[:2]), ('r', 'g', 'b')), names=('row', 'col', None) ) # Can be chained but separated for use in explanation df = pd.Series(A.flatten(), index=index) df = df.unstack() df = df.reset_index().reindex(columns=['col', 'row', 'r', 'g', 'b']) 

Explicación:

pd.Series(A.flatten(), index=index) creará una serie multiindex donde se puede acceder a cada intensidad de canal a través de df[row_n, col_n][channel_r_g_or_b] . La variable df (actualmente una serie) ahora tendrá un aspecto similar al siguiente:

 row col 0 0 r 116 g 22 b 220 1 r 75 g 134 b 43 ... 255 246 r 79 g 9 b 218 247 r 225 g 172 b 172 

unstack() girará el tercer índice (índice de canal), devolviendo un dataframe con las columnas b , g , r con cada fila indexada por un índice (row_n, col_n) . El df ahora se ve así:

  bgr row col 0 0 220 22 116 1 43 134 75 2 187 97 33 ... ... ... ... 255 226 156 242 128 227 221 63 212 228 75 110 193 

Luego llamamos a reset_index() para eliminar el (row_n, col_n) y solo tenemos un 0..⁠(n_pixels-1) plano de 0..⁠(n_pixels-1) . El df es ahora:

  row col bgr 0 0 0 220 22 116 1 0 1 43 134 75 2 0 2 187 97 33 ... ... ... ... ... ... 65506 255 226 156 242 128 65507 255 227 221 63 212 65508 255 228 75 110 193 

Y luego un simple reindex() para reorganizar las columnas en orden de col , row , r , g , b .


Tiempos:

Ahora, en cuanto a qué tan rápido se ejecuta esto, bueno … para una imagen de 3 canales, aquí están los horarios:

 Size Time 250x250 58.2 ms 500x500 251 ms 1000x1000 1.03 s 2500x2500 8.14 s 

Es cierto que no es bueno en imágenes> 1 MP. unstack() puede tardar un tiempo después de que el df sea muy grande.

He probado la solución de @ davidsheldon y se ejecutó mucho más rápido, para una imagen de 2500×2500, tomó 244 ms y una imagen de 10000×10000 tomó 9.04 s.