Sobel-Operator: Wie Pixel in Bilddatei auswählen?
-
Ich fand Portable Pixmap ziemlich einfach. Dafür ist das Format alles andere als optimal.
(Besonders das ASCII-Angebot).
Das englische Wikipedia gibt eine ausführliche Erläuterung zu diesem Bildformat.
-
Bisher habe ich durch Zufall sogar mit dem PGM-Format gearbeitet. Dementsprechend sind die Unterschiede ja nicht mehr groß. Bis auf den einen Punkt, dass ich jetzt drei Farbwerte pro Pixel statt einem habe.
Wie kann man das im Sobel-Operator berücksichtigen?
-
Entweder lädst du das Bild, machst daraus Graustufen und sobelst das Grauwertbild oder du sobelst die 3(4) RGB(A) einzeln und kombinierst die Ergebnisse irgendwie.
-
padreigh schrieb:
Entweder lädst du das Bild, machst daraus Graustufen und sobelst das Grauwertbild oder du sobelst die 3(4) RGB(A) einzeln und kombinierst die Ergebnisse irgendwie.
Ich weiß, diese zwei Möglichkeiten gibt es. Nur weiß ich bei beiden nicht, wie man das innerhalb von C++ durchführen kann. Bzw. ist mir für Variante 2 der Algorithmus nicht bekannt.
-
Am einfachsten finde ich das (unkomprimierte) Targa Dateiformat (erweiterung .tga).
Ich hab mal eine Funktion für ein OpenGL Projekt geschrieben, das eine TGA Datei in den Speicher liest und anschließend in die Grafikkarte packt. Dabei wird die Bildgröße (Höhe * Breite) abgespeichert, sowie ein Array mit (Höhe*Breite) Pixeln, wobei ein Pixel nochmal aus 3 bzw. 4 Bytes besteht, für Blau, Grün, Rot und gegebenfalls dem Alpha Channel (Durchsichtigkeit). Die OpenGL Sachen sollten sich relativ schnell rausschreiben lassen, wenn du interresse hast einfach mal ne pm schreiben.
Ansonsten kann ich dir die Intel JPEG library empfehlen (ijl15.dll), die wird zwar offiziell nicht mehr unterstützt, aber funktioniert nach wie vor rasend schnell und gut. Die macht im prinzip das selbe wie meine Funktion, in dem sie einfach ein Array mit Höhe*Breite*3 Bytes anlegt und die Farbwerte darin ablegt.
Als drittes gibts noch das gute alte BMP, bei ich zumindestens bei Windows von vorgefertigten Funktionen zum laden in ein Array weis.
/Edit: Achja und Graustufen rechnen sollte relativ schnell gehen per Durchschnittsbildung der 3 Grundfarben eines Pixels.
Kann dir dazu auch grade noch ein bischen Code schreiben:unsigned char *BerechneGraustufen( unsigned char* FarbArray, int width, int height ) { unsigned char *GrauArray = new unsigned char[width*height]; for( int i = 0; i < width*height; i++ ) { GrauArray[i] = (FarbArray[i*3] + FarbArray[i*3+1] + FarbArray[i*3+2]) / 3; } return GrauArray; }
-
duckman schrieb:
Ich weiß, diese zwei Möglichkeiten gibt es. Nur weiß ich bei beiden nicht, wie man das innerhalb von C++ durchführen kann.
Nimms mir bitte nicht böse, aber da scheint mir ein paar c++ Grundlagen zu fehlen ... warum nimmst du nicht eine GrafikBib (zB. (sehr umfangreich) OpenCV) die das schon kann, wenn du es nur benutzen willst? Dann brauchst du in der Doku / den Beispielen nur noch die Methoden finden die das Bild laden, den Filter anwenden und wieder abspeichern

