AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Canny edge algorithmus und Sobel Matrix

Ein Thema von Gutelo · begonnen am 26. Jul 2014 · letzter Beitrag vom 1. Aug 2014
Antwort Antwort
Gutelo

Registriert seit: 29. Sep 2013
152 Beiträge
 
#1

AW: Canny edge algorithmus und Sobel Matrix

  Alt 27. Jul 2014, 17:32
Hallo Medium,

danke fuer die Antwort. Dass man aus den x und y gradienten das endgueltige bild mittels G=sqrt(Gx*Gx + Gy*Gy) berechnet ist mir klar. Es geht mir um eine vernuenftige Darstellung der einzelnen Bilder Gx und Gy und da muessen alle Werte positiv sein.

Ich habe immer noch das Problem, dass sowohl die einzelnen Bilder als auch das endgueltige Sobel Bild keinerlei Kannten zeigen. Wahrscheinlich habe ich irgendwo einen Fehler in der Implementierung. Hier mein Code:

Delphi-Quellcode:

// ##############################################################
// Prozedur um Graubild zu erzeugen
// ##############################################################

Procedure ConvertToGrayScale(ImgIn : TBitmap; var ImgOut : TBitmap);
type TRGB = Array[1..3] of Byte;
     PixArray = Array[1..MaxInt div SizeOf(TRGB)-1] of TRGB;
     PPixArray = ^PixArray;
     PixArray2D = Array of PPixArray;
var ImgIn2D : PixArray2D; // Access to ImgIn Pixels
    ImgOut2D : PixArray2D; // Access to ImgOut Pixels
    rows,cols : Integer;
    GrayShade : Byte;
begin

   // Build 2D Arrays via 'scanline' to speed up pixel operations
   // access pixels: Img[a]^[b][c] , a=line, b=col, c: 3=R,2=G,or 1=B
   // Input Image:
   SetLength(ImgIn2D,ImgIn.Height);
   For rows := 0 to ImgIn.Height-1 do ImgIn2D[rows] := ImgIn.ScanLine[rows];
   // Output Image:
   SetLength(ImgOut2D,ImgOut.Height);
   For rows := 0 to ImgOut.Height-1 do ImgOut2D[rows] := ImgOut.ScanLine[rows];

   // Convert to grayscale
   for rows := 0 to ImgIn.Height-1 do
   for cols := 0 to ImgIn.Width-1 do
    begin
      GrayShade := Round( 0.3 * ImgIn2D[rows]^[cols][3]
                         + 0.6 * ImgIn2D[rows]^[cols][2]
                         + 0.1 * ImgIn2D[rows]^[cols][1]);
      ImgOut2D[rows]^[cols][1] := GrayShade; // B-value
      ImgOut2D[rows]^[cols][2] := GrayShade; // G-value
      ImgOut2D[rows]^[cols][3] := GrayShade; // R-value
    end;
end;


// ##############################################################
// Prozeduren um Filter Matrizen zu erzeugen
// ##############################################################

Procedure MakeSmoothenFilter(var Filter : TFilter);
var i : Integer;
begin
   // Set Dimensions of Filter Array
   SetLength(Filter,5);
   for i := 0 to High(Filter) do SetLength(Filter[i],5);
   // Define Gauss Filter Matrix
   // 2 4 5 4 2
   // 4 9 12 9 4
   // 5 12 15 12 5
   // 4 9 12 9 4
   // 2 4 5 4 2
   // first Line
   Filter[0,0] := 2; Filter[0,1] := 4; Filter[0,2] := 5;
   Filter[0,3] := 4; Filter[0,4] := 2;
   // second Line
   Filter[1,0] := 4; Filter[1,1] := 9; Filter[1,2] := 12;
   Filter[1,3] := 9; Filter[1,4] := 4;
   // third Line
   Filter[2,0] := 5; Filter[2,1] := 12; Filter[2,2] := 15;
   Filter[2,3] := 12; Filter[2,4] := 5;
   // fourth Line
   Filter[3,0] := 4; Filter[3,1] := 9; Filter[3,2] := 12;
   Filter[3,3] := 9; Filter[3,4] := 4;
   // fifth Line
   Filter[4,0] := 2; Filter[4,1] := 4; Filter[4,2] := 5;
   Filter[4,3] := 4; Filter[4,4] := 2;
end;

