Bitmap Pixel Koordinaten ändern

Für Probleme bezüglich Grafik, Audio, GL, ACS, ...
ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Bitmap Pixel Koordinaten ändern

Beitrag von ErnstVolker »

Hallo Zusammen,

ich versuche mich gerade an einer Bildentzerrung um wahre Längen aus Fotos zu erhalten. Dazu ist ein 8x8-Gleichungssystem zu lösen, welches ich ich mit "slegen" aus NumLib gelöst habe. Die Lösung stimmt auch, konnte ich mit Excel verifizieren. Der Lösungsvektor hat die 8 Koeffizienten a0 bis a7.

Mit den Koeffizienten kann man mit folgenden zwei Formeln:

xWelt = (a0 * u + a1 * v + a2) / (a6 * u + a7 * v +1) und yWelt = (a3 * u + a4 * v + a5) / (a6 * u + a7 * v +1) die "wahren Koordinaten" berechnen. Bei "u" und "v" handelt es sich um die Pixelkoordinaten des Ausgangsbildes.

Ich würde jetzt gerne aufgrund der Pixelkoordinaten des Ausgangsbildes das entzerrte Bild berechnen. Einen Anfang habe ich gemacht aber es kommt nix vernünftiges bei raus. Hier mein Ansatz:

Code: Alles auswählen

Breite := FrmBild.Image1.Picture.Bitmap.Width;
Hoehe := FrmBild.Image1.Picture.Bitmap.Height;
 
EntzBMP:= TBitmap.Create;
  EntzBMP.SetSize(Breite, Hoehe);
  EntzBMP.PixelFormat:=FrmBild.Image1.Picture.Bitmap.PixelFormat;
  for i:=0 to Breite-1 do begin
    for j:=0 to Hoehe-1 do begin
      EntzBMP.Canvas.Pixels[i,j]:= FrmBild.Image1.Picture.Bitmap.Canvas.Pixels[Round(((a[0] * i +a[1] * j +a[2])/(a[6] * i +a[7] * j +1))), Round(((a[3] * i +a[4] * j +a[5])/(a[6] * i+a[7] * j +1)))];
    end;
  end;
 
 ShowMessage('Entzerrtes Bild berechnet!');
 FrmEntzerrt.Image1.Picture.Bitmap.Assign(EntzBMP);
 FrmEntzerrt.Show;
 EntzBMP.Free;


Die Neuberechnung des Bildes dauert, bei einer Auflösung von 1920 x 1440 auch etwas. Kann man das irgenwie, evtl. durch Verwenden von BGRABitmap, beschleunigen?

Vielen Dank für Eure Hilfe

Volker

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Bitmap Pixel Koordinaten ändern

Beitrag von wp_xyz »

u und v sind die Pixelkoordinaten im Ausgangsbild. Du berechnest in xWelt und yWelt die Koordinaten des Pixels im Zielbild, auf das das Ausgangspixel abgebildet wird. Um jedes Pixel im Zielbild zu treffen laufen deine Schleifen über die Koordinaten des Zielbildes. Aber du setzt diese in deinen Formeln für u und v ein - ich denke das ist nicht richtig, denn u und v sind die Koordinaten im Ausgangsbild.

Damit das funktioniert musst du die Koeffizierten der Umkehrfunktion berechnen. Momentan hast du die Abbildung (u,v) --> (i,j); du brauchst aber (i,j) --> (u,v). Damit wüsstest du für jedes Pixel I,j des Zielbildes das zugehörige Pixel im Ausgangsbild.

Zur Geschwindigkeit: Canvas.Pixels ist sehr langsam. Schneller geht's mit BGRABitmap oder mit LazIntfImage (was keine separate Bibliothek erfordert): https://wiki.freepascal.org/Developing_ ... TLazCanvas. Aber löse zuerst das Problem mit der Abbildung.

[EDIT etwas später]
Oder vielleicht doch nicht die Umkehrfunktion...? Wie machst du die Entzerrung von der Idee her? Werden die vier ecken eines verzerrten Quellbild-Vierecks auf die vier Ecken des "schönen" Rechtecks des Zielbildes abgebildet und dazwischen wird linear interpoliert?

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Bitmap Pixel Koordinaten ändern

Beitrag von ErnstVolker »

Hallo wp_xy,

Hier schien heute Mittag die Sonne, da bin ich mal in ein Freilichtmuseum gefahren und noch was gefuttert. Deshalb erst verspätet meine Antwort.

