Datenspeicherung OpenCV Python

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
nikh22
User
Beiträge: 28
Registriert: Montag 27. Juni 2022, 15:46

Hallo,

aktuell befasse ich mich mit WetterPrädiktion über Bildverarbeitung in meinem Studium, dafür muss ich die aufgenommen Bilder einer Weitwinkelkamera entzerren. Meine einziges Problem aktuell ist, dass ich gerne die berechneten Matrizen und Vektoren zur Entzerrung gerne abspeichern würde, da diese sich nicht ändern und somit nicht immer wieder neu berechnet werden müssen. Logischer Ablauf ist also 1. Einlesen der Bilder 2. Kalibrierungsmatrix berechnen 3. Entzerren des Bildes 4. Ausgabe relativer Fehler .
Was ist denn der "Beste" bzw. einfachste Weg die Werte "cameraMatrix, dist, rvecs, tvecs" abzuspeichern, dass diese nicht verloren gehen aber bei Bedarf von der Kalbrierungsfunktion "camera_calibration" geändert werden könnten.Ich bin recht neu in Python selbst, habe vorher eher C++ gentuzt aber auch nicht immens viel.

Code: Alles auswählen

import numpy as np
import cv2 as cv
import glob

# FIND CHESSBOARD CORNERS - OBJECT POINTS AND IMAGE POINTS

chessboardSize = (9, 6)
frameSize = (2000, 1500)

# termination criteria
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)    # GENAUIGKEIT BEACHTEN

# prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
objp = np.zeros((chessboardSize[0] * chessboardSize[1], 3), np.float32)
objp[:, :2] = np.mgrid[0:chessboardSize[0], 0:chessboardSize[1]].T.reshape(-1, 2)

size_of_chessboard_squares_mm = 20
objp = objp * size_of_chessboard_squares_mm

# Arrays to store object points and image points from all the images.
objpoints = []  # 3d point in real world space
imgpoints = []  # 2d points in image plane.

images = glob.glob('*.jpg')                 # Kalibrierungsbilder müssen im selber Ordner liegen wie Programm

# [b]Calibration Variables             Globale Adressierung
	cameraMatrix = None
        dist = None
        rvecs = None
        tvecs = None[/b]

def read_in_for_calibration():
    for image in images:

        img = cv.imread(image)
        gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)

        # Find the chess board corners
        ret, corners = cv.findChessboardCorners(gray, chessboardSize, None)

        # If found, add object points, image points (after refining them)
        if ret == True:
            objpoints.append(objp)
            corners2 = cv.cornerSubPix(gray, corners, (11, 11), (-1, -1), criteria)
            imgpoints.append(corners)

            # Draw and display the corners
            # cv.drawChessboardCorners(img, chessboardSize, corners2, ret)
            # cv.imshow('img', img)
            # cv.waitKey(1000)

    cv.destroyAllWindows()


# CALIBRATION
[b]def camera_calibration():
    global cameraMatrix, dist, rvecs, tvecs

    ret, cameraMatrix, dist, rvecs, tvecs = cv.calibrateCamera(objpoints, imgpoints, frameSize, None, None)[/b]

    print("Camera Calibrated: ", ret)
    print("\nCamera Matrix:\n", cameraMatrix)
    print("\nDistortion Paramaters:\n", dist)
    print("\nRotation Vector: \n", rvecs)
    print("\nTranslation Vector: \n", tvecs)


def undistortion():
    # UNDISTORTION

    img = cv.imread('100GOPRO-GOPR0147.JPG')
    h, w = img.shape[:2]
    newCameraMatrix, roi = cv.getOptimalNewCameraMatrix(cameraMatrix, dist, (w, h), 1, (w, h))

    # Undistort
    dst = cv.undistort(img, cameraMatrix, dist, None, newCameraMatrix)

    # crop the image
    x, y, w, h = roi
    dst = dst[y:y + h, x:x + w]
    cv.imwrite('caliResult1.png', dst)


