Archivos de la categoría utilidades

Rotar imágenes según sus metadatos de orientación

¡Hola!

Hay herramientas que se te ocurren pensando en qué cosas podrían ser útiles; y otras que surgen cuando te topas con un problema y dices: ¡joder, seguro que hay alguna solución para esto!

El otro día, mi mujer, @amaterasu_n estaba escribiendo en su blog de mami the vikings mama un post, y quería poner algunas fotos. Al ser ciegos, el tema de las imágenes en los posts es algo complejo. Nunca sabemos si la foto ha quedado bien, o se corta, o se descentra… así que después de ponerlas en el post, preguntamos a una compañera de trabajo, y me dijo que había fotos que estaban giradas… Ya os podéis imaginar, la típica foto tomada en horizontal, y que necesitas girar para que no tengas que darle la vuelta a la pantalla.

Así que me dije: Los smartphones tienen acelerómetros y giroscopios para detectar el cambio de orientación y adaptar las interfaces a esos cambios. ¿No guardarán esta información en los metadatos exif de las fotos? Y sorprendentemente, ¡la respuesta es sí! Imagino que también habrá cámaras que lo hacen, pero no he podido comprobarlo.

Buscando un poco por internet, me encontré con ExifExtractor, una pequeña librería en c# que me permitía extraer los datos exif de las imágenes. Por otro lado, en este artículo sobre las etiquetas exif encontré los valores y significados de esta etiqueta…

¡Problema resuelto!

He desarrollado una pequeñísima aplicación en c# que dada una imagen, extrae la orientación, la rota o voltea (rotate / flip) en consecuencia e informa al usuario de la operación que ha realizado sobre ella.

Podéis descargar la herramienta FixPictureOrientation desde este enlace.

Solo tenéis que descomprimirlo en alguna carpeta, ejecutar el exe y pulsar el botón de abrir y corregir foto, buscar la foto, abrirla… ¡y ya está!

