Bilder mit OpenCV entzerren

Ammar Ali 21 Juni 2023
Bilder mit OpenCV entzerren

In diesem Tutorial wird das Entzerren eines Bildes mit der Funktion undistort() von OpenCV erläutert.

Bilder mit OpenCV entzerren

Beim Aufnehmen von Bildern mit Kameras werden einige Bilder verzerrt. Die in den Bildern hinzugefügte Verzerrung ist hauptsächlich auf die Kamera zurückzuführen, mit der das Bild aufgenommen wurde.

Die in einem Bild vorhandene Verzerrung kann radial oder tangential sein. Bei radialer Verzerrung werden die geraden Linien in einem Bild gekrümmt, und die Linien werden stärker gekrümmt, wenn sie sich an den Ecken des Bildes befinden.

Wenn sich die Linien in der Mitte des Bildes befinden, ist ihre Verzerrung gering und kann nicht bemerkt werden. Siehe zum Beispiel das verzerrte Bild unten.

Schachbrett

Im obigen Bild sind die Kanten des Schachbretts mit einer geraden roten Linie verbunden, sodass wir die im Bild vorhandene Verzerrung leicht erkennen können. Wir können sehen, dass die Randlinien im obigen Bild gekrümmt sind, und je weiter die Linie von der Mitte des Bildes entfernt ist, desto gekrümmter werden sie.

Bei der tangentialen Verzerrung sind die Bildebene und das Objektiv, mit dem das Bild aufgenommen wurde, nicht parallel zueinander, und deshalb erscheinen einige im Bild vorhandene Bereiche weiter entfernt und einige Bereiche näher als im tatsächlichen Bild . Siehe zum Beispiel das Bild unten, das eine tangentiale Verzerrung zeigt.

tangentiale Verzerrung

Im obigen Bild zeigt das rote Gitter die Ausgangsbildebene und das blaue Gitter die Eingangsbildebene. Die tangentiale Verzerrung hängt vom Winkel des Objektivs ab, mit dem das Bild aufgenommen wurde.

Wenn sich der Objektivwinkel ändert, ändert sich die Verzerrung, und die Verzerrung nimmt zu, wenn der Objektivwinkel zunimmt. Wir müssen fünf Parameter finden, die als Verzerrungskoeffizienten bekannt sind, um ein Bild zu entzerren.

Wir müssen auch Informationen über die Kamera finden, mit der die Bilder aufgenommen wurden, wie die Brennweite des Objektivs und die optischen Zentren. Unter Verwendung der Werte für Brennweite und optisches Zentrum können wir eine Kameramatrix erstellen, die verwendet wird, um ein Bild zu entzerren.

Wir können eine Kameramatrix für andere Bilder wiederverwenden, wenn dieselbe Kamera sie aufnimmt, aber wenn sich die Kamera ändert, müssen wir die Matrix erneut finden, da sich die Werte für Brennweite und optisches Zentrum je nach Kamera ändern können. Die Kameramatrix ist eine 3-mal-3-Matrix wie die unten gezeigte Matrix.

Kameramatrix

In der obigen Kameramatrix enthalten die Variablen fx und fy die Brennweitenwerte und die Variablen cx und cy die Werte des optischen Zentrums des Objektivs, mit dem die Bilder aufgenommen wurden. Die Brennweite und die optischen Mittenwerte sind die intrinsischen Parameter einer Kamera.

Wir müssen auch die extrinsischen Parameter einer Kamera finden, die die Rotations- und Translationsvektoren zeigen, die verwendet werden, um einen Punkt im 3D-Koordinatensystem zu verschieben. Um den Verzerrungsparameter zu finden, müssen wir einige verzerrte Beispielbilder verwenden, und wir müssen einige spezifische Punkte, die in diesen Bildern vorhanden sind, und ihre Position verwenden.

Alle Musterbilder sollten von derselben Kamera aufgenommen werden, damit eine genaue Kameramatrix berechnet wird, und die verzerrten Bilder dieser Kamera sollten unter Verwendung der Kameramatrix unverzerrt sein. Nehmen wir zum Beispiel 10 Schachbrettbilder und extrahieren die realen 3D-Punkte und ihre 2D-Koordinaten, die die Position der 3D-Punkte enthalten.