Procedure MakeSobelFilterX(var Filter : TFilter);
var i : integer;
begin
   // Set Dimensions of Filter Array
   SetLength(Filter,3);
   for i := 0 to High(Filter) do SetLength(Filter[i],3);
   // Define Sobel-X Filter Matrix
   // -1 0 1
   // -2 0 2
   // -1 0 1
   // first Line
   Filter[0,0] := -1; Filter[0,1] := 0; Filter[0,2] := 1;
   // second Line
   Filter[1,0] := -2; Filter[1,1] := 0; Filter[1,2] := 2;
   // third Line
   Filter[2,0] := -1; Filter[2,1] := 0; Filter[2,2] := 1;
end;

Procedure MakeSobelFilterY(var Filter : TFilter);
var i : integer;
begin
   // Set Dimensions of Filter Array
   SetLength(Filter,3);
   for i := 0 to High(Filter) do SetLength(Filter[i],3);
   // Define Sobel-Y Filter Matrix
   // 1 2 1
   // 0 0 0
   // -1 -2 -1
   // first Line
   Filter[0,0] := 1; Filter[0,1] := 2; Filter[0,2] := 1;
   // second Line
   Filter[1,0] := 0; Filter[1,1] := 0; Filter[1,2] := 0;
   // third Line
   Filter[2,0] := -1; Filter[2,1] := -2; Filter[2,2] := -1;
end;


// ##############################################################
// Prozedur um Filter auf ein Bild anzuwenden (Faltung)
// ##############################################################

Procedure ApplyFilterOnImage(var ImgIn, ImgOut : TBitmap; Filter : TFilter);
type TRGB = Array[1..3] of Byte;
     PixArray = Array[1..MaxInt div SizeOf(TRGB)-1] of TRGB;
     PPixArray = ^PixArray;
     PixArray2D = Array of PPixArray;
var rows,cols : Integer; // rows and cols in the image
    FLowX, FHighX : Integer;
    FLowY, FHighY : Integer;
    weight : Integer; // Weight for Filter Matrix
    ImgIn2D : PixArray2D; // Access to ImgIn Pixels
    ImgOut2D : PixArray2D; // Access to ImgOut Pixels
    SumMinus : Integer;
    ScaleShift : Integer;
    PixelSum, x, y : Integer;
    Pixel : Integer;
begin
   // Filter matrix, element -> pixel position with respect to center pixel
   // Calculate extremes in x and y direction
   FLowX := -Round(0.5 * Length(Filter)-1);
   FHighX := +Round(0.5 * Length(Filter)-1);
   FLowY := -Round(0.5 * Length(Filter[0])-1);
   FHighY := +Round(0.5 * Length(Filter[0])-1);

   // Determine matrix weight and scaleshift required after convolution:
   ScaleShift := 0;
   SumMinus := 0;
   weight := 0;
   For y := 0 to High(Filter[0]) do
   For x := 0 to High(Filter) do
   begin
      if Filter[x,y] < 0 then SumMinus := SumMinus + Filter[x,y];
      weight := weight + Abs(Filter[x,y]);
   end;
   ScaleShift := Abs(Round((SumMinus/weight)*255));

   // Build 2D Arrays via 'scanline' to speed up pixel operations
   // access pixels: Img[a]^[b][c] , a=line, b=col, c: 3=R,2=G,or 1=B
   // Input Image:
   SetLength(ImgIn2D,ImgIn.Height);
   For rows := 0 to ImgIn.Height-1 do ImgIn2D[rows] := ImgIn.ScanLine[rows];
   // Output Image:
   SetLength(ImgOut2D,ImgOut.Height);
   For rows := 0 to ImgOut.Height-1 do ImgOut2D[rows] := ImgOut.ScanLine[rows];

   // Calculate filtered Image, i.e. convolution of Filter and ImgIn -> ImgOut
   For rows := 0-FLowY to ImgIn.Height-1-FHighY do
   For cols := 0-FLowX to ImgIn.Width-1-FHighX do
   begin
      // Perform Convolution
      PixelSum := 0;
      For y := FLowY to FHighY do
      For x := FLowX to FHighX do
      begin
        PixelSum := PixelSum +
            Filter[x-FLowX,y-FLowY]* ImgIn2D[rows+y]^[cols+x][1];
      end;
      Pixel := Round(PixelSum/weight) + ScaleShift;
      ImgOut2D[rows]^[cols][1] := Pixel;
      ImgOut2D[rows]^[cols][2] := Pixel;
      ImgOut2D[rows]^[cols][3] := Pixel;
   end;
end;

// ##############################################################
// Prozedur um finales Sobelbild aus Einzelbildern zu erzeugen
// ##############################################################

Procedure GenerateFinalSobel(ImgSx, ImgSy : TBitmap; var ImgOut : TBitmap);
type TRGB = Array[1..3] of Byte;
     PixArray = Array[1..MaxInt div SizeOf(TRGB)-1] of TRGB;
     PPixArray = ^PixArray;
     PixArray2D = Array of PPixArray;
