---
title: "Arreglo (*array*) de pixeles"
---

Processing nos permite acceder y modificar a los pixeles de una imagen a través de una estructura sistematizada llamada arreglo (*array*).

[[Arreglos: Fundamentos]](/notas/arreglos.html)

# Fundamentos

## Atributo `.pixels`

Dada una variable de tipo `PImage` llamada `imagen`, su arreglo de pixeles lo obtenemos via  `imagen.pixels`.

## Tipo de dato

El arreglo de pixeles consiste en un arreglo de tipo `color[]`, pues cada pixel lo podemos entender como una unidad fundamental de la imagen que solo tiene/es un color. 

## Dimensiones

El arreglo de pixeles tendrá `imagen.width*imagen.height` elementos, o bien `imagen.pixels.length`.

El primer pixel (`imagen.pixels[0]`) es el de la esquina superior izquierda, el segundo pixel es el que le sigue a la derecha, y así sucesivamente: de izquierda a derecha y de arriba a abajo.

El último pixel es el de la esquina inferior derecha.

## Lectura y escritura

Antes de realizar operaciones sobre el arreglo de pixeles, necesitamos "cargarlos" de la siguiente forma:

```java
imagen.loadPixels();
```

Después de modificarlos, actualizamos la imagen de la siguiente forma:

```java
imagen.updatePixels();
```

Para leer y escribir pixeles usamos la notación de arreglos:

```java
// almacena el color del primer pixel:
color pixel = imagen.pixels[0];

// colorea de blanco al pixel con índice 200
imagen.pixels[200] = color(255);
```

# Ciclos

La principal ventaja de tener acceso a los pixeles a través de un arreglo, es que podemos utilizar un ciclo `for` (o `while`) para iterar sobre cada uno de ellos y entonces leerlos y modificarlos.

## Una dimensión: índice

Esta es una plantilla de  ciclo `for` que usa a una variable `int i` para iterar sobre cada uno de los pixeles.

```java
imagen.loadPixels();
for( int i=0; i<imagen.pixels.length; i++){
  // lee el color del pixel
  color pixel = imagen.pixels[i];

  // escribe el color del pixel
  imagen.pixels[i] = pixel;
}
imagen.updatePixels();
```

### Coordenadas a partir de índice

En ocasiones necesitaremos obtener las coordenadas `x, y` que corresponden al índice `i`. 

Partiendo de que la imagen tiene `imagen.height` filas de `imagen.width` pixeles cada uno, podemos calcular el número de fila (`y`) con una división, y el número de pixel en esa fila (`x`) con el operador módulo, que es el residuo de la división:

```java
int x = i % imagen.width;
int y = i / imagen.width;
```

[[Operador módulo]](/notas/modulo.html)

## Dos dimensiones

Esta plantilla usa dos ciclos anidados, uno con la variable `y` para recorrer las filas de pixeles, y otro con la variable `x` para recorrer los pixeles de cada una.

### Índice a partir de coordenadas

En este caso necesitamos calcular el valor del índice respecto a esas variables `x`,`y`

```java
int i = x + y*imagen.width;
```

### Plantilla

```java
imagen.loadPixels();
for( int y=0; y<imagen.height; y++){
  for( int x=0; x<imagen.width; x++){
    int i = x + y*imagen.width;

    // lee el color del pixel
    color pixel = imagen.pixels[i];

    // escribe el color del pixel
    imagen.pixels[i] = pixel;
}
imagen.updatePixels();
```

*Nota: también podríamos utilizar `imagen.get(x,y)` para obtener el color del pixel, pero es una operación un poco más lenta que en ocasiones se va a notar*

# En el ciclo de animación

Si solo queremos realizar un proceso una vez, conviene hacerlo dentro de `setup()`. Si ese proceso requiere cambiar en el tiempo, habrá que hacerlo dentro de `draw()`.

La siguiente es una plantilla que carga una imagen y recorre sus pixeles sin cambiarlos. Más adelante se mostrarán posibilidades con las cuales reemplazar el ciclo `for` mostrado.

```java
PImage imagen;

void setup(){
  size(640, 480);

  imagen = loadImage("trajinera.jpg");
  imagen.resize(640, 480);

  // recorre pixeles:
  imagen.loadPixels();
  for( int i=0; i<imagen.pixels.length; i++){
    // lee el color del pixel
    color pixel = imagen.pixels[i];
  
    // escribe el color del pixel
    imagen.pixels[i] = pixel;
  }
  imagen.updatePixels();
}

void draw(){
  image(imagen, 0, 0);
}
```


# Información de color

Las siguientes funciones nos permiten obtener información específica sobre el color que leímos, como un número de tipo `float`. Sus nombres indican qué valor calculan a partir del color que utilicemos como argumento.

* `alpha( c )`
* `red( c )`
* `green( c )`
* `blue( c )`
* `hue( c )`
* `saturation( c )`
* `brightness( c )`

Por ejemplo, las siguientes líneas obtener el valor de brillo del primer pixel en el arreglo:

