2009-05-28 41 views
11

Estoy usando las clases System.Drawing para generar miniaturas e imágenes con marcas de agua de fotos cargadas por el usuario. Los usuarios también pueden recortar las imágenes usando jCrop después de cargar el original. Me he apoderado de este código de otra persona, y estoy buscando simplificarlo y optimizarlo (está siendo utilizado en un sitio web con mucho tráfico).Manipulación eficiente de imágenes en C#

El tipo anterior tenía métodos estáticos que recibían un mapa de bits como parámetro y también uno devuelto, asignando y desechando internamente un objeto Graphics. Según entiendo, una instancia Bitmap contiene toda la imagen en la memoria, mientras que Graphics es básicamente una cola de operaciones de dibujo, y es idempotente.

El proceso actualmente funciona de la siguiente manera:

  • recibir la imagen y almacenarla en un archivo temporal.
  • Recibir coordenadas de recorte.
  • Cargue el mapa de bits original en la memoria.
  • Crea un nuevo mapa de bits a partir del original, aplicando el recorte.
  • Realice un poco de brillo loco ajustando el nuevo mapa de bits, tal vez (?) Devolviendo un nuevo mapa de bits (prefiero no tocar esto; abundan los aritméticos de puntero!), Llamemos a esto A.
  • Cree otro mapa de bits desde la que resulta una, la aplicación de la marca de agua (permite llamar a este B1)
  • Crear un mapa de bits 175x175 miniatura de A.
  • Crear un mapa de bits 45x45 miniatura de A.

esto parece como una gran cantidad de asignaciones de memoria; mi pregunta es esta: ¿es una buena idea reescribir partes del código y reutilizar las instancias Graphics, creando de hecho una canalización? En efecto, solo necesito 1 imagen en la memoria (la carga original), mientras que el resto se puede escribir directamente en el disco. Todas las imágenes generadas necesitarán las transformaciones de recorte y brillo, y una transformación única que es única para esa versión, creando efectivamente un árbol de operaciones.

¿Algún pensamiento o idea?

Ah, y probablemente debería mencionar que esta es la primera vez que realmente estoy trabajando con .NET, así que si algo que digo parece confuso, por favor tengan paciencia y denme algunas pistas.

+0

Es desafortunado que esta sea su primera interacción con .NET. El patrón IDisposable es mi verdadera frustración duradera con el marco. Este negocio "de uso" es la característica más esotérica que se usa con regularidad; la mayoría de las cosas son muy sencillas. – overslacked

+0

¡Buena pregunta, digo! +1 – Cerebrus

+1

Tuve que buscar 'idempotent' ... buena palabra. – JasonRShaver

Respuesta

3

objetos Reutilización de gráficos probablemente no va a resultar en el aumento de rendimiento significativo.

El código GDI subyacente simple crea un contexto de dispositivo para el mapa de bits que ha cargado en la RAM (una memoria DC).

El cuello de botella de su operación parece estar en la carga de la imagen desde el disco.

¿Por qué volver a cargar la imagen desde el disco? Si ya está en una matriz de bytes en la RAM, que debería ser cuando se carga, puede simplemente crear una secuencia de memoria en la matriz de bytes y luego crear un mapa de bits a partir de esa secuencia de memoria.

En otras palabras, guárdelo en el disco, pero no lo vuelva a cargar, simplemente opere desde la memoria RAM.

Además, no debería ser necesario para crear un nuevo mapa de bits para aplicar la marca de agua (dependiendo de cómo se había hecho.)

Usted debe perfilar la operación para ver donde se necesita mejorar (o incluso si necesita ser mejorado.)

3

El proceso parece razonable. Cada imagen debe existir en la memoria antes de guardarse en el disco, por lo que cada versión de las miniaturas estará primero en la memoria. La clave para asegurarse de que esto funcione de manera eficiente es desechar sus objetos gráficos y de mapa de bits. La forma más fácil de hacerlo es con la instrucción using.

using(Bitmap b = new Bitmap(175, 175)) 
using(Graphics g = Graphics.FromBitmap(b)) 
{ 
    ... 
} 
+0

Mi verdadera pregunta es más como: ¿me dará un aumento en el rendimiento si reutilizo el objeto 'Gráficos'? –

0

sólo voy a tirar esto hacia fuera allí casualmente, pero si quería una 'guía' rápida a las mejores prácticas para trabajar con imágenes, a ver el proyecto Paint.NET. Para obtener herramientas de alta proformancia gratuitas para manipular imágenes, consulte AForge.NET.

El beneficio de AForge es permitirle hacer muchos de estos pasos sin crear un nuevo mapa de bits cada vez. Si esto es para un sitio web, casi puedo garantizar que el código con el que está trabajando será el cuello de botella de rendimiento para la aplicación.

2

Completé un proyecto similar hace un tiempo e hice algunas pruebas prácticas para ver si había una diferencia en el rendimiento si reutilizaba el objeto Graphics en lugar de girar uno nuevo para cada imagen. En mi caso, estaba trabajando en un flujo constante de grandes cantidades de imágenes (> 10.000 en un "lote"). Descubrí que obtuve un ligero aumento en el rendimiento reutilizando el objeto Graphics.

También encontré que obtuve un ligero aumento al usar GraphicsContainers en el objeto Graphics para intercambiar fácilmente diferentes estados dentro/fuera del objeto, ya que se utilizó para realizar varias acciones. (Específicamente, tuve que aplicar un recorte y dibujar algo de texto y un cuadro (rectángulo) en cada imagen.) No sé si esto tiene sentido para lo que debe hacer. Es posible que desee examinar los métodos BeginContainer y EndContainer en el objeto Graphics.

En mi caso, la diferencia fue leve. No sé si obtendrías más o menos mejoras en tu implementación. Pero dado que incurrirá en un costo al volver a escribir su código, le recomendamos que finalice el diseño actual y realice algunas pruebas de rendimiento antes de volver a escribir. Solo un pensamiento.

Algunos enlaces que pueden resultar útiles:

Using Nested Graphics Containers
GraphicsContainer Class

Cuestiones relacionadas