Wie machen Sie den folgenden code, der die Bilineare interpolation effizienter?
Dem folgenden code wird zum vergrößern der Bilder mit der bilinearen interpolation.
Wo kann geändert werden, in der Funktion der slow_rescale effizienter zu machen?
Erwarte ich, um es zu ändern aus der Sicht der Grundsätze der Computer-Organisation.
Freue mich auf Eure Antworten!
Dank!
unsigned char *slow_rescale(unsigned char *src, int src_x, int src_y, int dest_x, int dest_y)
{
double step_x,step_y; //Step increase as per instructions above
unsigned char R1,R2,R3,R4; //Colours at the four neighbours
unsigned char G1,G2,G3,G4;
unsigned char B1,B2,B3,B4;
double RT1, GT1, BT1; //Interpolated colours at T1 and T2
double RT2, GT2, BT2;
unsigned char R,G,B; //Final colour at a destination pixel
unsigned char *dst; //Destination image - must be allocated here!
int x,y; //Coordinates on destination image
double fx,fy; //Corresponding coordinates on source image
double dx,dy; //Fractional component of source image coordinates
dst=(unsigned char *)calloc(dest_x*dest_y*3,sizeof(unsigned char)); //Allocate and clear destination image
if (!dst) return(NULL); //Unable to allocate image
step_x=(double)(src_x-1)/(double)(dest_x-1);
step_y=(double)(src_y-1)/(double)(dest_y-1);
for (x=0;x<dest_x;x++) //Loop over destination image
for (y=0;y<dest_y;y++)
{
fx=x*step_x;
fy=y*step_y;
dx=fx-(int)fx;
dy=fy-(int)fy;
getPixel(src,floor(fx),floor(fy),src_x,&R1,&G1,&B1); //get N1 colours
getPixel(src,ceil(fx),floor(fy),src_x,&R2,&G2,&B2); //get N2 colours
getPixel(src,floor(fx),ceil(fy),src_x,&R3,&G3,&B3); //get N3 colours
getPixel(src,ceil(fx),ceil(fy),src_x,&R4,&G4,&B4); //get N4 colours
//Interpolate to get T1 and T2 colours
RT1=(dx*R2)+(1-dx)*R1;
GT1=(dx*G2)+(1-dx)*G1;
BT1=(dx*B2)+(1-dx)*B1;
RT2=(dx*R4)+(1-dx)*R3;
GT2=(dx*G4)+(1-dx)*G3;
BT2=(dx*B4)+(1-dx)*B3;
//Obtain final colour by interpolating between T1 and T2
R=(unsigned char)((dy*RT2)+((1-dy)*RT1));
G=(unsigned char)((dy*GT2)+((1-dy)*GT1));
B=(unsigned char)((dy*BT2)+((1-dy)*BT1));
//Store the final colour
setPixel(dst,x,y,dest_x,R,G,B);
}
return(dst);
}
void getPixel(unsigned char *image, int x, int y, int sx, unsigned char *R, unsigned char *G, unsigned char *B)
{
//Get the colour at pixel x,y in the image and return it using the provided RGB pointers
//Requires the image size along the x direction!
*(R)=*(image+((x+(y*sx))*3)+0);
*(G)=*(image+((x+(y*sx))*3)+1);
*(B)=*(image+((x+(y*sx))*3)+2);
}
void setPixel(unsigned char *image, int x, int y, int sx, unsigned char R, unsigned char G, unsigned char B)
{
//Set the colour of the pixel at x,y in the image to the specified R,G,B
//Requires the image size along the x direction!
*(image+((x+(y*sx))*3)+0)=R;
*(image+((x+(y*sx))*3)+1)=G;
*(image+((x+(y*sx))*3)+2)=B;
}
- Können Sie zeigen, getPixel() und setPixel()?
- Ich habe gerade editiert und Hinzugefügt, die Funktionen getPixel() und setPixel().@selbst.
- Gut, abgesehen von völlig verändern den Algorithmus, den Sie entfernen könnte einige redundante Multiplikationen. Bei getPixel:
int x, int y, int sx
pass((x+(y*sx))*3)+0
an die Funktion statt. - Möchten Sie bitten, dies auf CodeReview, dass es angemessener wäre es.
-O3
? vielleicht profiling? dann, wenn einige von diesen können verschwenderisch sein/unwirksam, weil der compiler-Optimierung: inline-get/setPixel; ändern Sie Sie so, dass ein 32-bit-Lesen/schreiben vom/mem fertig ist (Vorsicht vor endianness), möglicherweise 4/8 byte ausgerichtet sind (können Sie haben "R G B 0 R G B 0 L" statt "R G B R G B"?); einige weitere temporäre vars (z.B. für Boden(fx)) ...- btw, versucht, einige vorgeschlagen, "manuelle" Optimierungen, ohne compiler-optim ... es stellt sich heraus, dass
-O3
übertrifft diese "manuelle" Optimierungen.
Du musst angemeldet sein, um einen Kommentar abzugeben.
Ich mir sorgen über das image-processing Leistung die ganze Zeit. Unten sind ein paar offensichtliche Punkte zu Bedenken:
Numerische Präzision:
Ist die erste Sache, springt bei mir mit dem code ist die Verwendung von doubles für Schritt Größe, Farbwerte und Koordinaten. Brauchen Sie wirklich dieses Maß an Genauigkeit für diese Mengen? Wenn nicht, können Sie einige profiling zur überprüfung der Leistungsfähigkeit des Codes bei der Verwendung von fixed-point oder Schwimmer statt.
Beachten Sie, dass dies ist eine hardware-abhängige Frage und die Leistung kann oder kann nicht ein Problem sein, je nachdem, ob oder nicht Ihre hardware implementiert, double, float nur, oder keines von beiden (dann sind beide in software implementiert). Die Diskussionen zu diesem Aspekt gehören auch memory-Ausrichtung, verschmolz memory access, etc. Sicherlich sind diese Themen berühren, die "Principles of Computer Organization," es ist mehr Diskussion über dieses Thema ist hier.
Loop Unrolling:
Haben Sie auch erwogen, manuelle loop unrolling? Dies kann oder kann nicht helfen, da dein compiler kann bereits versuchen, nehmen Vorteil von solchen Optimierungen, ist jedoch mindestens eine überlegung Wert, da Sie einen Doppel-Loop-Schleife über potenziell große array-Größen.
Numerische Entlassungen:
In Ihrem getPixel () - Funktion, die Sie auch berechnen
image+((x+(y*sx))*3
für jede RGB-Komponente, und dies scheint sich nicht zu ändern, warum nicht einfach berechnen diese Menge einmal am Anfang Ihrer Funktion?Vektor-Verarbeitung:
Seine schwer zu denken über die Optimierung solcher code ohne zuerst zu Fragen, ob oder nicht, können Sie die Vorteile von Vektor-Verarbeitung. Sie haben Zugriff auf vektorisierte Anweisungen, sets, z.B., SSE?
Parallele Verarbeitung:
Meisten Systeme haben OpenMP installiert. Wenn dem so ist, sollten Sie überlegen, Restrukturierung Ihres Codes zu nutzen, Ihre-Prozessor multi-core-Fähigkeiten. Dies ist überraschend einfach zu implementieren mit pragma ist, seine sicherlich lohnt sich.
Compiler Flags:
Auch, obwohl Sie nicht erwähnt, dass es direkt, compilation-flags beeinflussen die Leistung von C-code. E. g., wenn Sie gcc verwenden, könnten Sie vergleichen die performance-Unterschiede mit:
vs.
Hier sind einige Ideen:
floor
undceil
(und möglicherweise Multiplikationen, aber ich bin nicht sicher) schneller.ceil(x)
durchfloor(x)+1
fx=x*step_x
durch ZugabegetPixel
durch etwas effizienter(dx*R2)+(1-dx)*R1
==>R1+dx*(R2-R1)
ceil(x)
ist nicht gleichfloor(x) + 1
wennx
ist bereits eine ganze Reihe.ceil(x)
selbst. Ich dachte, Sie waren, was darauf hindeutet, dass die beiden gleichwertig waren eher als die Korrektur der OP Missbrauch der ehemaligen.ceil
. Auch wenn das argument derceil
ist eine ganze Zahl, danndx=0
, und die Berechnung wird multipliziert mit 0, so ist es OK für ihn falsch zu sein (allerdings könnte man vermeiden müssen, Lesen von schlechten Adressen, das dazu führen könnte, Seitenfehler und was nicht).Multiplikation kann weitgehend reduziert in diesem code.
dx
berechnet werden kann, die in der äußeren Schleife, und dort bereiten wir das Einmaleins für weitere Operationen wieRT1=(dx*R2)+(1-dx)*R1
da die Multiplikation(R2,R1,etc), von 1 byte Größe.Den folgenden code läuft ~10-mal schneller als das original auf meinem Rechner (Mac OS, Mac C++ - compiler mit -O3):
GPUs hardware zu tun, die Bilineare interpolation für Sie. Tun dies auf der CPU ist, wie floating-point-Operationen in der software ohne Verwendung der floating-point-hardware (z.B. x87 oder SSE/AVX). Meine beste Rat ist, zu prüfen, optimieren-algorithmen wie die bikubische interpolation oder Allgemeinen Bild-Filter, die kann eine bessere visuelle Ergebnisse, und die werden nicht unterstützt, auf den meisten GPUs. Grafik Gems III, auch wenn es uralt ist, hat einen guten Abschnitt "Allgemeine Gefilterte Bild Reskalierung" sowohl für maginfication und Verkleinerung.
Allerdings, wenn Sie immer noch wollen, zu tun ist, die Bilineare interpolation auf der CPU, sollten Sie überlegen, hardware-Beschleunigungen auf der CPU. In diesem Fall würde ich schauen mit SIMD. Finden Sie unter diesem link bilinear-pixel-interpolation-mit-sse, die veranschaulicht, wie die bi-lineare interpolation mit SSE. Ich habe getestet, dieser code und die SSE-code ist viel schneller. Könnte man kombinieren, dass mit OpenMP für die Verwendung von mehreren threads, die auf große Bilder.
Ich auch getestet, die Festkomma-code und gefunden, dass es gab bessere Ergebnisse als die non-SSE-code mit MSVC2010 aber nicht in MSVC2012. Ich erwarte, dass für die meisten modernen Compiler die Festkomma-code wird nicht besser sein, es sei denn, es läuft auf einem embedded system ohne floating point hardware.