¡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 5024 veces