Pregunta

En mi proyecto, estoy usando imágenes (no comprimido de 16 bits en escala de grises) gigapixel que provienen de un escáner de alta resolución para fines de medición. Dado que estos mapas de bits no se pueden cargar en la memoria (principalmente debido a la fragmentación de memoria) que estoy usando azulejos (y TIFF en el disco de azulejos). (Ver tema Stackoverflow en este )

necesito para poner en práctica la panorámica / zoom de una manera como Google Maps o DeepZoom. Tengo que solicitar el procesamiento de imágenes sobre la marcha antes de presentarlo en la pantalla, así que no puedo utilizar una biblioteca precocida que accede directamente a un archivo de imagen. Para acercar tengo la intención de mantener una imagen de resolución múltiple en mi archivo (almacenamiento pirámide). Los pasos más útiles parecen ser + 200%, 50% y mostrar todos.

Mi código base es actualmente C # y .NET 3.5. Actualmente Asumo tipo de formularios, a menos que WPF para mí una gran ventaja en esta área. Tengo un método que puede devolver cualquier parte (procesado) de la imagen subyacente.

Las cuestiones específicas:

  • pistas o referencias sobre cómo implementar este pan / zoom con la generación a pedido de partes de imagen
  • cualquier código que podría ser utilizado como una base (preferiblemente comercial o LGPL / BSD como licencias)
  • puede DeepZoom ser utilizado para esto (es decir, ¿hay alguna manera de que me puede proporcionar una función de proporcionar una baldosa en la resulution adecuado para el nivel de zoom actual?) (Es necesario disponer de píxeles precisa abordar todavía)
¿Fue útil?

Solución 2

decidí probar algo a mí mismo. Se me ocurrió con un sencillo GDI + código, que utiliza los azulejos ya tengo. Acabo de filtrar las partes que son relevantes para la región de recorte actual. Funciona como magia! Por favor, encontrar el código de abajo. (Configuración del formulario doble buffer para los mejores resultados)

 protected override void OnPaint(PaintEventArgs e)
    {
        base.OnPaint(e);
        Graphics dc = e.Graphics;
        dc.ScaleTransform(1.0F, 1.0F);
        Size scrollOffset = new Size(AutoScrollPosition);

        int start_x = Math.Min(matrix_x_size, 
                             (e.ClipRectangle.Left - scrollOffset.Width) / 256);
        int start_y = Math.Min(matrix_y_size, 
                             (e.ClipRectangle.Top - scrollOffset.Height) / 256);
        int end_x = Math.Min(matrix_x_size, 
                        (e.ClipRectangle.Right - scrollOffset.Width + 255) / 256);
        int end_y = Math.Min(matrix_y_size, 
                      (e.ClipRectangle.Bottom - scrollOffset.Height + 255) / 256);

        // start * contain the first and last tile x/y which are on screen 
        // and which need to be redrawn.
        // now iterate trough all tiles which need an update 
        for (int y = start_y; y < end_y; y++)
            for (int x = start_x; x < end_x; x++)
            {  // draw bitmap with gdi+ at calculated position.
                dc.DrawImage(BmpMatrix[y, x], 
                           new Point(x * 256 + scrollOffset.Width, 
                                     y * 256 + scrollOffset.Height));
            }
    }

Para probarlo, he creado una matriz de 80x80 de 256 azulejos (420 megapíxeles). Por supuesto, voy a tener que añadir un poco de carga diferida en la vida real. Puedo dejar fuera azulejos (vacío) si aún no están cargados. De hecho, le he pedido a mi cliente a pegarse 8 GBytes en su máquina por lo que no tiene que preocuparse por el rendimiento demasiado. Una vez cargados los azulejos pueden permanecer en la memoria.

public partial class Form1 : Form
{
    bool dragging = false;
    float Zoom = 1.0F;
    Point lastMouse;
    PointF viewPortCenter;

