C++ OpenCV-Filter mit OpenMP parallelisieren

N_O_K_I_A

Commander
Registriert
Jan. 2007
Beiträge
2.121
Moin,

ich versuche gerade mit Hilfe von OpenMP ein Filter für Bilder zu parallelisieren.
Leider bis jetzt eher mit fragwürdigem Erfolg. :freak:
Also das Filter läuft, nur das Parallelisieren bringt zeitlich keinen Vorteil.

(Programmiert wird unter Ubuntu)


Compiler:

g++ `pkg-config --libs --cflags opencv` filter_omp.cpp -o filter_omp


Code:

Code:
#include <omp.h>
#include "cv.h"
#include "highgui.h"
#include "cvaux.h"
#include "cxcore.h"
#include "stdio.h"
#include "stdlib.h"
#include <iostream>

using namespace std;

int main(int argc,char* argv[])
{
   long int i, j, x, y ;
   float sum ;
   long int pixel ;
   long int width, height ;

	
   IplImage* img = cvLoadImage( argv[1] );
   IplImage* grayimg = cvCreateImage(cvSize(img->width,img->height),img->depth,1) ;
   IplImage* filterimg = cvCreateImage(cvSize(img->width,img->height),img->depth,1) ;

   width = img->width ;
   height = img->height ;
/*
   cvNamedWindow(argv[1], CV_WINDOW_AUTOSIZE );
   cvShowImage(argv[1], img ); 
   cvWaitKey(0); */

   //cvNamedWindow("GRAY", CV_WINDOW_AUTOSIZE );
   cvCvtColor(img,grayimg,CV_RGBA2GRAY) ;
   //cvShowImage("GRAY", grayimg );
   //cvWaitKey(0);


#pragma omp parallel
{
	#pragma omp for reduction(+: sum)

   for(i=0;i<width;i++){
      for(j=0;j<height;j++) {
	 sum = 0 ;
         for(x=i-1;x<=i+1;x++) {
	    for(y=j-1;y<=j+1;y++) {
				
	       if((x<0) || (y<0)|| (x>=width) || (y>=height)) {
	       //Randwertproblem
		  pixel = (int)(((uchar *)(grayimg->imageData+i*grayimg->widthStep))[j]) ;	          
	       } else {
	          pixel = (int)(((uchar *)(grayimg->imageData+x*grayimg->widthStep))[y]) ;                 	
	       }
	       sum += pixel ;				
	    }
	 }
	sum = sum / 9 ;
	((uchar *)(filterimg->imageData+i*filterimg->widthStep))[j]=(uchar)sum;

      }
   }

}

   cvNamedWindow("Filter", CV_WINDOW_AUTOSIZE );
   cvShowImage("Filter", filterimg );
   cout<<"blub";
   cvWaitKey(3000);

	
}
 
Ganz sicher bin ich mir nicht, aber bei 4 ineinander geschachtelten for-Schleifen, die alle auch noch voneinander abhängen, kann man nicht viel parallelisieren.

Kannst du versuchen die Verschachtelungstiefe zu verändern, so dass du weniger Schleifendurchläufe brauchst?
 
Ohne den Code angesehen zu haben: Füg mal den Compilerflags -fopenmp hinzu. Standardmäßig ist OpenMP bei gcc meines Erachtens bei keiner Version aktiv. Wenn dein Performance-Gewinn bei 0% liegt, könnte es einfach daran liegen...
 
Hi Nokia,