Ansonsten finde ich das ein "bisschen" hochgestochen um c++ zu lernen :).
duckman schrieb:
Bzw. ist mir für Variante 2 der Algorithmus nicht bekannt.
Du löst das ganze für ein Grauwertbild. Statt grauwerten fütterst du den Algo dann einmal mit den Rot, den Grün und Blauwerten. Die resultierenden Bilder verwurstest du mit einer selbst geschriebenen max(pixeXYimrotsobel,pixelXYimgrünsobel,pixelXYimblausobel) verwurschten um ein sobel der maximalen werte zu bekommen ... wie sinnvoll das ist musst du selber entscheiden.
edit:
http://freeimage.sourceforge.net Für Bilder laden und Pixelzugriff
dann brauchst du nur noch den Sobel zu proggen...
-
Vielen Dank an alle!

Ich habe mich für die FreeImage-Library entschieden und mein Programm inzwischen fertigstellen können.
Eine Frage hätte ich aber rein interessenhalber noch:
Im Moment bearbeite ich ein Bild und speichere es dann auf der Festplatte. Wie kann man dieses (Original-)Bild nach dem Laden einfach unkompliziert auf dem Bildschirm darstellen?
Sobald der Sobel-Filter darüber gelaufen ist, soll dann das neu erstellte Bild dargestellt werden.
Ist das ohne weiteres möglich?
-
duckman schrieb:
Ist das ohne weiteres möglich?
Nein. C++ hat keine Grafikschnittstelle, also musst du die Möglichkeiten deines Ziel-OS verwenden. Am einfachsten dürfe das mit einem GUI-Framework wie QT oder wxWidgets gehen.
Bei FreeImage sind sicher auch Samples dabei die Bilder laden und darstellen, da kannst du dir bestimmt auch was abgucken.
-
Für die "libpng" gibt es noch einen netten C++-Header, der das Laden und Speichern von PNG-Grafiken sehr einfach macht.
Dann gibt es noch Boost.GIL. Ich habe es nie benutzt. Aber da ist mWn auch PNG/TIFF/JPEG Ein/Ausgabe dabei.
-
alternativ kannst du versuchen die Bilder(pfade) einfach an ein Programm in deinem Betriebssystem weiterzugeben
http://www.cplusplus.com/reference/clibrary/cstdlib/system/
Je nach system gibts Progs die Bilder betrachten möglich machen, zB GIMP, bei PNG zB auch Firefox o.ä. vielleicht kannst du die Nutzen.
Ich würds mit Qt machen, aber da ist die Einarbeitszeit bisserl hoch denke ich wenns nur um "syntactic sugar" bzw "optical sugar" geht
-
Ich muss doch noch einmal auf die eigentliche Problemstellung zuürckkommen. Inzwischen ist mir aufgegefallen, dass der Operator nicht korrekt arbeitet und sogar einen Strich quer durchs Bild macht. Am besten ich zeige euch meinen Quellcode, damit ihr es selbst nachvollziehen könnt:
Die Sobeloperatoren werden wie folgt in den Speicher gelesen, damit ich sie in die Sobelfunktion übergeben kann:
pGX =(int*)malloc(9*sizeof(int)); GX[0][0] = 1; GX[1][0] = 0; GX[2][0] = -1; GX[0][1] = 2; GX[1][1] = 0; GX[2][1] = -2; GX[0][2] = 1; GX[1][2] = 0; GX[2][2] = -1; for(j=0;j<=2;j++) { for(i=0;i<=2;i++) { *(pGX + j*3 + i)=GX[i][j]; } } // 3x3 GY Sobel Matrix. pGY =(int*)malloc(9*sizeof(int)); GY[0][0] = 1; GY[1][0] = 2; GY[2][0] = 1; GY[0][1] = 0; GY[1][1] = 0; GY[2][1] = 0; GY[0][2] = -1; GY[1][2] = -2; GY[2][2] = -1; for(j=0;j<=2;j++) { for(i=0;i<=2;i++) { *(pGY + j*3 +i)=GY[i][j]; } }Das Bild wird so in den Speicher geladen:
BYTE *bits = (BYTE*)FreeImage_GetBits(verarbeitungsBild); //verarbeitungsBild stellt das in Graustufen gewandelt Originalbild dar for(int y = 0; y < rows; y++) { BYTE *pixel = (BYTE*)bits; for(int x = 0; x < columns; x++) { *(originalImage + y*columns + x) = pixel[x]; }void SobelAlgorithmus(char *originalImage, char *edgeImage,int *pGX,int *pGY,int rows,int columns) { int I, J; int x,y; int sumX, sumY; int SUM; for(y=0; y<=(rows-1); y++) { for(x=0; x<=(columns-1); x++) { sumX = 0; sumY = 0; // Sonderbehandlung des Bildrandes if(y==0 || y==rows-1) SUM = 0; else if(x==0 || x==columns-1) SUM = 0; // Die Faltung mittels Sobel-Operator startet hier else { // X-Gradienten Approximation for(J=-1; J<=1; J++) { for(I=-1; I<=1; I++) { sumX = sumX + (int)( (*(originalImage +(y + J)*columns + x + I )) * (*(pGX + (J+1)*3 + (I+1)) )); } } if(sumX>255) sumX=255; if(sumX<0) sumX=0; // Y-Gradienten Approximation for(I=-1; I<=1; I++) { for(J=-1; J<=1; J++) { sumY = sumY + (int) ( (*(originalImage + (y + J)*columns) + x + I) * (*(pGY + (J+1)*3 + (I+1)) )); } } if(sumY>255) sumY=255; if(sumY<0) sumY=0; // Normalisierung der berechneten Werte SUM = (int)sqrt(float((sumX*sumX) + (sumY*sumY))); // Begrenzung auf 2 mögliche Zustände um die Kanten deutlicher zu machen if(SUM>127) SUM=255; else SUM=0; } *(edgeImage + x + y*columns) = (char)SUM; } }Neben den oben genannten Fehler ist es so, dass es keinen Unterschied macht, wenn ich die Y-Gradienten Approximation auskommentiere. Ich kann mir aber einfach nicht erklären, weswegen das so ist.