Diese Punkte werden vom Schachbrett genommen, wo sich zwei schwarze Quadrate berühren. Wir werden diese Punkte verwenden, um die Kamera zu kalibrieren, mit der die Beispielbilder und das zu kalibrierende Bild aufgenommen wurden.

Ein realer 3D-Punkt enthält drei Werte x, y und z. Um das Verfahren zu vereinfachen, werden wir die Beispielbilder in einem festen Abstand von der Kamera aufnehmen, wobei der Wert z der 3D-Realwortpunkte eliminiert wird.

Jetzt übergeben wir die Position aller im Bild vorhandenen Punkte in den realen 3D-Punkten wie (0, 0), (1, 0) und so weiter. Diese Punkte repräsentieren die Position der Quadrate im Schachbrettbild.

Wenn wir die Größe der Felder im Schachbild kennen, können wir einige Punkte überspringen, anstatt alle Punkte hinzuzufügen. Wenn das Quadrat beispielsweise 30 Millimeter groß ist, können wir Punkte wie (0, 0), (30, 0) usw. hinzufügen.

Wisse, dass wir die Ecken der Felder auf dem Schachbrett finden müssen. Wir können cv2.findChessboardCorners() verwenden, um die Ecken der Quadrate zu finden.

Wir müssen das Muster, nach dem wir suchen, innerhalb der Funktion cv2.findChessboardCorners() wie ein 8-mal-8-Gitter übergeben. Ein Schachbrett hat normalerweise 8 mal 8 Felder, aber wir werden in diesem Beispiel ein 7 mal 6 Raster verwenden.

Die Funktion gibt die Eckpunkte zurück, die in einer Reihenfolge von links nach rechts und von oben nach unten platziert sind. Die Funktion gibt auch einen booleschen Wert zurück, der true ist, wenn das gesuchte Muster gefunden wird.

Die Beispielbilder, die wir verwenden möchten, enthalten möglicherweise nicht das erforderliche Muster, daher müssen wir die Bilder verwenden, die das erforderliche Muster enthalten, um die Eckpunkte zu finden.

Wir können die boolesche Ausgabe von cv2.findChessboardCorners() verwenden, um zu prüfen, ob das Muster erhalten wurde oder nicht, und wenn das Muster erhalten wird, speichern wir die Eckpunkte, und wenn sie nicht gefunden werden, speichern wir die Ecke nicht Punkte und gehen Sie zum nächsten Bild.

Überprüfen Sie diesen Link für weitere Details über die Funktion cv2.findChessboardCorners(). Um die Genauigkeit der Eckpunkte zu erhöhen, können wir die Funktion cv2.cornerSubPix() verwenden.

Überprüfen Sie diesen Link für weitere Details über die Funktion cv2.cornerSubPix(). Wir können auch die Funktion cv2.drawChessboardCorners() verwenden, um die Eckpunkte auf den Schachbrettbildern zu zeichnen.

Überprüfen Sie diesen Link für weitere Details über die Funktion cv2.drawChessboardCorners(). Lassen Sie uns zum Beispiel 10 Musterschachbrettbilder lesen, die realen 3D-Welt- und 2D-Bildpunkte finden und sie auf die Bilder zeichnen.

Siehe Code unten.

import numpy as np
import cv2

term_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
obj_points = np.zeros((6 * 7, 3), np.float32)
obj_points[:, :2] = np.mgrid[0:7, 0:6].T.reshape(-1, 2)
real_points = []
img_points = []
chess_images = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for name in chess_images:
    chess_img = cv2.imread(str(name) + ".png")
    chess_gray = cv2.cvtColor(chess_img, cv2.COLOR_BGR2GRAY)
    ret, corners = cv2.findChessboardCorners(chess_gray, (7, 6), None)

    if ret == True:
        real_points.append(obj_points)
        corners2 = cv2.cornerSubPix(
            chess_gray, corners, (11, 11), (-1, -1), term_criteria
        )
        img_points.append(corners)
        cv2.drawChessboardCorners(chess_img, (7, 6), corners2, ret)
        cv2.imshow("img", chess_img)
        cv2.waitKey(0)
cv2.destroyAllWindows()

Ausgang:

Eckpunkte

Im obigen Code haben wir die Funktion zeros() der Bibliothek numpy verwendet, um eine Matrix aus Nullen zu erstellen, und dann die Funktion mgrid() verwendet, um Werte in die Matrix einzufügen, die als 3D-Real verwendet werden -Weltpunkte.