Nota: Para ejecutar el programa deberéis tener instalado el .NET Framework 4 (si usáis Windows 7 o superior ya viene por defecto, y si no, casi seguro que lo tenéis de todos modos 😉 Si no fuera así, podéis descargar el instalador del .NET Framework 4 desde este enlace

El código c# es muy sencillo (obviando toda la parte de la extracción de datos Exif, cuyo código podéis encontrar en el sitio de codeproject que os puse arriba:

using Goheer.EXIF;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace FixPictureOrientation
{
	public partial class FrmPrincipal : Form
	{
		public FrmPrincipal()
		{
			InitializeComponent();
		}

		private void BtnCorregir_Click(object sender, EventArgs e)
		{
			OpenFileDialog dlg = new OpenFileDialog()
			{
				Filter = "Archivos de imagen JPG (*.jpg)|*.jpg",
				Title = "Abrir foto",
				ShowReadOnly = false,
				ShowHelp = false,
				InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyComputer),
				CheckFileExists = true
			};
			DialogResult res = dlg.ShowDialog();
			if (res == DialogResult.Cancel) return;
			var rutaImagen = dlg.FileName;
			// Rotamos la imagen de acuerdo a los datos de exif
			Bitmap bmp;
			try
			{
				bmp = new Bitmap(rutaImagen);
			}
			catch (Exception)
			{
				MessageBox.Show("¡Pero qué me has dado! Esto no parece una imagen válida!", "¡Esto no se come!", MessageBoxButtons.OK, MessageBoxIcon.Error);
				return;
			}
			try
			{
				var exif = new EXIFextractor(ref bmp, Environment.NewLine);
				if (exif["Orientation"] != null)
				{
					int iOrientacion;
					if (!Int32.TryParse(exif["Orientation"].ToString(), out iOrientacion))
					{
						MessageBox.Show("Los datos de orientación parecen no ser correctos. ¡Disculpa!", "Uups!", MessageBoxButtons.OK, MessageBoxIcon.Error);
						return;
					}
					string rotado;
					RotateFlipType tipoRotacion = GetDesiredRotation(iOrientacion, out rotado);
					if (tipoRotacion == RotateFlipType.RotateNoneFlipNone)
					{
						MessageBox.Show("¡Esta foto no necesita ninguna rotación! ¡Puedes usarla sin problemas!", "¡Esta ya está!", MessageBoxButtons.OK, MessageBoxIcon.Information);
						return;
					}

					bmp.RotateFlip(tipoRotacion);
					var equivalenciasExif = new Goheer.EXIF.translation();
					int idOrientacion = equivalenciasExif.Keys.OfType<int>().FirstOrDefault(k => equivalenciasExif[k].ToString() == "Orientation");
					// Reajustamos la propiedad de orientación para que coincida con la orientación actual.
					bmp.RemovePropertyItem(idOrientacion);
					exif.setTag(idOrientacion, "1");
					bmp.Save(rutaImagen, ImageFormat.Jpeg);
					MessageBox.Show($"¡Perfecto! La imagen se ha rotado {rotado}.", "¡Listo!", MessageBoxButtons.OK, MessageBoxIcon.Information);
				}
				else
				{
					MessageBox.Show("Lo siento, no hay datos de orientación en los metadatos de la foto... ¡No puedo detectar la posición de la fotografía!", "Me cachis!", MessageBoxButtons.OK, MessageBoxIcon.Information);
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show($"Se produjo un error inesperado: {ex.Message}.", "Uuups.", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
			finally
			{
				if (bmp != null) bmp.Dispose();
			}
		}
		
		private void BtnSalir_Click(object sender, EventArgs e)
		{
			this.Close();
		}

		/// <summary>
		///  Devuelve la rotación que habría que realizar para que la foto rote a la posición adecuada en función del dato de orientación EXIF
		/// </summary>
		/// <param name="orientation"> Valor numérico de orientación que indica la orientación actual de la foto</param>
		/// <param name="rotationExplanation">Explicación de qué rotación se va a realizar</param>
		/// <returns>Uno de los valores de la enumeración RotateFlipType indicando la rotación que se debe aplicar</returns>
		private static RotateFlipType GetDesiredRotation(int orientation, out string rotationExplanation)
		{
			RotateFlipType rotationType;
			switch (orientation)
			{
				case 1: // fila 0 = arriba, columna 0 = lado izquierdo
					rotationExplanation = "0 grados";
					rotationType = RotateFlipType.RotateNoneFlipNone;
					break;
				case 2: // fila 0 = arriba, columna 0 = lado derecho
					rotationExplanation = "0 grados y volteo horizontal";
					rotationType = RotateFlipType.RotateNoneFlipX;
					break;
				case 3: // fila 0 = abajo, columna 0 = lado derecho
					rotationExplanation = "180 grados en sentido horario";
					rotationType = RotateFlipType.Rotate180FlipNone;
					break;
				case 4: // fila 0 = abajo, columna 0 = lado izquierdo
					rotationExplanation = "180 grados en sentido horario y volteo horizontal";
					rotationType = RotateFlipType.Rotate180FlipX;
					break;
				case 5: // fila 0 = lado izquierdo, columna 0 = arriba
					rotationExplanation = "90 grados en sentido horario y volteo horizontal";
					rotationType = RotateFlipType.Rotate90FlipX;
					break;
				case 6: // fila 0 = lado derecho, columna 0 = arriba
					rotationExplanation = "90 grados en sentido horario";
					rotationType = RotateFlipType.Rotate90FlipNone;
					break;
				case 7: // fila 0 = lado derecho, columna 0 = abajo
					rotationExplanation = "270 grados en sentido horario y volteo horizontal";
					rotationType = RotateFlipType.Rotate270FlipX;
					break;
				case 8: // fila 0 = lado izquierdo, columna 0 = abajo
					rotationExplanation = "270 grados en sentido horario";
					rotationType = RotateFlipType.Rotate270FlipNone;
					break;
				default:
					rotationExplanation = "0 grados";
					rotationType = RotateFlipType.RotateNoneFlipNone;
					break;
			}
			return rotationType;
		}

	}
}

¡Espero que os sea útil!

¡Un saludo!

Entrada visitada 581 veces

Reproductor accesible html5

¡Hola!

Hace algunos meses, Salvi Melguizo me pidió que le hiciera un reproductor accesible para su blog, así que como me gusta cacharrear y ganarme un sobresueldo cuando se puede :P, a ello me puse.
Se trata de un script jQuery que, por cada elemento audio de HTML5 de una página, genera un conjunto de controles encima del elemento, que permiten manejar de forma totalmente accesible dicho reproductor.

El reproductor está probado con Internet Explorer 9, 10 y 11, Firefox 42 y 43, Chrome 47 y Safari con iOS 8 y 9. Funciona correctamente con JAWS, NVDA y VoiceOver para MAC e iOS… He de confesar que con window Eyes no lo he probado 😉

Podéis ver
una demo del reproductor accesible aquí.
El reproductor está en español, aunque por defecto el ejemplo está con la traducción al inglés macarrónico (lo he traducido yo), así que si alguien se anima a corregir la traducción, yo encantado.
Podéis colaborar con el proyecto en su repositorio de github, o si solo queréis probarlo, podéis descargar la última versión del proyecto en formato zip.

Sugerencias, críticas constructivas, mejoras… ¡son bienvenidas!

¡Espero que os parezca interesante!

¡Un saludo!

Entrada visitada 477 veces