OutOfMemoryException: memoria insuficiente – System.Drawing.Graphics.FromImage

Al utilizar System.Drawing.Graphics.FromImage (usando las últimas versiones del software .NET en el servidor de Windows 2012), solo obtengo la excepción de memoria insuficiente. La mayoría de las veces el código funciona bien.

Las respuestas típicas al problema anterior indican que ciertos recursos no se están liberando.

Por favor, considere lo siguiente antes de responder:

  • Esta imagen específica tiene un tamaño de 34KB, es una imagen .JPG. El servidor está inactivo y tiene más de 32 GB de RAM.
  • Si observo las propiedades de este archivo jpg, usando el explorador de Windows, al hacer clic derecho en el archivo, Windows dice: 96 dpi y 32 bits de profundidad .
  • PERO, si abro este archivo jpg con cualquier progtwig de gráficos (por ejemplo, photoshop), las propiedades del archivo se muestran como: 72 ppp y 24 bits de profundidad .
  • Por lo tanto, hay una falta de coincidencia entre lo que creo que dicen las propiedades del encabezado del archivo y lo que realmente contiene el archivo .
  • Además, si abro el archivo jpg con un progtwig de gráficos y lo vuelvo a guardar sin cambiar nada , las propiedades del archivo en el explorador de Windows ahora coinciden / son correctas (72 ppp y 24 bits de profundidad); y el archivo es procesado por System.Drawing.Graphics correctamente, sin lanzar la excepción .

Debido a mi conocimiento limitado del tema, no sé si el encabezado de un archivo de imagen puede contener datos diferentes del contenido real del archivo .

Preguntas:

¿Como puedo solucionar este problema? O ¿cómo puedo decirle a System.Drawing.Graphics que ignore los datos del encabezado del archivo y que simplemente mire el contenido real del archivo de imagen? (como todos los progtwigs gráficos como photoshop parecen hacer).

¡Gracias!

Si bien no soy un gurú en el formato de archivo JPEG, investigué un poco sobre el tema y he aquí lo que encontré que podría ayudarlo con sus problemas o preguntas.

Tenga en cuenta que esta respuesta asumirá en lugar de señalar específicamente la fuente de su problema debido a la falta de un archivo de ejemplo para inspeccionar y decir qué difiere de lo que espera el decodificador .Net / GDI + JPEG / JFIF.

El formato JPEG / JFIF

Comenzando, es posible que desee tener una idea del propio formato JPEG / JFIF. Después de todo, acaba de encontrar un archivo que .Net / GDI + no puede cargar / analizar. Como no tengo el archivo con el que tiene problemas, sugeriría que lo cargue en un editor hexadecimal de su elección … que tenga la capacidad de resaltar el archivo en función de una plantilla / código / analizador.

Usé 010 Editor y la plantilla JPEG del repository de plantillas en línea de Sweetscape. 010 Editor viene con una prueba gratuita de 30 días.

Lo que está buscando específicamente es el identificador SOF n y los datos en su JPEG defectuoso .

introduzca la descripción de la imagen aquí

En los datos SOF n puedo ver que mi imagen tiene una altura de Y (154) píxeles y una anchura de X (640) píxeles con una precisión de 8 bits por componente utilizando 3 componentes, por lo que es de 24 bits por píxel.

El formato JPEG / JFIF es una mezcla enorme de muchas implementaciones / formatos diferentes. Obviamente, no encontrará todas las variantes del formato en ninguna biblioteca que haya existido desde hace mucho tiempo antes de que aparecieran los impares formatos JPEG. Que tiene la biblioteca GDI +.

En su caso, sospecho que se ha topado con el perfil de color CMYK preguntado comúnmente en sus archivos JPEG.

La implementación .Net

Usted dijo que usó System.Drawing.Graphics.FromImage, así que asumiré que su código se parece a uno de los siguientes:

Graphics.FromImage(Image.FromFile("nope.jpg")); Graphics.FromImage(Image.FromFile("nope.jpg", true)); Graphics.FromImage(Image.FromStream(nopeJpegStream)); 

De esas llamadas, puede obtener una excepción OutOfMemoryException cuando el gdiplus.dll nativo llama …

  • GdipGetImageGraphicsContext
  • GdipLoadImageFromFile
  • GdipLoadImageFromFileICM (o sus respectivas * variantes de transmisión) o
  • GdipImageForceValidation