Die Variablen real_points und img_points speichern die realen 3D-Punkte und die 2D-Bildpunkte aller Beispielbilder.

Wir haben eine for-Schleife verwendet, um jedes Bild separat zu verarbeiten. Wir lesen jedes Bild mit der Funktion imread() und konvertieren dann jedes Bild mit der Funktion cv2.cvtColor() in Graustufen.

Wir haben die if-Anweisung verwendet, um zu prüfen, ob das Muster erhalten wurde oder nicht, und wenn es erhalten wird, speichern wir die Punkte in den Variablen real_points und img_points.

Wir haben die Funktion cv2.cornerSubPix() verwendet, um die Genauigkeit der Eckpunkte zu erhöhen und die oben definierten Abbruchkriterien innerhalb der Funktion zusammen mit dem Graustufenbild und seinen Ecken zu übergeben, um die Genauigkeit der Eckpunkte zu erhöhen. Wir haben die Funktion append() verwendet, um die Eckpunkte zu den 2D-Bildpunkten hinzuzufügen.

Wir können die realen 3D-Punkte und die 2D-Bildpunkte verwenden, um die 5 Verzerrungsparameter mit der Funktion cv2.calibrateCamera() zu finden. Unter diesem Link finden Sie Details zur Funktion cv2.calibrateCamera().

Wir können zwei Parameter aus den 5 Verzerrungsparametern und der Eingabebildgröße verwenden, um die Kameramatrix mit der Funktion cv2.getOptimalNewCameraMatrix() zu finden.

Die Funktion cv2.getOptimalNewCameraMatrix() gibt auch den interessierenden Bereich zurück, der zum Zuschneiden des Bildes verwendet werden kann. Jetzt können wir die Verzerrungsparameter und die Kameramatrix verwenden, um ein Bild mit der Funktion cv2.undistort() von OpenCV zu entzerren.

Überprüfen Sie diesen Link für weitere Details über die Funktion cv2.getOptimalNewCameraMatrix(). Lesen wir zum Beispiel ein Bild aus den 10 verzerrten Beispielbildern und entzerren es mit der Funktion cv2.undistort().

Siehe Code unten.

import numpy as np
import cv2

term_criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 30, 0.001)
obj_points = np.zeros((6 * 7, 3), np.float32)
obj_points[:, :2] = np.mgrid[0:7, 0:6].T.reshape(-1, 2)
real_points = []
img_points = []
chess_images = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

for name in chess_images:
    chess_img = cv2.imread(str(name) + ".png")
    chess_gray = cv2.cvtColor(chess_img, cv2.COLOR_BGR2GRAY)
    ret, corners = cv2.findChessboardCorners(chess_gray, (7, 6), None)
    if ret == True:
        real_points.append(obj_points)
        corners2 = cv2.cornerSubPix(
            chess_gray, corners, (11, 11), (-1, -1), term_criteria
        )
        img_points.append(corners)

ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(
    real_points, img_points, chess_gray.shape[::-1], None, None
)
img = cv2.imread("5.png")
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(mtx, dist, (w, h), 1, (w, h))
dst = cv2.undistort(img, mtx, dist, None, newcameramtx)
x, y, w, h = roi
dst = dst[y : y + h, x : x + w]

cv2.imshow("Undistorted Image", dst)
cv2.imshow("distorted image", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Ausgang:

unverzerrtes Bild

In der obigen Ausgabe ist das linke Bild das verzerrte Bild und das rechte Bild das unverzerrte Bild. Im obigen Code haben wir die Funktion cv2.imshow() verwendet, um das Bild anzuzeigen und das unverzerrte Bild mit der Funktion cv2.imwrite() zu speichern.

Überprüfen Sie diesen Link für weitere Details über die Funktion cv2.undistort(). Im obigen Beispiel haben wir die quadratischen Gitter verwendet, um das Muster zu finden, aber wenn die Gitter kreisförmig sind, können wir die Funktion cv2.findCirclesGrid() verwenden, um das Muster zu finden.

Überprüfen Sie diesen Link für weitere Details über die Funktion cv2.findCirclesGrid().

Autor: Ammar Ali
Ammar Ali avatar Ammar Ali avatar

Hello! I am Ammar Ali, a programmer here to learn from experience, people, and docs, and create interesting and useful programming content. I mostly create content about Python, Matlab, and Microcontrollers like Arduino and PIC.

LinkedIn Facebook