Danke für den Tipp. Das Wort Zielbild ist es gewesen. Ich habe die Zeile einfach "rumgedreht" und die Koordinatenformel in die eckigen-Pixelklammern[] von "EntzBMP.Canvas.Pixels[]" eingefügt. Dazu noch Skalierfaktoren, damit geht's. Die Skalierfaktoren passt die wahren Längen auf die Bildbreite bzw. Bildhoehe an.

Code: Alles auswählen

EntzBMP.Canvas.Pixels[Round(SkalB*((a[0]*i+a[1]*j+a[2])/(a[6]*i+a[7]*j+1))), Round(SkalH*((a[3]*i+a[4]*j+a[5])/(a[6]*i+a[7]*j+1)))]:= FrmBild.Image1.Picture.Bitmap.Canvas.Pixels[i,j];


Allerdings liegt dann über dem Zielbild ein schwarzer Schleier. Ich vermute, dass durch die Formel bzw. das Skalieren zwischen den Pixeln schwarze Lücken entstehen. Kann man die irgendwie mit "NearestNeighbour" oder ähnlich interpolieren?

Viele Grüße

Volker

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Bitmap Pixel Koordinaten ändern

Beitrag von ErnstVolker »

Hi,

die Entzerrung mache ich über ein Referenzviereck, dessen wahre Längen bekannt sind. Das Testbild habe ich im Anhang. Ich musste es für's hier hochladen etwas verkleinern. Theorie bzw. Formeln aus Kapitel 3. auf Seite 3 (angedruckt 58) des pdf, welches ich im Netz gefunden hatte.
Dateianhänge
VGI_199615_Dorffner.pdf
(1.14 MiB) 209-mal heruntergeladen
Testbild.jpg

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Bitmap Pixel Koordinaten ändern

Beitrag von wp_xyz »

Ich meine immer noch - und das zitierte Paper unterstützt mich darin (Seite 58 rechte Spalte) - dass links vom := Zeichen ganzzahlige i,j-Koordinaten stehen sollten - dadurch wird das Zielbild lückenlos abgetastet. Deine Formeln geben xWelt und yWelt als Funktion von u und v an - du willst aber xWelt und yWelt vorgehen (als lückenlose Koordinaten-Indices im Zielbild) und musst daher nach u und v (die Koordinaten im Quellbild) auflösen. Dadurch erhältst du andere a/b/c-Koeffizienten, die du genauso wie im ersten Post verwenden kannst.

Eine Bemerkung noch zu Round(): Dies wird eine stufige Interpolation ergeben, was - wenn wenig skaliert wird - kaum auffällt, aber wenn viel stark vergrößert/verkleinert wird, nicht gut aussehen wird. Daher musst du die interpolierten Koordinaten-Werte u und v als Double-Zahlen speichern. Dann nimmst du den ganzzahligen Anteil u1 = trunc(u) und v1 = trunc(v), sowie den nächsten ganzen Wert, u2 = u1+1, v2=v1+1, so dass du die vier Pixel bei (u1, v1), (u2, v1), (u1, v2) und (u2, v2) auslesen kannst, also die vier Nachbarn. Dann interpolierst du die RGB-Werte dieser vier Pixel entsprechend dem Gleitkomma-Anteil.

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Bitmap Pixel Koordinaten ändern

Beitrag von ErnstVolker »

Verstehe ich das richtig, dass ich den Gleichungslöser ein weiteres mal bemühen muß, jetzt nur mit vertauschtem Vektor der rechten Seite, um 8 Koeffizienten (ich sie mal "c") zu berechnen die aus xWelt und yWelt wieder Bildkoordinaten machen, die dann im Zielbild dargestellt werden?

Sprich so:

Code: Alles auswählen

xBild = (c0 * xWelt + c1 * yWelt + c2) / (c6 * xWelt+ c7 * yWelt +1) und yBild = (c3 * xWelt + c4 * yWelt + c5) / (c6 * xWelt + c7 * yWelt +1)

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Bitmap Pixel Koordinaten ändern

Beitrag von wp_xyz »