… devuelve el código 3 o 5 (memoria insuficiente o búfer insuficiente respectivamente)

Lo que obtuve de referenceource.microsoft.com buscando en las fonts .Net allí.
En cualquier caso, lo más probable es que no sea un problema con .Net sino un problema con GDI + (gdiplus.dll) para el cual Microsoft no proporciona el código fuente. Lo que también significa que no hay forma de controlar cómo se carga la imagen con los envoltorios .Net y no hay forma de verificar POR QUÉ falla. (aunque sigo sospechando que tu JPEG está guardado con CMYK)

Desafortunadamente, va a encontrar muchas más de estas extrañas excepciones / errores a medida que avanza en GDI + land. Como la biblioteca está casi en desuso en favor de Windows Presentation Framework (WPF) y el componente de imágenes de Windows. (WIC)

Mis propias pruebas

Como nunca proporcionó una imagen ni ningún detalle adicional sobre el tema, intenté reproducir su problema. La cual era una tarea en sí misma, Image.FromFile (GdipLoadImageFromFile) fallará en muchos formatos de archivo diferentes. Al menos no importa cuál es la extensión del archivo, lo que afortunadamente a Photoshop sí le importa.

Así que con su información, finalmente logré reproducir un archivo .jpg que se carga bien en Photoshop, muestra el DPI como 96 y la profundidad de bits como 32. Por supuesto, si supiera más sobre el formato JPEG, probablemente podría haber llegado a la solución correcta lejos.

Al mostrar este archivo (que tuve que establecer en el espacio de color CMYK en Photoshop) en 010 Editor, obtuve los siguientes datos SOF n : Y (154) píxeles de alto y X (640) píxeles de ancho con una precisión de 8 bits por componente usando 4 componentes, por lo que es de 32 bits por píxel.

Sospecho que verías lo mismo en tu archivo “malo”.
Y sí, Image.FromFile ahora lanza una excepción OutOfMemoryException!