```java
color pixel = imagen.pixeles[ 0 ];
float brillo = brightness( pixel );
```

# Ejemplos de procesamientos por pixel

Las siguientes son ejemplos de procesamientos que buscan ser modificados y explorados.

Todos estos actúan sobre un pixel a la vez, y lo modifican respecto a sus propias características de color.

## Cambio *lineal* de niveles

### Eliminación de canal RGB

Este ejemplo lee los tres canales RGB y crea un nuevo color sin uno de ellos:

```java
imagen.loadPixels();
for( int i=0; i<imagen.pixels.length; i++){
  // lee el color del pixel
  color pixel = imagen.pixels[i];

  // obtén componentes RGB
  float rojo = red( pixel );
  float verde = green( pixel );
  float azul = blue( pixel );

  // asigna un nuevo color
  imagen.pixels[i] = color( 0, verde, azul);
}
imagen.updatePixels();
```

### Cambio de brillo

Este ejemplo lee los tres canales HSB y escala (multiplica) uno de ellos.

Nota el uso de `colorMode( )` para que el color creado funcione en HSB

```java
colorMode(HSB, 360, 100, 100);
imagen.loadPixels();
for( int i=0; i<imagen.pixels.length; i++){
  // lee el color del pixel
  color pixel = imagen.pixels[i];

  // obtén componentes HSB
  float hue = hue( pixel );
  float sat = saturation( pixel );
  float bri = brightness( pixel );

  // asigna un nuevo color con brillo al 80%
  imagen.pixels[i] = color( hue, sat, bri*0.8);
}
imagen.updatePixels();
```

### Más posibilidades

* ¿Qué pasa si intercambias la posición de los canales de color? 
* ¿O si usas solo uno de los canales como nuevo color/tono del pixel?
* ¿Y si sumas o restas alguna cantidad a los canales? ¿Y si esa cantidad es aleatoria?
* ¿Cómo podrías *invertir* los colores a través de arimética?
* ¿Y si usas `map()` para redimensionar el rango de alguno de los canales?

## Umbral (*Threshold*)

Podemos utilizar expresiones condicionales para modificar los pixeles de manera *no lineal*.

### Brillo

Este ejemplo usa el valor de brillo para decidir entre dos posibilidades: si el valor de brillo es mayor a cierta cantidad, deja el color como está, y si no lo reemplaza por negro.

```java
imagen.loadPixels();
for( int i=0; i<imagen.pixels.length; i++){
  // lee el color del pixel
  color pixel = imagen.pixels[i];

  // obtén componente del brillo 
  float bri = brightness( pixel );

  if ( bri > 50 ){ // si es brillo es mayor a 50%
    // asigna el color original
    imagen.pixels[i] = pixel;
  } else {
    imagen.pixels[i] = color(0);
  }
}
imagen.updatePixels();
```

### Chroma key

En este caso utilizamos un condicional más complejo para encontrar cierto rango de colores con valores determinados de *hue*, saturación y brillo. Los que cumplen la condición se vuelven transparentes, y los que no se dejan igual.

```java
imagen.loadPixels();
for( int i=0; i<imagen.pixels.length; i++){
  // lee el color del pixel
  color pixel = imagen.pixels[i];

  // obtén componentes HSB
  float hue = hue( pixel );
  float sat = saturation( pixel );
  float bri = brightness( pixel );

  // encuentra rango de colores via HSB
  if( hue > 85 && hue < 140 && sat > 20 && bri > 50){
    // asigna tono con transparencia total
    foto.pixels[i] = color( 0, 0 );
  } else {
    // deja el pixel como estaba:
    foto.pixels[i] = pixel;
  }
}
imagen.updatePixels();
```

### Más posibilidades

* ¿Y si dejas igual a los pixeles en cierto rango de colores, y modificas a los que no?
* ¿Y si a cada canal de color le aplicas un umbral para asignalo en uno u otro valor?
* ¿Y si tienes más de un condicional para asignar diferentes posibles niveles a cada canal?

# Ejemplos de procesamientos con pixeles aledaños

## Intercambio de pixeles

Este ejemplo intercambia el color de dos pixeles que son aledaños en el arreglo.

```java
imagen.loadPixels();
for( int i=0; i<imagen.pixels.length; i++){
  // lee el color del pixel
  color pixel = imagen.pixels[i];

  // calcula el índice de un pixel entre 0 y 10 lugares
  // más adelante en el arreglo
  int nuevoindice = (i + int(random(10)) ) % foto.pixels.length;

  // lee el color de ese pixel
  color pixel2 = imagen.pixels[nuevoindice];

  // asigna los valores intercambiados:
  imagen.pixels[i] = pixel2;
  imagen.pixels[nuevoindice] = pixel;

}
imagen.updatePixels();
```

Nota el uso del operador módulo al calcular el nuevo índice, para asegurarnos que el resultado siempre esté entre 0 y `foto.pixels.length - 1`.

[[Operador módulo]](/notas/modulo.html)