Wenn du dich nicht verrechnet hast, sind die anfangs genannten Gleichungen xWelt := ... und yWelt := ... Lösungen für das Entzerrungsproblem. Sie berechnen, ausgehend von einem Quellbildpunkt am Ort (u,v) die Koordinaten des Zielbildpunkts (xWelt, yWelt). Nun brauchst du es umgekehrt: gegeben sind (xWelt,yWelt) und du willst (u,v) haben. Also musst die diese Gleichungen so umformen, dass u und v auf der linken Seite stehen. Wenn die Gleichungen für den Fall allgemeiner Vierecke hergeleitet worden sind, dann kann man wahrscheinlich die Struktur der Gleichungen beibehalten und muss nur, so wie du gemacht hast, (u,v) und (xWelt, yWelt) austauschen und andere Koeffizienten nehmen. Aber wenn in der Herleitung eingegangen ist, dass z.B. das Zielviereck ein Rechteck mit einer Ecke im Ursprung ist, dann könnte ich mir vorstellen, dass diese Symmetrie nicht mehr gilt. In diesem Fall musst du das Gleichungsystem

Code: Alles auswählen

xWelt = (a0 * u + a1 * v + a2) / (a6 * u + a7 * v +1)
yWelt = (a3 * u + a4 * v + a5) / (a6 * u + a7 * v +1)
händisch nach u und v auflösen. Ist viel Schreibarbeit...

Ich würde zunächst mal die von dir vorgeschlagene Umformung ausprobieren, vielleicht bin ich ja mal wieder Reichsbedenkenträger und es stimmt ja doch so.

[EDIT]
Ich habe gerade die beiden Gleichungen nach v aufgelöst (u musst du selber machen) und folgendes erhalten:

Code: Alles auswählen

v = ((a3 - a5*a6) * xWelt + (a0 * a2 - a0) * yWelt + (a0 * a5 - a2 * a3)) / ((a4 * a6 - a3 * a7) * xWelt + (a0 * a7 - a1 * a6) * yWelt + (a1 * a3 - a0 * a4))

Das hat genau dieselbe Struktur wie deine Gleichung, wenn man folgende Ersetzungen macht

Code: Alles auswählen

c0 = a3 - a5*a6
c1 = a0*a2 - a0
c2 = a0*a5 - a2*a3
usw.

Dein Vorgehen im letzten Post ist also richtig (auch wenn ich es für u nicht nachgeprüft habe).

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Bitmap Pixel Koordinaten ändern

Beitrag von ErnstVolker »

Das Auflösen der Formeln nach u und v habe ich mit wxMaxima gemacht. Aber danke dafür. Und wenn ich die aufgelösten Formel in Excel eingebe, dann kommen auch bei Einsetzen der Weltkoordinaten rückgerechnet die Pixelkoordinaten des Ausgangsbildes heraus. Heute Morgen hatte ich mit Lazarus auf die Schnelle einen Lauf aber es kam nur ein schwarzes Bild heraus.

Ich habe das Verständnisproblem weshalb ich zuerst aus den Pixelkoordinaten (es sind ja nur Integerzahlen zwischen 0 und Breite bzw. 0 und Höhe) zuerst "Weltwerte" errechne um dann damit wieder "Pixelwerte" zu machen. Wenn ich in die Formeln des Ausgangspostes Pixelkoordinaten, z.B. der Eckpunkte "1" und "2" eingebe (Bild in Paint oder Irfanview laden und Koordinaten ablesen), dann rechnet mir Excel einerseits die Weltkoordinaten aus und über Pythagoras eine Länge von 210 mm. Das ist hinreichend genau.

Ich dachte ich nehme die Pixelwerte[ i, j ] des Ausgangsbildes und setze sie an die durch die Formeln (a0, a1...) korrigierten Pixelstellen im Zielbild. Was natürlich zur Folge hat, dass zwischen den Pixeln "Löcher" entstehen. Das funktioniert auch, nur leider ist das Bild dann zu klein. Optisch sieht es richtig aus, nur die Größe ist nicht groß genug. Nehme ich aber diese Bild und lese dessen Pixelkoordinaten aus, dann sind das ja die Weltkoordinaten und Excel rechnet zwischen zwei Punkten entsprechend den Abstand.
Um das Bild zu füllen bin ich auf die Idee mit dem Skalierfaktor gekommen. Der füllt dann zwar das Bild über die volle Breite und die volle Höhe, aber es bleiben immer noch Löcher. Und bei der Excel-Probe muß ich die abgelesenen Koordinatenwerte aus dem Zielbild erst durch die Skalierfaktoren von Breite und Höhe dividieren. Ansonsten kommt beim Pythagoras Unsinn heraus.
Lade ich allerdings das Bild in mein CAD-Programm und gebe Bildbreite und Bildhöhe in Weltabmessungen vor, dann kann ich mit den Bemaßungsfunktionen prima wahre Längen ausmessen.