in deinem Code sind mir ein paar Dinge aufgefallen. Du hast vermutlich nur mit quadratischen Bildern getestet, daher ist dir entgangen, dass du beim Pixelzugriff x und y bzw. i und j vertauscht hast. Dein Code lässt sich wie geisterfahrer vermutet hat mit -fopenmp kompilieren und läuft dann auch. Allerdings wird er langsamer und produziert Artefakte (siehe http://img6.imagebanana.com/img/9ft68h67/artefakte.png). Wenn man allerdings die OpenMP-Anweisungen etwas anpasst, lässt sich der Code beschleunigen:

Code:
#include "cv.h"
#include "highgui.h"

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <omp.h>
 
using namespace std;
 
int main(int argc, char* argv[])
{
    IplImage* img = cvLoadImage(argv[1]);
    IplImage* grayimg = cvCreateImage(cvSize(img->width, img->height), img->depth, 1);
    IplImage* filterimg = cvCreateImage(cvSize(img->width, img->height), img->depth, 1);
    cvCvtColor(img, grayimg, CV_RGBA2GRAY);

    /*
    cvNamedWindow("Input", CV_WINDOW_AUTOSIZE);
    cvShowImage("Input", img );
    cvWaitKey(0);
     
    cvNamedWindow("Gray", CV_WINDOW_AUTOSIZE);
    cvShowImage("Gray", grayimg);
    cvWaitKey(0);
    */

    int width = img->width;
    int height = img->height;
    
    clock_t start = clock();
    #pragma omp parallel
    {
        #pragma omp for
        for (int i=0;i<width;i++) {
            for (int j=0;j<height;j++) {
                int sum = 0;
                for (int x=i-1;x<=i+1;x++) {
                    for (int y=j-1;y<=j+1;y++) {
                        int pixel;
                        if ((x<0) || (y<0)|| (x>=width) || (y>=height)) {
                            //Randwertproblem
                            pixel = (int)(((uchar*)(grayimg->imageData+j*grayimg->widthStep))[i]);	
                        } else {
                            pixel = (int)(((uchar*)(grayimg->imageData+y*grayimg->widthStep))[x]);
                        }
                        sum += pixel ;	
                    }
                }
                sum /= 9;
                ((uchar*)(filterimg->imageData+j*filterimg->widthStep))[i] = (uchar)sum;
            }
        }
    }
    
    double span = (clock() - start) / (CLOCKS_PER_SEC / 1000.0);
    cout << span << " ms" << endl;

    cvNamedWindow("Output", CV_WINDOW_AUTOSIZE);
    cvShowImage("Output", filterimg);
    cvWaitKey(0);
}

Meine Änderungen:
- "#pragma omp for reduction(+: sum)" - > "#pragma omp for"
- Deklaration der Variablen IN den Schleifen. Da das vorher nicht der Fall war, waren die Variablen shared und wurden von mehreren Threads aus geschrieben/gelesen. Das kann nicht gut gehen und führt zu den Fehlern aus meinem Screenshot.

Ohne OpenMP benötigt mein Testbild (2200x1550) rund 80ms, mit OpenMP nur noch rund 20ms. Getestet unter Windows 7 mit OpenCV 4.2 (kompiliert mit MinGW als 32-bit Binary).

Generelle Anmerkungen:
- Der Thread hat das Prefix C++, du benutzt aber noch das alte C-Interface von OpenCV.
- Einen solchen Filter gibt es schon fertig in OpenCV: http://docs.opencv.org/modules/imgproc/doc/filtering.html?highlight=boxfilter#boxfilter
- Dein Box-Filter ist ein linearer Filter der sich separieren lässt! Du kannst ihn also in zwei 3x1 Filter aufteilen. Siehe hier: http://de.wikipedia.org/wiki/Separierbarkeit. Dadurch sollte er nochmals schneller werden. Wenn du dich generell für das Thema interessierst, dann kann ich dir das "Buch Digitale Bildverarbeitung - Eine Einführung mit Java und ImageJ" von Burger & Burge empfehlen. Dort werden solche Dinge anschaulich erklärt.

Nachtrag mit ein paar Testergebnissen:
Original: 80ms
Original + OpenMP: 21ms
Separiert: 66ms
Separiert + OpenMP: 18ms
OpenCV Box Filter: 14ms
 
Zuletzt bearbeitet:
Nur mal so zum Spaß:
Schon mal probiert nur die innere Schleife zu parallelisieren? Soweit ich weiß ist bei verschachtelten Schleifen nämlich immer nur die innerste Schleife parallelisierbar.
Da ich keine Erfahrung mit openMP habe daher nun die Frage: Ist das gemessene Ergebnis dann noch annähernd das gleiche?!
 
Dragomer schrieb:
[...] Soweit ich weiß ist bei verschachtelten Schleifen nämlich immer nur die innerste Schleife parallelisierbar. [...]

Das "immer" ist falsch, es hängt selbstverständlich vom Problem ab, ob und was bei verschachtelten Schleifen parallelisierbar ist. Das lässt sich nicht verallgemeinern.

edit: @Cry & Die: Du solltest bei den OpenMP-Ergebnissen eventuell noch die Anzahl der Threads dazuschreiben, wäre interessant.
 
Zuletzt bearbeitet:
Moin,

haben uns grad mal wieder rangesetzt.
Danke für den überarbeiteten Code.
Deine Optimierungen haben es verbessert.
Außerdem wird jetzt nach deiner Optimierung bei uns rechts am Bild nichts mehr abgeschnitten.
Vielleicht ist dir der schwarze Rand ja aufgefallen.
Zwar beträgt die Differenz zwischen seriell und parallel bei uns aufm PC nur bei 0,005s, aber es läuft auf allen Cores.

Allerdings ist immernoch kein zeitlicher Vorteil bei uns aufm PC zu erkennen.

Seriell: 6,340s
Parallel: 6,332s
Testbild: 1920*1080
OS: Ubuntu

Haben auch mal ein Gigapixel Bild versucht, allerdings hält es sich da auch in Grenzen:

Seriell: 8,341s
Parallel: 7,744s
Bild: 11709*4716 Pixel (13 mb)
 

Ähnliche Themen

Antworten
19
Aufrufe
5.321
Zurück
Oben