Hoy toca hablar de otro #NuGetRecomendado. Este es tal vez uno de los más entretenidos que he usado en mi vida, espero que tu también lo encuentres así.

Es muy probable que ya te hayas enfrentado o que tengas que enfrentarte con este problema en el futuro: Procesamiento de imagenes. Puede ser algo tan simple como cargar y recortar las imagenes de perfil de usuarios a un sistema o aplicar filtos a fotos al más puro estilo de Instagram, como programadores comenzamos a imaginarnos y a investigar cómo podemos realizar esta tareas... o, tal vez busquemos mejor una librería para hacerlo.

Esta es precisamente una tarea para ImageProcessor, una librería para realizar algunas operaciones sobre imagenes con la ayuda de sus métodos, pero vamos a ver cómo se usa:

Comenzando

Vamos a trabajar con esta imagen:

Para comenzar a trabajar con una imagen, es necesario cargarla y con ImageProcessor esta tarea se realiza a través de una instancia de ImageFactory y un flujo de datos (en este caso un MemoryStream), los colocamos dentro de using para facilitarnos el manejo de memoria:

var balonBytes = File.ReadAllBytes("photo/balon.jpg");

using (var inStream = new MemoryStream(balonBytes))
using (var imageFactory = new ImageFactory(false))
{

Load

Una vez creada la imageFactory podemos cargar nuestra imagen usando el método Load:

var img = imageFactory.Load(inStream);

Resize

Una vez cargada, podemos comenzar a aplicar las transformaciones. Por ejemplo, cambiar el tamaño:

img.Resize(new Size(300, 0));

Save

Hasta este punto, tenemos la imagen modificada en memoria, así que nuestro imageFactory nos provee un método para guardarla en disco:

img.Save("photo/bolaResized.jpg");

Tras lo cual nos queda la siguiente imagen:

API fluída

Para nuestra conveniencia, ImageProcessor provee una API fluída (fluent API) para encadenar todos los métodos anteriores, por ejemplo, el código anterior se puede reescribir de la siguiente manera:

imageFactory.Load(inStream)
            .Resize(new Size(300, 0))
            .Save("photo/bolaResized.jpg");

Quality

Otra de las transformaciones que permite es la de reducir la calidad de una imagen, esto a través del método Quality que recibe número con la nueva calidad de la imagen:

imageFactory.Load(inStream)
            .Quality(5) // Only works with jpg
            .Save("photo/bolaLow.jpg");

Format

Trabajar con diversos formatos de archivo a veces puede ser un poco complicado, es por eso que esta librería también permite cambiar el formato de la imagen, por ejemplo, en esta convertimos la imagen de jpg a png:

imageFactory.Load(inStream)
            .Format(new PngFormat { Quality = 10 })
            .Save("photo/balon.png");

Transformaciones en la imagen

Además de cosas como cambio de formatos, de tamaño y de calidad ImageProcessor también permite modificar la imagen para aplicarle algunos efectos y transformaciones al contenido, para este ejemplo vamos a trabajar con la foto de la perrita Micha:

Filter

Al más puro estilo de Instagram, podemos aplicarle filtros a la imagen con el método Filter, la librería tiene ya definidos algunos filtos como sepia y escala de grises, entre otros. O si estás muy inspirado, puedes crear el tuyo propio:

imageFactory.Load(inStream)
            .Filter(MatrixFilters.HiSatch)
            .Save("photo/michaArt.jpg");

Y este es el resultado:

Qué sería de esta ibrería si no diera la capacidad de invertir los colores de una imagen. Esta transformación no requiere de mucha explicación, salvo que se usa el método `Filter` con el filtro `Invert`:
imageFactory.Load(inStream)
            .Filter(MatrixFilters.Invert)
            .Save("photo/michaInverse.jpg");

Y este es el resultado:

Michstagram (Filter, Tint y Saturation)

Aprovechando que se pueden encadenar varias transformaciones gracias a la API fluída podemos crear un pequeño instagram para Micha:

imageFactory.Load(inStream)
            .Filter(MatrixFilters.Sepia)
            .Tint(Color.LightSalmon)
            .Saturation(50)
            .Save("photo/michaInstagram.jpg");

Más transformaciones

ImageProcessor no se queda ahí, sino que también nos deja recortar recuadros de la imagen, girarla e invertirla respecto a determinado eje. Para la demostración mira la siguiente imagen:

Crop

```csharp var m = imageFactory.Load(inStream) .Crop(new Rectangle(100, 100, 250, 250)) .Save("photo/motherboardCropped.jpg"); ```

Rotate

```csharp m.Rotate(10f) .Save("photo/motherboardRotated.jpg"); ```

Flip

```csharp m.Flip(true, true) .Save("photo/motherboardFlipped.jpg"); ```

Extras

Usando una combinación de APIs se pueden crear aplicaciones más interesantes, por ejemplo, con el uso de los Cognitive Services y en especifico, el reconocimiento de caras, se pueden lograr cosas como estas:

```csharp Face face = null; using (var inStream = new MemoryStream(robbieBytes)) { var detectionTask = faceServiceClient.DetectAsync(inStream); detectionTask.Wait(); face = detectionTask.Result.FirstOrDefault(); }

var faceContainer = new Rectangle(face.FaceRectangle.Left, face.FaceRectangle.Top, face.FaceRectangle.Width, face.FaceRectangle.Height); using (var inStream = new MemoryStream(robbieBytes)) using (var imageFactory = new ImageFactory(false)) { imageFactory.Load(inStream) .Crop(faceContainer) .Save("photo/robbieFace.jpg"); }

</div>
<div class="pure-u-1 pure-u-md-1-4">
<img src="https://thatcsharpguy.github.io/postimages/imageprocessor/robbie3.jpg" title="Robbie Williams" />

<img src="https://thatcsharpguy.github.io/postimages/imageprocessor/robbieFace.jpg" title="Robbie Williams face" />
</div>
</div>     

O mejor aún, podemos detectar las caras aplicarle algunas transformaciones como pixelizarlas: 

```csharp  
Face[] faces;
using (var inStream = new MemoryStream(friendsBytes))
{
    var detectionTask = faceServiceClient.DetectAsync(inStream);
    detectionTask.Wait();
    faces = detectionTask.Result;
}

using (var inStream = new MemoryStream(friendsBytes))
using (var imageFactory = new ImageFactory(false))
{
    var friendsImage = imageFactory.Load(inStream);
    foreach (var f in faces)
    {
        var faceContainer = new Rectangle(f.FaceRectangle.Left, f.FaceRectangle.Top, 
            f.FaceRectangle.Width, f.FaceRectangle.Height);
        friendsImage.Pixelate(20, faceContainer);
    }
    friendsImage.Save("photo/friendsAnonymous.jpg");
}

Conclusión

No hay mucho que decir sobre ImageProcessor, salvo que es una librería muy potente y que nunca está de más contemplarla para proyectos en los que se requiere manipular imágenes. Si bien me parece que podrían ser añadidos otros métodos, los que tiene son de gran provecho. También ten en cuenta que yo no he hablado de todos los métodos disponibles, y te invito a que veas la documentación completa para conocerlos todos.

Instalación

Como siempre, hay que buscar en el gestor de paquetes de NuGet: ImageProcessor

O desde la consola:

PM> Install-Package ImageProcessor

No olvides echarle un ojo al proyecto en GitHub o en el sitio web del proyecto. Y de nueva cuenta, te invito a descargar el código y hagas pruebas con él.