Ich dachte es gäbe jetzt eine Stretch-Funktion mit der man ein Bild vergrößern kann und die automatisch zwischen Bildpunkten interpoliert. Ich wollte das interpolieren nicht selbst vornehmen. Bei meinen Programmierkünsten braucht der Algorithmus bestimmt ewig bis er mit der Berechnung durch ist.

Ich habe bereits eine Entzerrung gebastelt, die mit OpenCV funktioniert. Das rechnet flott, keine Frage. Aber es gibt zwei Probleme.

1.) Mein Kollege bekommt es nicht an's laufen, weil die OpenCV-DLL's wohl auf Redistributable Package von C++ 2015 angewiesen sind. Und man muß die DLL's mitgeben.
2.) Ich bekomme immer nur das Areal innerhalb des Referenzvierecks bzw. Referenzvielecks entzerrt dargestellt. Ich möchte aber z.B. das rote Quadrat als Referenz verwenden und trotzdem die wahren Längen der Außenkanten und der Diagonalen messen können.
Das ist übrigens auch jetzt noch ein Problem. Das Zielbild setzt seinen Ursprungspunkt auf die obere linke Ecke des roten Quadrates und es wird nur von da an der Bereich nach rechts und nach unten dargestellt. Ich will aber ALLES! ;-)

Benutzeravatar
af0815
Lazarusforum e. V.
Beiträge: 6198
Registriert: So 7. Jan 2007, 10:20
OS, Lazarus, FPC: FPC fixes Lazarus fixes per fpcupdeluxe (win,linux,raspi)
CPU-Target: 32Bit (64Bit)
Wohnort: Burgenland
Kontaktdaten:

Re: Bitmap Pixel Koordinaten ändern

Beitrag von af0815 »

ErnstVolker hat geschrieben:1.) Mein Kollege bekommt es nicht an's laufen, weil die OpenCV-DLL's wohl auf Redistributable Package von C++ 2015 angewiesen sind. Und man muß die DLL's mitgeben.

Für die C++ 2015 Sachen kannst du bei Micrsoft suchen, normalerweise gibt ein Redist-Package zum installieren in der Bitness das du untern Windows benötigst.
Bsp: https://www.microsoft.com/de-at/downloa ... x?id=48145
Blöd kann man ruhig sein, nur zu Helfen muss man sich wissen (oder nachsehen in LazInfos/LazSnippets).

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Bitmap Pixel Koordinaten ändern

Beitrag von wp_xyz »

Ich habe Probleme mit dem Begriff "Welt" in diesem Zusammenhang und würde lieber von Quellbild (enthält das verzerrte Object) und Zielbild (enthält das entzerrte Bild) sprechen. In meinen Postings habe ich gemeint, dass das Zielbild Pixel für Pixel durchlaufen werden muss (i, j). Für jedes Pixel (i,j) werden mit den "c" Formeln die Pixel-Koordinaten im Quellbild berechnet. Dabei werden die Integer-Werte nicht exact getroffen und man erhält floating point Werte. Für erste kann man natürlich mit Round() oder Trunc() ganze Zahlen daraus machen, so dass man an den entsprechenden, nun ganzzahligen Koordianten die Farbe im Quellbild auslesen und am Ort (i, j) im Zielbild eintragen kann).

Wenn das Zielbild größer ist als das Quellbild, werden dabei immer wieder Pixel des Quellbilds mehrfach verwendet werden, wodurch sich ein stufiges Aussehen ergibt. Besser wird es, wenn man die berechneten Floatingpoint-Werte als Grundlage nimmt, um zwischen benachbarten Pixeln zu interpolieren. Angenommen die Rechnung ergibt die Werte u=6.5/v=10.5. Das bedeutet, ein "imaginäres" Pixel in der Mitte des Blocks zwischen u = 6..7 und v = 10..11 auszulesen ist. Das erhält man wenn man die Farben der Pixel (6/10), (7/10), (6/11), (7/11) ausliest und den Mittelwert der RGB-Komponenten. Wenn das berechnete Pixel näher beim einem Eckpunkt zu liegen kommt, muss die Farbe dieses Eckpunkts stärker, die der anderen Eckpunkte weniger stark gewichtet werden.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Bitmap Pixel Koordinaten ändern