Soluciones posibles

  • Utilice una biblioteca externa para cargar archivos de imagen. (Un ejercicio que te dejo, pero ImageMagick AKA Magick.NET parece una buena apuesta)
  • Utilice una herramienta de línea de comandos (invocada cuando obtiene esta excepción) que puede convertir una imagen de un formato a otro. O de JPEG a JPEG como puede ser en este caso. (Una vez más, la herramienta de línea de comandos “convertir” de ImageMagick parece una buena apuesta)
  • Utilice los ensamblados de Windows Presentation Framework …

     public static Image ImageFromFileWpf(string filename) { /* Load the image into an encoder using the Presentation Framework. * This is done by adding a frame (which in laymans terms is a layer) to a class derived BitmapEncoder. * Only TIFF, Gif and JPEG XR supports multiple frames. * Since we are going to convert our image to a GDI+ resource we won't support this as GDI+ doesn't (really) support it either. * If you want/need support for layers/animated Gif files, create a similar method to this one that takes a BitmapFrame as an argument and then... * 1. Instanciate the appropriate BitmapDecoder. * 2. Iterate over the BitmapDecoders frames, feeding them to the new method. * 3. Store the returned images in a collection of images. * * Finally, i opted to use a PngBitmapEncoder here which supports image transparency. */ var bitmapEncoder = new PngBitmapEncoder(); bitmapEncoder.Frames.Add(BitmapFrame.Create(new Uri(filename))); // Use a memorystream as a handover from one file format to another. using (var memoryStream = new MemoryStream()) { bitmapEncoder.Save(memoryStream); /* We MUST create a copy of our image from stream, MSDN specifically states that the stream must remain * open throughout the lifetime of the image. * We cannot instanciate the Image class, so we instanciate a Bitmap from our temporary image instead. * Bitmaps are derived from Image anyways, so this is perfectly fine. */ var tempImage = Image.FromStream(memoryStream); return new Bitmap(tempImage); } } 

    Basado en esta respuesta …

… Lo que diría es una buena opción, ya que te mantiene dentro del marco .Net.
Tenga en cuenta que cuando el método regrese, obtendrá específicamente una imagen PNG. Si llama a Image.Save(string) , guardará un archivo PNG, independientemente de la extensión con la que lo guarde.

Hay una sobrecarga Image.Save(string, ImageFormat) que guardará el archivo usando el formato de archivo deseado. Sin embargo, el uso de esa sobrecarga con ImageFormat.Jpeg causará una pérdida de calidad en el archivo resultante en más de un nivel.

Eso puede remediarse de alguna manera usando la tercera sobrecarga:

 foreach (var encoder in ImageCodecInfo.GetImageEncoders()) { if (encoder.MimeType == "image/jpeg") image.Save(filename, encoder, new EncoderParameters { Param = new [] { new EncoderParameter(Encoder.Quality, 100L) }}); } 

Lo cual, al menos, guardará un JPEG con “casi” sin compresión. GDI + todavía no hace un buen trabajo en ello.
Sin embargo, no importa cuánto lo tuerzas y lo giras. GDI + no será tan bueno como una biblioteca de imágenes adecuada, que una vez más probablemente sea ImageMagick. Cuanto más lejos pueda obtener de GDI +, mejor estará.

Conclusión / TL: DR y otras notas.

P: ¿Puedo cargar estos archivos en .Net?
R: Sí, con un poco de manipulación y sin usar GDI + para la carga inicial del archivo, ya que GDI + no admite el espacio de color CMYK en archivos JPEG.
Y aún así, GDI + carece de soporte para muchas cosas, por lo que recomendaría una biblioteca de imágenes externa sobre GDI +.

P: No coinciden los ppp y la profundidad de bits para el archivo entre Windows y
R: Esto es solo una prueba de que la carga de JPEG de Windows difiere de las rutinas de carga de JPEG de otras aplicaciones. Solo las aplicaciones que usan GDI o GDI + verán la misma información que Windows cuando muestra los detalles de la imagen.
Si está utilizando Windows 7+, entonces no está utilizando GDI + para mostrar la información ni la imagen. Está utilizando WPF o WIC para hacerlo, que están algo más actualizados.

P: Si abro el archivo jpg con un progtwig de gráficos y lo vuelvo a guardar sin cambiar nada, las propiedades del archivo en el explorador de Windows ahora coinciden / son correctas (72 ppp y 24 bits de profundidad)
R: Si está utilizando Adobe Photoshop y utiliza “Guardar para web”, la imagen JPEG no se guardará en formato CMYK. Utilice “Guardar como …” en su lugar y encontrará que el espacio de color (y la profundidad de bits) permanece igual.

Sin embargo, no pude reproducir su discrepancia en DPI y profundidad de bits al cargar mi archivo en Photoshop. Se reportan como iguales tanto en Windows como en Photoshop.

Tuve el mismo problema con este error: parece que la biblioteca Gráficos / Mapa de bits / Imágenes produce una excepción con ciertas imágenes con formato incorrecto. Reducirlo más que eso, como muestra Cadde, es difícil.

A raíz de la gran respuesta hecha por Cadde (que dejó de usar una biblioteca externa como ejercicio para el lector), cambié mi código al siguiente usando MagickNet, que puede obtener aquí , o simplemente con NuGet: PM> Install-Package Magick.NET-Q16-x86 .

El código intenta crear un objeto de Gráficos a partir de la imagen, y si falla, usa ImageMagick para cargar la imagen nuevamente, convertirla en un Bitmap e intenta cargarla desde allí.

 Image bitmap = Bitmap.FromFile(filename, false); Graphics graphics = null; try { graphics = Graphics.FromImage(bitmap); } catch (OutOfMemoryException oome) { // Well, this looks like a buggy image. // Try using alternate method ImageMagick.MagickImage image = new ImageMagick.MagickImage(filename); image.Resize(image.Width, image.Height); image.Quality = 90; image.CompressionMethod = ImageMagick.CompressionMethod.JPEG; graphics = Graphics.FromImage(image.ToBitmap()); } 

Yo tuve el mismo problema. Mi archivo jpg fue generado desde Photoshop. Una solución simple es abrir el archivo jpg con Winodws Paint y guardarlo como un nuevo archivo jpg. Importe el nuevo archivo jpg al proyecto C # y el problema desaparecerá.

    Intereting Posts