var rows,cols : Integer; // rows and cols in the image
     ImgSx2D : PixArray2D; // Access to ImgSx Pixels
     ImgSy2D : PixArray2D; // Access to ImgSy Pixels
     ImgOut2D : PixArray2D; // Access to ImgOut Pixels
     Gx,Gy,Gf : Byte;
begin
   // Build 2D Arrays via 'scanline' to speed up pixel operations
   // access pixels: Img[a]^[b][c] , a=line, b=col, c: 3=R,2=G,or 1=B
   // SobelX Image:
   SetLength(ImgSx2D,ImgSx.Height);
   For rows := 0 to ImgSx.Height-1 do ImgSx2D[rows] := ImgSx.ScanLine[rows];
   // SobelY Image:
   SetLength(ImgSy2D,ImgSy.Height);
   For rows := 0 to ImgSy.Height-1 do ImgSy2D[rows] := ImgSy.ScanLine[rows];
   // Output Image:
   SetLength(ImgOut2D,ImgOut.Height);
   For rows := 0 to ImgOut.Height-1 do ImgOut2D[rows] := ImgOut.ScanLine[rows];

   // Perform G = sqrt(Gx*Gx + Gy*Gy);
   for rows := 0 to ImgSx.Height-1 do
   for cols := 0 to ImgSx.Width-1 do
   begin
     Gx := ImgSx2D[rows]^[cols][1];
     Gy := ImgSy2D[rows]^[cols][1];
     Gf := Round(sqrt(Abs(Gx*Gx)+Abs(Gy*Gy)));
     ImgOut2D[rows]^[cols][1] := Gf;
     ImgOut2D[rows]^[cols][2] := Gf;
     ImgOut2D[rows]^[cols][3] := Gf;
   end;
end;

// ##############################################################
// Prozedur um geglaettetes Graubild zu erzeugen
// ##############################################################

Procedure Smoothen(var ImgIn, ImgOut : TBitmap);
var GaussFilter : TFilter;
    SobelXFilter : TFilter;
    ImgGray : TBitmap;
begin
   //convert to Grayscale
   ImgGray := TBitmap.Create;
   ImgGray.Width := ImgIn.Width;
   ImgGray.Height := ImgIn.Height;
   ConvertToGrayScale(ImgIn, ImgGray);
   //smoothen grayscale image
   MakeSmoothenFilter(GaussFilter);
   ApplyFilterOnImage(ImgGray, ImgOut, GaussFilter);
end;

// ##############################################################
// Prozedur fuer kompletten Sobel
// ##############################################################

Procedure Sobel(var ImgIn, ImgOut : TBitmap);
var SobelX, SobelY : TFilter;
    ImgSmooth, ImgSx, ImgSy : TBitmap;
begin
    // create ImgSmooth
   ImgSmooth := TBitmap.create;
   ImgSmooth.width := ImgIn.width;
   ImgSmooth.height := ImgIn.height;
   // Smoothen Image
   Smoothen(ImgIn, ImgSmooth);
   // create ImgSx
   ImgSx := TBitmap.create;
   ImgSx.width := ImgIn.width;
   ImgSx.height := ImgIn.height;
   // create ImgSy
   ImgSy := TBitmap.create;
   ImgSy.width := ImgIn.width;
   ImgSy.height := ImgIn.height;
   // Make SobelX and SobelY Filter
   MakeSobelFilterX(SobelX);
   MakeSobelFilterY(SobelY);
   // Apply Sobel Filter on ImgIn -> ImgSx and ImgSy
   ApplyFilterOnImage(ImgSmooth, ImgSx, SobelX);
   ApplyFilterOnImage(ImgSmooth, ImgSy, SobelY);
   // ImgSx and ImgSy -> ImgOut
   GenerateFinalSobel(ImgSx, ImgSy, ImgOut);
end;
Hier eine Prozedur zum Aufruf des Ganzen:

Delphi-Quellcode:
procedure TForm1.Button2Click(Sender: TObject);
var ImgIn, ImgOut : TBitmap;
begin
  ImgIn := TBitmap.Create;
  ImgOut := TBitmap.Create;
  ImgIn.LoadFromFile('Book.bmp');
  ImgOut.Width := ImgIn.Width;
  ImgOut.Height := ImgIn.Height;
  //Smoothen(ImgIn, ImgOut);
  Sobel(ImgIn, ImgOut);
  Image3.Picture.Bitmap := ImgOut;