def reproject_error():
    # Reprojection Error
    mean_error = 0

    for i in range(len(objpoints)):
        imgpoints2, _ = cv.projectPoints(objpoints[i], rvecs[i], tvecs[i], cameraMatrix, dist)
        error = cv.norm(imgpoints[i], imgpoints2, cv.NORM_L2) / len(imgpoints2)
        mean_error += error

    print("total error: {}".format(mean_error / len(objpoints)))


read_in_for_calibration()
camera_calibration()
undistortion()
reproject_error()

Meine Idee war eine Klasse zu erstellen die als Eigenschaft die Werte cameraMatrix, dist, rvecs, tvecs besitzt.

Code: Alles auswählen

class DistortionValues():
    """Values for function 'Undistortion' to reduce calcuation time """

    def __init__(self, cameraMatrix, dist, rvecs,tvecs):
        self.cameraMatrix = None
        self.dist = None
        self.rvecs = None
        self.tvecs = None 
Allerdings weiß ich dann nicht genau wie ich die Funktion camera_calibration(): ändern muss dass diese Werte im Bedarfsfall geändert werden.
__deets__
User
Beiträge: 14494
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du solltest die Matrizen eigentlich mit dem pickle-Modul speichern koennen, und wieder laden. Alternativ (sind ja numpy) gibt es da wenn ich mich recht erinner auch eine Text-Repraesentation. Einfach mal schauen, wie man numpy Arrays speichert und laed, das ist bestimmt gut dokumentiert.

Und dann arbeitet man nicht mit globalen Variablen. Stattdessen kann man in Python wunderbar Argumente uebergeben an Funktionen, und auch (ggf mehrere) wieder zurueckgeben.
Sirius3
User
Beiträge: 17712
Registriert: Sonntag 21. Oktober 2012, 17:20

Konstanten werden nach Konvention KOMPLETT_GROSS geschrieben, also CHESSBOARD_SIZE, FRAME_SIZE und CRITERIA.
Variablennamen werden komplett klein geschrieben und sind nicht kryptisch, also object_points statt objp.
Man benutzt keine globalen Variablen `read_in_for_calibration` benötigt als Argumente images, und sollte selbst die Listen object_points und image_points generieren und zurückgeben und nicht irgendwo von draußen bekommen.
object_points ist eh keine sinnvolle Liste, wenn da nur etliche male das selbe Array drinsteht, das aber innerhalb der Funktion gar nicht benutzt wird.
Auf `True` wird nicht explizit geprüft.
Soll in image_points tatsächlich `corners` und nicht `corners2` gespeichert werden? Warum berechnest Du dann zweiteres?
Die Funktion sieht damit so aus:

Code: Alles auswählen

import numpy as np
import cv2 as cv
import glob

# FIND CHESSBOARD CORNERS - OBJECT POINTS AND IMAGE POINTS

CHESSBOARD_SIZE = (9, 6)
FAME_SIZE = (2000, 1500)
SIZE_OF_CHESSBOARD_SQUARES_MM = 20

# termination criteria
CRITERIA = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 30, 0.001)    # GENAUIGKEIT BEACHTEN

def read_images_for_calibration(image_filenames):
    image_points = []
    for filename in image_filenames:
        imgage = cv.imread(filename)
        gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)

        # Find the chess board corners
        ret, corners = cv.findChessboardCorners(gray, CHESSBOARD_SIZE, None)

        # If found, add object points, image points (after refining them)
        if ret:
            corners2 = cv.cornerSubPix(gray, corners, (11, 11), (-1, -1), CRITERIA)
            image_points.append(corners)
    cv.destroyAllWindows()
    return image_points
`camera_calibration` fehlen auch entsprechende Argumente.

Code: Alles auswählen

def camera_calibration(image_points, object_points):
    (   
        ret,
        camera_matrix,
        distortion_coefficients,
        rotation_vectors,
        translation_vectors
    ) = cv.calibrateCamera(
        [object_points] * len(image_points),
        image_points,
        FRAME_SIZE, None, None
    )
    return camera_matrix, distortion_coefficients, rotation_vectors, translation_vectors