Hat jemand eine Idee, wo der Fehler liegen könnte? Ist der Algorithmus falsch?
-
Das sieht sehr nach C aus .... malloc und so sind ein sicherer Hinweis ... schau dir mal std::vector und Konsorten an
oder Übergabe per Reference für arrays und co.statt
for (int i=0; i <= (irgendwas-1); i++)besserfor (int i=0; i < irgendwas; ++i). Nimm pre-inc statt post-inc (ist besser)
stattJ,IlieberdeltaX, deltaYStatt mit dem Rungesobele 9 bytes zu sparen definier dir 2 Filter oder (besser da lerneffekt
) du schaust (und rechnest händisch) nochmal nach was// X-Gradienten Approximation for(J=-1; J<=1; J++) { for(I=-1; I<=1; I++) { sumX = sumX + (int)( (*(originalImage +(y + J)*columns + x + I )) * (*(pGX + (J+1)*3 + (I+1)) )); } } if(sumX>255) sumX=255; if(sumX<0) sumX=0; // Y-Gradienten Approximation for(I=-1; I<=1; I++) { for(J=-1; J<=1; J++) { sumY = sumY + (int) ( (*(originalImage + (y + J)*columns) + x + I) * (*(pGY + (J+1)*3 + (I+1)) )); } }machen ... mein Gefühl sagt mir das du die Sysmetrie des Filters nicht ganz richtig umgesetzt hast (mal dir 3 mal 3x3 auf, in den Ersten den Sobel und konstruier die anderen so wie die Schleifen den reinsetzen dann weisst du auch warum das zZ keinen Unterschied macht).
Der threshold gehört mE variabel in den Funktionsaufruf .. oder ganz ausgelagert ... und warum begrenzt du deine sumX sumY zum einen den Wert und zum anderen Normalisierst du sie? Ich würde die Begrenzung mal rausnehmen