Beitrag von wp_xyz »

Ich hab's jetzt mal selbst probiert. Ja, die Idee mit den "c"-Gleichungen funktioniert. Siehe beigefügtes Demo-Projekt. Das in dem Screenshot weiter oben perspektivisch verzerrte Blatt wird auf ein normales Rechteck im Hochformat abgebildet und entzerrt. Zunächst muss man die Eckpunkte des Blattes im verzerrten Bild anklicken - die Koordinaten werden in dem Grid gesammelt. Mit jedem Klick springt das Programm automatisch in die nächste Zeile für den nächsten Eckpunkt. Die Punkte werden in der im Grid angegebenen Reihenfolge erwartet. "Links oben" bezieht sich dabei auf denjenigen Eckpunkt im Quellbild, der im Zielbild die linke obere Ecke darstellt. Die Punkte sind im Screenshot mit A, B, C und D bezeichnet. Das Zielbild entspricht etwa einem verkleinerten DIN A4-Blatt im Hochformat (für Querformat muss man die Koordinaten der Punkte PD vertauschen). Die Pixel für das Zielbild werden im Quellbild vereinfacht an den gerundeten Koordinaten abgelesen, und auch ein schneller Pixelzugriff mit Hilfe von LazIntfImage wurde nicht eingebaut, es ging mir nur ums Prinzip.
Dateianhänge
entzerrung.jpg
entzerrung.zip
(677.44 KiB) 201-mal heruntergeladen

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Bitmap Pixel Koordinaten ändern

Beitrag von ErnstVolker »

DANKE!!
"c" sind bei Dir die Lösungen des Gleichungssystems. Was bei mir "a" ist.

Das hier:

Code: Alles auswählen

v = ((a3 - a5*a6) * xWelt + (a0 * a2 - a0) * yWelt + (a0 * a5 - a2 * a3)) / ((a4 * a6 - a3 * a7) * xWelt + (a0 * a7 - a1 * a6) * yWelt + (a1 * a3 - a0 * a4))
hast Du jetzt aber nicht verwendet.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Bitmap Pixel Koordinaten ändern

Beitrag von wp_xyz »

In meiner Gleichung ist nach den Pixelkoordinaten im Quellbild aufgelöst, weil die Koordinaten im Zielbild in den Schleifen durchlaufen werden und somit vorgegeben sind. Ich meine, das ist deine Gleichung im Beitrag vom 27.9. 7:49, die mit den c-Koeffizienten. Aber wiegesagt, ich weiß nicht, was du mit Welt und Bild meinst.

Das Gleichungssystem wird einfacher in slegen anzuwenden, wenn man den Nenner auf die andere Seite bringt und die Terme nach c0, c1, ... c7 sortiert und die entsprechenen Koeffizienten in die Matrix des Gleichungssystems schreibt. Dabei bekommt jeder der bekannten Eckpunkte, die aufeinander abgebildet werden, zwei Zeilen, eine für X und eine für Y.

ErnstVolker
Beiträge: 326
Registriert: Di 17. Feb 2009, 10:44
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit

Re: Bitmap Pixel Koordinaten ändern

Beitrag von ErnstVolker »

Mein Vokabular ist unglücklich gewählt, stimmt. Quelle und Ziel sind besser.
Mit xWelt und yWelt meinte ich die Pixelkoordinaten des Zieles. "Welt" aus dem Grund, weil sie sich aus tatsächlichen Längen ergeben. Das was bei Dir "PD"="PointDestination" also Zielpunkt ist. In Deinem Beispiel die Außenmaße des DIN-A4-Blattes.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 1430
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Lazarus Fixes FPC Stable
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Bitmap Pixel Koordinaten ändern

Beitrag von fliegermichl »

Hallo ihr beiden,

Da ihr euch so gut mit Gleichungssystemen auskennt, könntet ihr mir das mal so erklären, daß das auch ein Nichtmathematiker verstehen kann?

Ich versuche mich schon geraume Zeit an der Berechnung von kubischen Splines mit wählbarer Tangente am Anfang und am Ende.
Ich hab auch einen funktionierenden Quellcode gefunden aber ich verstehe ihn nicht, weil mir der mathematische Hintergrund fehlt.
Beschreibungen auf Wikipedia usw. lassen mir nur noch mehr Fragezeichen im Hirn entstehen.

Vielen Dank
Michael

Antworten