In `reproject_error` wird über einen Index iteriert, was man aber in Python nicht macht.

Code: Alles auswählen

def undistortion(camera_matrix, distortion_coefficients):
    image = cv.imread('100GOPRO-GOPR0147.JPG')
    height, width = image.shape[:2]
    new_camera_matrix, roi = cv.getOptimalNewCameraMatrix(
        camera_matrix,
        distortion_coefficients, (width, height), 1, (width, height)
    )

    # Undistort
    dst = cv.undistort(image, camera_matrix, distortion_coefficients, None, new_camera_matrix)

    # crop the image
    x, y, w, h = roi
    dst = dst[y:y + h, x:x + w]
    cv.imwrite('caliResult1.png', dst)


def reproject_error(object_points, image_points, rotation_vectors, translation_vectors, camera_matrix, distortion_coefficients):
    # Reprojection Error
    mean_error = 0
    for image_point, rotation_vector, translation_vector, distortion_coefficient in zip(image_points, rotation_vectors, translation_vectors, distortion_coefficients):
        image_points2, _ = cv.projectPoints(object_points, rotation_vector, translation_vector, camera_matrix, distortion_coefficient)
        error = cv.norm(image_point, imgpoints2, cv.NORM_L2) / len(image_points2)
        mean_error += error

    print("total error: {}".format(mean_error / len(image_points)))


def main():
    # Kalibrierungsbilder müssen im selber Ordner liegen wie Programm
    image_filenames = glob.glob('*.jpg')
    image_points = read_in_for_calibration(image_filenames)

    # prepare object points, like (0,0,0), (1,0,0), (2,0,0) ....,(6,5,0)
    object_points = np.zeros((CHESSBOARD_SIZE[0] * CHESSBOARD_SIZE[1], 3), np.float32)
    object_points[:, :2] = np.mgrid[0:CHESSBOARD_SIZE[0], 0:CHESSBOARD_SIZE[1]].T.reshape(-1, 2)
    object_points *= SIZE_OF_CHESSBOARD_SQUARES_MM

    camera_matrix, distortion_coefficients, rotation_vectors, translation_vectors = camera_calibration(image_points, object_points)
    undistortion(camera_matrix, distortion_coefficients)
    reproject_error(object_points, image_points, rotation_vectors, translation_vectors, camera_matrix, distortion_coefficients)

if __name__ == "__main__":
    main()
nikh22
User
Beiträge: 28
Registriert: Montag 27. Juni 2022, 15:46

Vielen Dank euch beiden , vorallem an Sirius3,

Wahnsinnig wie ausführlich du mir geantwortet hast und vorallem wie schnell ! Da merk ich immer wieder, dass jemand mit richtig Ahnung sowas viel schneller hinbekommt.
Hab für den Code fast ne Woche gebraucht und dann innerhalb 2h einmal alles verbessert, unfassbar. Muss dazu aber auch sagen mein erstes großes Python Projekt und vorher auch noch nie OpenCv benutzt.
Werde mir die Tipps zur Notation in Zukunft zu Herzen nehmen, dann ist der Code auch verständlicher wenn man mal nach längerer Zeit nachsieht.
Die "corners2" waren anfangs dazu gedacht sie auf das noch nicht kalibrierte Bild zu werfen um zu sehen, ob sie überhaupt richtig erkannt wurden.
Da dies aber unnötige Rechenleistung meines Jestson braucht, hab ich die Anzeige der Bilder auskommentiert, allerdings die "corners2" vergessen.
Muss außerdem zugeben so ist das ganze Programm deutlich übersichtlicher und verstehe auch nun besser was ich eigentlich machen wollte. :lol:

Also Vielen Dank nochmal, endlich mal ein Forum wo sich Zeit genommen wird die Fragen der Leute zu beantworten.
Antworten