    private readonly Brush solidYellowBrush = new SolidBrush(Color.Yellow);
    private readonly Brush solidBlueBrush = new SolidBrush(Color.LightBlue);
    const int matrix_x_size = 80;
    const int matrix_y_size = 80;
    private Bitmap[,] BmpMatrix = new Bitmap[matrix_x_size, matrix_y_size];
    public Form1()
    {
        InitializeComponent();

        Font font = new Font("Times New Roman", 10, FontStyle.Regular);
        StringFormat strFormat = new StringFormat();
        strFormat.Alignment = StringAlignment.Center;
        strFormat.LineAlignment = StringAlignment.Center;
        for (int y = 0; y < matrix_y_size; y++)
            for (int x = 0; x < matrix_x_size; x++)
            {
                BmpMatrix[y, x] = new Bitmap(256, 256, PixelFormat.Format24bppRgb);
                //                    BmpMatrix[y, x].Palette.Entries[0] = (x+y)%1==0?Color.Blue:Color.White;

                using (Graphics g = Graphics.FromImage(BmpMatrix[y, x]))
                {
                    g.FillRectangle(((x + y) % 2 == 0) ? solidBlueBrush : solidYellowBrush, new Rectangle(new Point(0, 0), new Size(256, 256)));
                    g.DrawString("hello world\n[" + x.ToString() + "," + y.ToString() + "]", new Font("Tahoma", 8), Brushes.Black,
                        new RectangleF(0, 0, 256, 256), strFormat);
                    g.DrawImage(BmpMatrix[y, x], Point.Empty);
                }
            }

        BackColor = Color.White;

        Size = new Size(300, 300);
        Text = "Scroll Shapes Correct";

        AutoScrollMinSize = new Size(256 * matrix_x_size, 256 * matrix_y_size);
    }   

Resultó que esta era la parte fácil. Conseguir asíncrono multiproceso de E / S realiza en segundo plano era mucho más difícil de acchieve. Aún así, tengo que funcione de la manera descrita aquí. Las cuestiones a resolver son más .NET / Formulario de múltiples hilos relacionados que a este tema.

En el pseudo código funciona así:

after onPaint (and on Tick)
   check if tiles on display need to be retrieved from disc
       if so: post them to an async io queue
       if not: check if tiles close to display area are already loaded
           if not: post them to an async io/queue
   check if bitmaps have arrived from io thread
      if so: updat them on screen, and force repaint if visible

Resultado:. Ahora tengo mi propio control personalizado que utiliza aproximadamente 50 Mbytes para un acceso muy rápido a tamaño arbitrario archivos TIFF (azulejos)

Otros consejos

artículo CodeProject: Generar ... DeepZoom colección de imágenes podría ser una lectura útil ya que habla de la generación de una fuente de imagen DeepZoom.

Esta href="http://msdn.microsoft.com/en-us/magazine/dd943052.aspx" rel="nofollow noreferrer"> artículo tiene una sección Deep dinámica zoom: el suministro de píxeles de la imagen en tiempo de ejecución y enlaces a este Mandelbrot Explorador la que 'un poco' suena similar a lo que estás tratando de hacer (. es decir, que está generando partes específicas del conjunto de Mandelbrot en la demanda; desea recuperar partes específicas de la imagen gigapixel en la demanda de).

Creo que la respuesta a "puede DeepZoom ser utilizado para esto?" es probablemente "Sí", sin embargo, ya que sólo está disponible en Silverlight que tendrá que hacer algunos trucos con un control de navegador web incorporado Si necesita un WinForms / WPF aplicación cliente.

Lo siento, no puedo dar respuestas más específicas -. Espero que los enlaces de ayuda

p.s. No estoy seguro de si es compatible con Silverlight imágenes TIFF - que podría ser un problema a menos que convertir a otro formato

.

Creo que se puede solucionar este problema siguiendo los siguientes pasos:

  1. La generación de imágenes:

    • segmentar la imagen en múltiples subimágenes (teselas) de una resolución pequeña, para instace, 500x500. Estas imágenes son de profundidad 0
    • combinar una serie de azulejos con la profundidad 0 (4x4 o 6x6), cambiar el tamaño de la combinación de generar un nuevo azulejo con 500x500 píxeles de profundidad 1.
    • continuar con este enfoque hasta obtener la imagen completa utilizando sólo unas pocas baldosas.
  2. visualización de imágenes

    • Comience desde la más alta profundidad
    • Cuando el usuario arrastra la imagen, cargar las baldosas dinámicamente
    • Cuando el usuario zoom una región de la imagen, reducir la profundidad, la carga de los azulejos para esa región en una resolución más alta.

El resultado final es similar a Google Maps.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top