2015-12-14 75 views
5

Sto tentando di ricreare funzioni GDI + molto semplici, come ridimensionare e ruotare un'immagine. Il motivo è che alcune funzioni GDI non possono essere eseguite su più thread (ho trovato un modo di aggirare l'utilizzo dei processi ma non volevo entrare in quello), e l'elaborazione di migliaia di immagini su un thread non stava quasi tagliandola. Anche le mie immagini sono in scala di grigi, quindi una funzione personalizzata dovrebbe preoccuparsi solo di un valore invece di 4.In che modo GDI + funziona così velocemente?

Indipendentemente dal tipo di funzione che cerco di ricreare, anche quando altamente ottimizzato, è sempre SEMPRE più lento, nonostante sia notevolmente semplificato rispetto a ciò che GDI sta facendo (sto operando su un array 1D di byte, un byte per pixel)

Ho pensato che forse il modo in cui ruotavo ogni punto poteva essere la differenza, quindi l'ho tolto completamente, e fondamentalmente aveva una funzione che attraversa ogni pixel e la imposta su ciò che è già, e che era solo approssimativamente legata alla velocità di GDI, anche se GDI stava facendo una rotazione effettiva e cambiando 4 valori diversi per pixel.

Che cosa rende possibile questo? C'è un modo per abbinarlo usando la tua funzione?

+0

Come stai passando attraverso ogni pixel? –

+2

[Graphics Device Interface] (https://en.wikipedia.org/wiki/Graphics_Device_Interface) dovrebbe essere veloce. È scritto in C/C++ nativo e potrebbe anche utilizzare la funzione hardware dell'adattatore grafico per disegnare per es. linea. Sarebbe molto più veloce della tua iterazione per pixel in C#. Puoi provare a ottenere le stesse prestazioni se impari a utilizzare anche queste funzioni (ad esempio DirectX gestito). – Sinatr

+0

È solo un ciclo for che attraversa una serie di byte. ogni byte nella matrice rappresenta l'intensità di un pixel. Ho pensato che GDI non tocchi affatto la GPU. Se lo fa, allora lo spiegherebbe sicuramente, ma ho letto che non è così. – Frobot

risposta

3

Il codice GDI + è scritto in C/C++, o forse anche parzialmente in assembly. Alcune chiamate GDI + possono utilizzare GDI, un'API vecchia e ottimizzata. Troverai difficile abbinare le prestazioni, anche se conosci tutti i trucchi di manipolazione dei pixel.

+0

Sì. Fondamentalmente "imbroglia" richiamando le API e le ops operative meno gestite e più efficienti sotto il cofano. 'Lista .Sort()' fa la stessa cosa. ;) – Haney

+0

Sì, penso che si riduce ad essere una API altamente ottimizzata costruita da professionisti che utilizzano alcuni trucchi, oltre a non essere gestiti. Sarò ancora in grado di battere la sua velocità utilizzando una funzione personalizzata su più thread, ma non del tutto come previsto. Grazie per l'input di tutti – Frobot

+0

@Haney: Per curiosità, cosa viene chiamato 'Elenco .Sort()'? - Non sono a conoscenza del fatto che l'API Windows nativa fornisce una funzione di ordinamento. –

2

Sto aggiungendo la mia risposta insieme al mio codice per aiutare chiunque altro stia cercando di farlo.

Da una combinazione di puntatori e utilizzando un'approssimazione di Seno e Coseno invece di chiamare una funzione esterna per la rotazione, sono arrivato quasi alla velocità di raggiungere le velocità GDI. Nessuna funzione esterna viene chiamata affatto.

Ci vuole ancora circa il 50% di tempo in più rispetto a GDI, ma la mia precedente implementazione ha richiesto oltre 10 volte di più rispetto a GDI. E quando si considera il multi-threading, questo metodo può essere 10 volte più veloce di GDI. Questa funzione può ruotare un'immagine 300x400 in 3 millisecondi sulla mia macchina.

Ricordare che questo è per immagini in scala di grigi e ogni byte nell'array di input rappresenta un pixel. Se hai qualche idea per renderlo più veloce, per favore condividi!

private unsafe byte[] rotate(byte[] input, int inputWidth, int inputHeight, int cx, int cy, double angle) 
    { 
     byte[] result = new byte[input.Length]; 

     int 
      tx, ty, ix, iy, x1, y1; 
     double 
      px, py, fx, fy, sin, cos, v; 
     byte a, b; 

     //Approximate Sine and Cosine of the angle 
     if (angle < 0) 
      sin = 1.27323954 * angle + 0.405284735 * angle * angle; 
     else 
      sin = 1.27323954 * angle - 0.405284735 * angle * angle; 
     angle += 1.57079632; 
     if (angle > 3.14159265) 
      angle -= 6.28318531; 
     if (angle < 0) 
      cos = 1.27323954 * angle + 0.405284735 * angle * angle; 
     else 
      cos = 1.27323954 * angle - 0.405284735 * angle * angle; 
     angle -= 1.57079632; 


     fixed (byte* pInput = input, pResult = result) 
     { 
      byte* pi = pInput; 
      byte* pr = pResult; 

      for (int x = 0; x < inputWidth; x++) 
       for (int y = 0; y < inputHeight; y++) 
       { 
        tx = x - cx; 
        ty = y - cy; 
        px = tx * cos - ty * sin + cx; 
        py = tx * sin + ty * cos + cy; 
        ix = (int)px; 
        iy = (int)py; 
        fx = px - ix; 
        fy = py - iy; 

        if (ix < inputWidth && iy < inputHeight && ix >= 0 && iy >= 0) 
        { 
         //keep in array bounds 
         x1 = ix + 1; 
         y1 = iy + 1; 
         if (x1 >= inputWidth) 
          x1 = ix; 
         if (y1 >= inputHeight) 
          y1 = iy; 

         //bilinear interpolation using pointers 
         a = *(pInput + (iy * inputWidth + ix)); 
         b = *(pInput + (y1 * inputWidth + ix)); 
         v = a + ((*(pInput + (iy * inputWidth + x1)) - a) * fx); 
         pr = (pResult + (y * inputWidth + x)); 
         *pr = (byte)(v + (((b + ((*(pInput + (y1 * inputWidth + x1)) - b) * fx)) - v) * fy)); 
        } 
       } 
     } 

     return result; 
    } 
+0

Vedo un paio di piccole modifiche che potreste fare, come spostare 'tx = x - cx;' ei due termini correlati 'tx * cos' e' tx * sin' fuori dal nesting del ciclo interno, ma quest'ultimo dovrebbe richiedono più temp, quindi dovresti testare se qualcosa del genere è utile. Mi chiedo anche se passare alla valutazione booleana non a corto circuito nel tuo "se" possa dare un leggero impulso. Ma nel complesso penso che tu sia vicino a ciò che può essere raggiunto a questo livello (a meno che non ci sia un approccio completamente diverso di cui non sono a conoscenza). –

+0

Un'altra cosa che potresti provare, usa una [matrice di trasformazione] (https://en.wikipedia.org/wiki/Transformation_matrix) per applicare la rotazione all'immagine. Puoi anche utilizzare il pacchetto NuGet [Sistema.Numerics.Vectors] (https://www.nuget.org/packages/System.Numerics.Vectors) per ottenere versioni accelerate hardware di alcuni dei metodi Matrix per renderlo ancora più veloce. –

Problemi correlati