end;
Die Umwandlung in ein Graubild und das Glaetten mittels Gaussfilter klappt wunderbar. Die einzelnen Sobelbilder sehen nicht aus wie Bilder in denen horizontale oder vertikale Kannten betont sind sondern eher wie helle oder dunkle Bilder mit stark veraendertem Gammawert. Im finalen Sobelbild ist quasi nichts zu sehen, d.h. mehr oder weniger einfarbige Flaeche.

Gutelo

Edit: Code ist mit Lazarus erstellt, sollte aber 1:1 auf Delphi laufen

Geändert von Gutelo (27. Jul 2014 um 17:41 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von jfheins
jfheins

Registriert seit: 10. Jun 2004
Ort: Garching (TUM)
4.579 Beiträge
 
#2

AW: Canny edge algorithmus und Sobel Matrix

  Alt 27. Jul 2014, 19:03
Hast du mal ein Eingabebild und die zwei Ausgaben, die dein Programm produziert?
  Mit Zitat antworten Zitat
Gutelo

Registriert seit: 29. Sep 2013
152 Beiträge
 
#3

AW: Canny edge algorithmus und Sobel Matrix

  Alt 27. Jul 2014, 22:39
Hallo,

Ich habe die Bilder an dieses Post gehaengt

Gutelo
Angehängte Grafiken
Dateityp: jpg Original.jpg (45,8 KB, 30x aufgerufen)
Dateityp: jpg Gray_Smooth_Sy.jpg (6,4 KB, 26x aufgerufen)
Dateityp: jpg Gray_Smooth_Sx.jpg (6,4 KB, 25x aufgerufen)
Dateityp: jpg Gray_Smooth.jpg (33,7 KB, 27x aufgerufen)
Dateityp: jpg Gray.jpg (43,6 KB, 25x aufgerufen)
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.688 Beiträge
 
Delphi 2007 Enterprise
 
#4

AW: Canny edge algorithmus und Sobel Matrix

  Alt 28. Jul 2014, 01:38
Zwar sehen deine "gesobelten" Bilder in der Tat etwas komisch aus (eher wie kontrastverminderte und teilinvertierte Versionen des Originals), aber ich sehe ein großes Problem bei deiner Vorgehensweise: Du nutzt als Zwischenspeicher für deine Schritte immer ein TBitmap. Du brauchst aber auch die negativen Werte der Sobel-Komponenten um nachher die korrekten Magnituden und Richtungen zu berechnen, da kommst du nicht drum rum!

Ich hatte für mein Programm damals eine eigenen FloatBitmap Klasse gemacht, und habe diese für sämtliche Operationen genutzt - ausser Laden und Speichern, dafür konnte diese Klasse ein .CreateFromBitmap() und ein .ToBitmap() durchführen. Ich habe die Bilder darin, also zumindest die, die zur Darstellung genutzt wurden, auf die Range 0..1 skaliert, und alle Farbkomponenten als Single hinterlegt. (Hatte auch den Hintergrund, dass ich das nachher so umgebaut habe, dass Gauss und Sobel in einem Shader von der Graka gemacht wurden, wo man das eh so braucht.)
Für die Farben hatte ich eine eigene FloatColor Klasse, die Methoden wie .ToYCC() und .ToRGB() oder .ToColor() gleich mit gebracht hat. Das FloatBitmap hat davon einfach ein 2D Array verwaltet, und ein paar Methoden zum Umwandeln eben.

Nutzt man sowas als "Mittler" zwischen den Schritten, sind Negative Farben und denormalisierte "Bilder" kein Problem mehr (streng genommen sind es ja schon fast keine Bilder mehr dann). Das würde ich als aller erstes machen, und dann mal schauen wie es danach aussieht.

Sowohl die Normaierung auf 0..1, als auch die deutlich erhöhte Genauigkeit durch Floats sind gerade bei den Zwischenschritten zum einen eine Vereinfachung, und das Zweite je nach Anwendungsfall schon fast eine Notwendigkeit finde ich. Wenn die Geschwindigkeit nicht hin haut, könnte man auch noch mit auf Integer-Range skalierten Farben arbeiten, muss dann aber aufpassen mit Ausreissern in den Zwischenschritten die wie beim Sobel dann auch mal über "1" (bzw. MaxInt) liegen können. Und es ist eine ziemliche Rumrechnerei. Wenn wirklich sehr schnelle Verarbeitung sein soll, würde ich letztlich wieder zum Shader greifen. Da das aber eine ganze Ecke Infrastruktur und potenziell Anpassungen im gesamten Programm braucht wenn man es nicht von Anfang an darauf ausgelegt hat, will ich das nicht uneingeschränkt als Heiland preisen.

Aber deine negativen Werte brauchst du. Definitiv. Wenn du die Zwischenschritte anzeigen willst, würde ich dafür das FloatBitmap von (-1..1) auf (0..255) mappen, und Werte unter/über -1/1 clampen. Dann bleibt genügend "Kontrast" um vernünftig was sehen zu können. (Diese Bilder taugen dann aber nicht mehr wirklich zum weiter damit arbeiten, nur zur Kontrollanzeige.)
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Gutelo

Registriert seit: 29. Sep 2013
152 Beiträge
 
#5

AW: Canny edge algorithmus und Sobel Matrix

  Alt 28. Jul 2014, 02:04
Hallo Medium

Stimmt, ich sollte erstmal die Minuswerte beibehalten.

Was meinst du mit "würde ich dafür das FloatBitmap von (-1..1) auf (0..255) mappen, und Werte unter/über -1/1 clampen". Wenn ich nicht normiere dann varrieren die Werte von minimal -4*256 bis maximal 4*256 in den beiden "Teilbildern". Im naechsten Schritt - sqrt(Gx*Gx + Gy*Gy) - sollten aehnliche Werte rauskommen. Am Ende muesste ich doch diese Bereiche auf (0..255) mappen oder nicht?

Also ich rechne jetzt mal ohne Normierungsfaktor fuer die Sobel Matrizen die Werte fuer beide Sobel operatoren aus und anschliessend das finale Sobel Ergebnis. Danach bestimme ich die maximalen negativen und positiven Werte. Dann skaliere ich, so dass alle Werte zwischen 0 und 255 liegen. Richtig?

Gutelo
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.688 Beiträge
 
Delphi 2007 Enterprise
 
#6

AW: Canny edge algorithmus und Sobel Matrix

  Alt 28. Jul 2014, 02:34
Wenn ich nicht normiere dann varrieren die Werte von minimal -4*256 bis maximal 4*256 in den beiden "Teilbildern".
Ja, deswegen meinte ich ja, dass die so gemappten Bilder nur zum Angucken taugen. Werte jenseits von -1 und 1 sind recht seltene Extrema in Fotos, und wenn man etwa -4 bis 4 auf 0 bis 255 mapped wird das Bild überwiegend wie 50% Grau aussehen. Geht natürlich auch, fand ich zum zwischen drin mal beschauen aber nicht so prima.

Zitat:
Im naechsten Schritt - sqrt(Gx*Gx + Gy*Gy) - sollten aehnliche Werte rauskommen. Am Ende muesste ich doch diese Bereiche auf (0..255) mappen oder nicht?
Nur, wenn du das Gradienten-"Bild" anzeigen willst. Letztlich willst du doch eigentlich nur deine Kanten haben, oder? Der Sobel-Schritt wird also am Ende nie angezeigt. Warum sollte man ihn dann auf Bitmap-Werte umrechnen? Einfach so lassen wie es aus der Filterung kommt, und damit die Non-Maxima-Unterdrückung machen. Was davon übrig bleibt dann als S/W Bild nehmen (Kanten weiss, Rest schwarz) oder wie auch immer weiter damit arbeiten. Aber löse dich davon, dass die Zwischenschritte "Bilder" sind. Man kann sie zur Anzeige als solche interpretieren und daraufhin anpassen, aber an sich sind das nur noch große Matrizen mit "irgendwelchen" Werten. Deswegen plädiere ich auch dafür, dass man das TBitmap als Container für diese nicht nimmt.

Zitat:
Also ich rechne jetzt mal ohne Normierungsfaktor fuer die Sobel Matrizen die Werte fuer beide Sobel operatoren aus und anschliessend das finale Sobel Ergebnis. Danach bestimme ich die maximalen negativen und positiven Werte. Dann skaliere ich, so dass alle Werte zwischen 0 und 255 liegen. Richtig?
Jain, s.o.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Gutelo

Registriert seit: 29. Sep 2013
152 Beiträge
 
#7

AW: Canny edge algorithmus und Sobel Matrix

  Alt 28. Jul 2014, 23:46
Soderle, hab es jetzt mal umgeschrieben. Funktioniert soweit so gut -> Siehe angehaengte Bilder.
Als naechstes nehme ich NMS, Hysteresis und Hough in Angriff.


Falls jemand etwas Aehnliches machen moechte, hier mein Code bis hierhin. An einigen Stellen sicher noch etwas suboptimal und verbesserungsfaehig (z.B. dass das gesammte Array extra Durchlaufen wird um die minimalen und maximalen Werte zu bestimmen.)

Delphi-Quellcode:
unit Canny;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, Graphics, LCLIntf, Dialogs, Math;

type // type to store a filter
     TFilter = Array of Array of Integer;
     // types for fast access to TBitmap image pixels
     TRGB = Array[1..3] of Byte;
     PixArray = Array[1..MaxInt div SizeOf(TRGB)-1] of TRGB;
     PPixArray = ^PixArray;
     PixArray2D = Array of PPixArray;
     // type to store the pixels of grayscale TBitmaps in an Array
     // and to perform filter operations on the array values
     TPixelArray = Array of Array of integer;


// Export
Procedure ConvertToGrayScale(ImgIn : TBitmap; var ImgOut : TBitmap);
Procedure CalcSobel(ImgIn : TBitmap; var ImgOut : TBitmap);

implementation

// ############################################################################
// Convert Color Image to Grayscale Image
// ############################################################################

Procedure ConvertToGrayScale(ImgIn : TBitmap; var ImgOut : TBitmap);
//
var ImgIn2D : PixArray2D; // Access to ImgIn Pixels
    ImgOut2D : PixArray2D; // Access to ImgOut Pixels
    rows,cols : Integer; // rows and cols of an image
    GrayShade : Byte; // Gray value
//
begin
   // Build 2D Arrays via 'scanline' to speed up pixel operations
   // access pixels: Img[a]^[b][c] , a=line, b=col, c: 3=R,2=G,or 1=B
   // Input Image:
   SetLength(ImgIn2D,ImgIn.Height);
   For rows := 0 to ImgIn.Height-1 do ImgIn2D[rows] := ImgIn.ScanLine[rows];
   // Output Image:
   SetLength(ImgOut2D,ImgOut.Height);
   For rows := 0 to ImgOut.Height-1 do ImgOut2D[rows] := ImgOut.ScanLine[rows];
   // Convert to grayscale:
   for rows := 0 to ImgIn.Height-1 do
   for cols := 0 to ImgIn.Width-1 do
    begin
      GrayShade := Round( 0.3 * ImgIn2D[rows]^[cols][3]
                         + 0.6 * ImgIn2D[rows]^[cols][2]
                         + 0.1 * ImgIn2D[rows]^[cols][1]);
      ImgOut2D[rows]^[cols][1] := GrayShade; // B-value
      ImgOut2D[rows]^[cols][2] := GrayShade; // G-value
      ImgOut2D[rows]^[cols][3] := GrayShade; // R-value
    end;
end;

// ############################################################################
// Make Gauss Filter Matrix
// ############################################################################

Procedure MakeGaussFilter(var Filter : TFilter);
//
var i : Integer;
//
begin
   // Set dimensions of filter array
   SetLength(Filter,5);
   for i := 0 to High(Filter) do SetLength(Filter[i],5);
   // Define Gauss filter matrix
   // 2 4 5 4 2
   // 4 9 12 9 4
   // 5 12 15 12 5
   // 4 9 12 9 4
   // 2 4 5 4 2
   // first Line
   Filter[0,0] := 2; Filter[0,1] := 4; Filter[0,2] := 5;
   Filter[0,3] := 4; Filter[0,4] := 2;
   // second Line
   Filter[1,0] := 4; Filter[1,1] := 9; Filter[1,2] := 12;
   Filter[1,3] := 9; Filter[1,4] := 4;
   // third Line
   Filter[2,0] := 5; Filter[2,1] := 12; Filter[2,2] := 15;
   Filter[2,3] := 12; Filter[2,4] := 5;
   // fourth Line
   Filter[3,0] := 4; Filter[3,1] := 9; Filter[3,2] := 12;
   Filter[3,3] := 9; Filter[3,4] := 4;
   // fifth Line
   Filter[4,0] := 2; Filter[4,1] := 4; Filter[4,2] := 5;
   Filter[4,3] := 4; Filter[4,4] := 2;
end;

// ############################################################################
// Make Sobel-X Filter Matrix
// ############################################################################

Procedure MakeSobelXFilter(var Filter : TFilter);
//
var i : integer;
//
begin
   // Set dimensions of filter array
   SetLength(Filter,3);
   for i := 0 to High(Filter) do SetLength(Filter[i],3);
   // Define Sobel-X filter matrix
   // -1 0 1
   // -2 0 2
   // -1 0 1
   // first Line
   Filter[0,0] := -1; Filter[0,1] := 0; Filter[0,2] := 1;
   // second Line
   Filter[1,0] := -2; Filter[1,1] := 0; Filter[1,2] := 2;
   // third Line
   Filter[2,0] := -1; Filter[2,1] := 0; Filter[2,2] := 1;
end;

// ############################################################################
// Make Sobel-Y Filter Matrix
// ############################################################################

Procedure MakeSobelYFilter(var Filter : TFilter);
//
var i : integer;
//
begin
   // Set dimensions of filter array
   SetLength(Filter,3);
   for i := 0 to High(Filter) do SetLength(Filter[i],3);
   // Define Sobel-Y filter matrix
   // 1 2 1
   // 0 0 0
   // -1 -2 -1
   // first Line
   Filter[0,0] := 1; Filter[0,1] := 2; Filter[0,2] := 1;
   // second Line
   Filter[1,0] := 0; Filter[1,1] := 0; Filter[1,2] := 0;
   // third Line
   Filter[2,0] := -1; Filter[2,1] := -2; Filter[2,2] := -1;
end;

// ############################################################################
// Convert "Image -> Pixel Array" and "Pixel Array -> Image"
// ############################################################################

Procedure ImgToPixArray(ImgIn : TBitmap; var PixArr : TPixelArray);
//
var H,W : Integer;
    ImgIn2D : PixArray2D;
    rows, cols : Integer;
//
begin
  // Store height and width of input image
  H := ImgIn.Height;
  W := ImgIn.Width;
  // Fast access to input image:
  SetLength(ImgIn2D,H);
  For rows := 0 to H-1 do ImgIn2D[rows] := ImgIn.ScanLine[rows];
  // Set dimensions of pixel array
  SetLength(PixArr,H);
  For rows := 0 to H-1 do SetLength(PixArr[rows],W);
  // Copy pixel values of input image to pixel array
  For rows := 0 to H-1 do
  For cols := 0 to W-1 do
  begin
    PixArr[rows,cols] := ImgIn2D[rows]^[cols][1];
  end;
end;

Procedure PixArrayToImg(PixArr : TPixelArray; var ImgOut : TBitmap);
//
var H,W : Integer;
    ImgOut2D : PixArray2D;
    rows, cols : Integer;
    MinVal, MaxVal : integer;
    Pixel : Integer;
//
begin
   // Store height and width of input image
   H := Length(PixArr);
   W := Length(PixArr[0]);
   // Initialize output image
   ImgOut := TBitmap.Create;
   ImgOut.Width := W;
   ImgOut.Height := H;
   // Fast access to output Image:
   SetLength(ImgOut2D,H);
   For rows := 0 to H-1 do ImgOut2D[rows] := ImgOut.ScanLine[rows];
   // Determine minimum and maximum values in pixel array
   MinVal := PixArr[0,0];
   MaxVal := PixArr[0,0];
   For rows := 0 to H-1 do
   For cols := 0 to W-1 do
   begin
      if PixArr[rows,cols] < MinVal then MinVal := PixArr[rows,cols];
      if PixArr[rows,cols] > MaxVal then MaxVal := PixArr[rows,cols];
   end;
   // Rescale value range to 0..255 and copy values to output image
   For rows := 0 to H-1 do
   For cols := 0 to W-1 do
   begin
     // only scale if necessary
     if (MinVal < 0) or (MaxVal > 255)
     then
         Pixel := Round(255*((PixArr[rows,cols] - MinVal)/ (MaxVal-MinVal)))
     else
         Pixel := PixArr[rows,cols];
     // Copy values to output image
     ImgOut2D[rows]^[cols][1] := Pixel;
     ImgOut2D[rows]^[cols][2] := Pixel;
     ImgOut2D[rows]^[cols][3] := Pixel;
   end;
end;

// ############################################################################
// Apply Filter on Pixel Array
// ############################################################################

Procedure ApplyFilterOnPixArray(PA_In : TPixelArray; var PA_Out : TPixelArray; Filter : TFilter; DoWeight : Boolean);
//
var H,W : Integer; // Height and width of ImgIn
    rows,cols : Integer; // rows and cols in the image
    weight : Integer; // Weight for Filter Matrix
    FilterBT : Integer; // Border Thickness of Filter around central element
    x, y : Integer; // Variables for Convolution
    PixelSum : Integer; // Convolution Value
    Pixel : Integer; // Final Pixel Value
//
begin
   // Border thickness of filter around central matrix element:
   // Examples: 3x3 Matrix -> BT=1 , 5x5 Matrix -> BT=3
   FilterBT := Round(0.5 * (Length(Filter)-1));
   // Store height and width of input pixel array (PA_In):
   H := Length(PA_In);
   W := Length(PA_In[0]);
   // Set dimensions of output pixel array (PA_Out), "removes Filter Border":
   SetLength(PA_Out, H-(2*FilterBT));
   For rows := 0 to High(PA_Out) do SetLength(PA_Out[rows],W-(2*FilterBT));
   // Determine filter matrix weight:
   weight := 0;
   For y := 0 to High(Filter[0]) do
   For x := 0 to High(Filter) do
   begin
      weight := weight + Abs(Filter[x,y]);
   end;
   // Calculate filtered Image, i.e. perform convolution of Filter and PA_In -> PA_Out
   For rows := FilterBT to High(PA_In)-FilterBT do
   For cols := FilterBT to High(PA_In[0])-FilterBT do
   begin
      // Perform convolution
      PixelSum := 0;
      For y := -FilterBT to FilterBT do
      For x := -FilterBT to FilterBT do
      begin
        PixelSum := PixelSum + Filter[x+FilterBT,y+FilterBT]* PA_In[rows+y,cols+x];
      end;
      // Apply weight when required
      if DoWeight then Pixel := Round(PixelSum/weight)
                  else Pixel := PixelSum;
      // write output pixel array
      PA_Out[rows-FilterBT,cols-FilterBT] := Pixel;
   end;
end;

// ############################################################################
// Make final Sobel pixel array based on Sobel-X and Sobel-Y
// ############################################################################

Procedure MakeSobel(PA_Sx, PA_Sy : TPixelArray; var PA_Out : TPixelArray);
//
var rows, cols : Integer;
    Sx, Sy : Integer;
//
begin
   // Set dimensions of output pixel array
   SetLength(PA_Out,Length(PA_Sx));
   For rows := 0 to High(PA_Out) do SetLength(PA_Out[rows], Length(PA_Sx[0]));
   // calculate output pixel array:
   For rows := 0 to High(PA_Sx) do
   For cols := 0 to High(PA_Sx[0]) do
   begin
      Sx := PA_Sx[rows,cols];
      Sy := PA_Sy[rows,cols];
      // write output pixel array
      PA_Out[rows,cols] := Round(sqrt(Sx*Sx + Sy*Sy));
   end;
end;

// ############################################################################
// Procedure to perform complete Sobel calculation
// ############################################################################

Procedure CalcSobel(ImgIn : TBitmap; var ImgOut : TBitmap);
var ImgGray : TBitmap;
    PA_Gray, PA_Gauss, PA_SobelX, PA_SobelY, PA_Sobel : TPixelArray;
    GaussFilter, SobelX, SobelY : TFilter;
begin
  ImgGray := TBitmap.Create;
  ImgGray.Width := ImgIn.Width;
  ImgGray.Height := ImgIn.Height;
  // Convert color input image (ImgIn) to grayscale image (ImgGray)
  // and convert ImgGray to pixel array format (PA_Gray)
  ConvertToGrayScale(ImgIn,ImgGray);
  ImgToPixArray(ImgGray, PA_Gray);
  // Apply Gauss smoothening filter to PA_Gray -> PA_Gauss
  MakeGaussFilter(GaussFilter);
  ApplyFilterOnPixArray(PA_Gray, PA_Gauss, GaussFilter, true);
  // Apply SobelX filter to PA_Gauss -> PA_SobelX
  MakeSobelXFilter(SobelX);
  ApplyFilterOnPixArray(PA_Gauss, PA_SobelX, SobelX, false);
  // Apply SobelY filter to PA_Gauss -> PA_SobelY
  MakeSobelYFilter(SobelY);
  ApplyFilterOnPixArray(PA_Gauss, PA_SobelY, SobelY, false);
  // Combine PA_SobelX and PA_SobelY -> PA_Sobel
  MakeSobel(PA_SobelX, PA_SobelY, PA_Sobel);
  // Convert PA_Sobel to output image (ImgOut)
  PixArrayToImg(PA_Sobel, ImgOut);
end;

end.
Verwendung aus Hauptprogramm:

Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var ImgIn, ImgOut : TBitmap;
begin
  ImgIn := TBitmap.Create;
  ImgOut := TBitmap.Create;
  ImgIn.LoadFromFile('Test.bmp');
  CalcSobel(ImgIn, ImgOut);
  Image3.Picture.Bitmap := ImgOut;
end;
Gutelo
Angehängte Grafiken
Dateityp: jpg Book.jpg (45,8 KB, 38x aufgerufen)
Dateityp: jpg Gray_Gauss.jpg (33,5 KB, 34x aufgerufen)
Dateityp: jpg SobelX.jpg (32,4 KB, 39x aufgerufen)
Dateityp: jpg SobelY.jpg (32,4 KB, 38x aufgerufen)
Dateityp: jpg Sobel.jpg (38,7 KB, 51x aufgerufen)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:05 Uhr.
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz