Kamerakalibrierung und Verwendung mit opencv-python [chang method]

Überblick

Als ich mich mit der Kamerakalibrierung befasste, gab es einige Websites mit Quellcode, aber es gab viele unordentliche Codes, und es war schwierig zu verstehen, was funktioniert und wie es funktioniert, also habe ich es zusammengefasst.

Ich bin kein Bildverarbeitungssystem, daher kann ich nicht viel sagen, aber ich möchte es für diejenigen zusammenfassen, die trotzdem kalibrieren möchten.

Ich werde hauptsächlich die folgenden zwei beschreiben.

Ordnerstruktur

\root
│  Calib.py //Ausführungsdatei
│
└─Image //Ordner zum Speichern von Schachbrettbilddateien
    │  image1.jpg //Bilddatei
    │  ...
    └─dist //Ordner zur Bestätigung des Status der Eckenerkennung

Kamerakalibrierung

Der folgende Quellcode wird angezeigt. Wenn Sie die Bilddatei im Bildordner speichern und ausführen, werden die Kalibrierungsergebnisse (mtx.csv und dist.csv) im selben Ordner wie Calib.py ausgegeben. Der Auswertungswert wird unterwegs angezeigt (der Ort, der als Gesamtfehler bezeichnet wird). Wenn der Wert jedoch 0,05 oder weniger beträgt, ist er vorerst gut.

Calib.py


# -*- coding: utf-8 -*-
import cv2
import numpy as np
import os
from pathlib import Path
import logging

logging.basicConfig(level=logging.DEBUG)

class Calbration():
    def __init__(self, imagePath="", cols=2, rows=2, squareSize=1.0):
        # log
        self.log = logging.getLogger("Calbration")
#        self.log.addHandler(logging.StreamHandler())
#        self.log.setLevel(logging.DEBUG)
        
        self.formats = ["*.jpg ", "*.png "] #Bildformat
        self.image = [] #Bildpuffer
        self.imageSize = None #Bildgröße
        self.imagePath = Path(imagePath)#Bilddateipfad
        self.setConfig(cols, rows, squareSize)#Spalte x Zeile
        
        self.log.debug("initial Calb..")
        
    def setConfig(self, cols, rows, squareSize):
        self.log.debug("setConfig")
        self.patternSize = (cols, rows)#Bildgröße
        
        self.patternPoints = np.zeros( (np.prod(self.patternSize), 3), np.float32 ) #Chestboard (X.,Y,Z) Festlegen von Koordinaten(Z=0)
        self.patternPoints[:,:2] = np.indices(self.patternSize).T.reshape(-1, 2)
        self.patternPoints *= squareSize #Die Größe einer Seite des Quadrats[cm]Der Satz
        
        self.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 50, 0.0001)#Schwelle für hohe Genauigkeit. Legen Sie die Zielgenauigkeit fest, wie oft bei CornerSubPix
        
    # image read from iamge folder
    def read(self):
        if( os.path.exists(self.imagePath) ):
            for fmt in self.formats:
                for path in self.imagePath.glob(fmt):
                    img = cv2.imread(str(path)) #Bild wird geladen
                    self.image.append([ path, img])
            
            if( len(self.image) > 0 ):
                self.log.debug("find image..." + str(len(self.image)))
                return True
            else:
                # error
                self.log.debug("Don't exist image file.")
                return False
        else:
            # error
            self.log.debug("Don't exist folder.")
            return False
        
    def calbration(self):
        
        if( not self.read()):# read image
            # error
            return False
        
        self.log.debug("corner finding start")
        imgPoints = []# corner buff
        objPoints = []# obj buff
        count = 0
        for img in self.image: 
            self.log.debug(str(img[0]) + " find...")
            
            gray = cv2.cvtColor(img[1], cv2.COLOR_BGR2GRAY)
            if(self.imageSize is None):
                self.imageSize = gray.shape[::-1]
            ret, corners = cv2.findChessboardCorners(gray, self.patternSize, None)
            if(ret):
                self.log.debug("detected corner")
                
                corners = cv2.cornerSubPix(gray,corners,(11,11),(-1,-1), self.criteria) #Korrektur der Eckpositionsgenauigkeit.(11,11)Wo ist die Größe des Suchfensters für eine höhere Genauigkeit?
                imgPoints.append(corners)
                objPoints.append(self.patternPoints)

                # debug draw
                distImg = cv2.drawChessboardCorners(img[1], self.patternSize, corners, ret)
                cv2.imwrite( str(self.imagePath) + "/dist/dist_" + str(img[0]).replace( str(self.imagePath) + "\\", ""), distImg)
                count += 1
            else:
                os.remove(str(img[0]))
                self.log.debug("don't detected corner")
            
        self.log.debug("detected image len is..." + str(count))
        
        if(len(imgPoints) > 0):
            ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objPoints, imgPoints, self.imageSize, None, None)
            np.savetxt("mtx.csv", mtx, delimiter=",", fmt="%0.14f") #Kameramatrix
            np.savetxt("dist.csv", dist, delimiter=",", fmt="%0.14f") #Verzerrungsparameter
            #Berechnungsergebnis anzeigen
            print("RMS:", ret)
            print("mtx:", mtx)
            print("dist:", dist)
            
            #Auswertung durch Reprojektionsfehler
            mean_error = 0
            for i in range(len(objPoints)):
                image_points2, _ = cv2.projectPoints(objPoints[i], rvecs[i], tvecs[i], mtx, dist)
                error = cv2.norm(imgPoints[i], image_points2, cv2.NORM_L2) / len(image_points2)
                mean_error += error
            print ("total error: ", mean_error/len(objPoints)) #Je näher es an 0 liegt, desto besser
            
            return True
        else:
            self.log.debug("all image don't exist corner")
            return False

if __name__ ==  "__main__":
    path = "Image" #Ordner mit Schachbrettbilddateien
  #Chestboard Kreuzung
    rows = 10
    cols = 7
  
    size = 2.4 #Chestboard Seitenlänge
    calb = Calbration(path, cols=cols, rows=rows, squareSize=size)
    calb.calbration()

Verwendung von Kameraparametern und Verzerrungsparametern

Das Folgende ist ein Beispiel für die Verwendung der Ausgabeparameter. Sie können das obige Programm ändern, um das numpy-Array auszugeben, aber dieses Mal habe ich es dazu gebracht, die Ausgabe-CSV-Datei zu lesen.

Correction.py


import cv2
import numpy as np

class Correction():
    def __init__(self, mtx, dist, formatType=None):
        
        if ( formatType == "csv" ):
            self.mtx = np.loadtxt(mtx, delimiter=",")
            self.dist= np.loadtxt(dist, delimiter=",")
        else:
            self.mtx = mtx
            self.dist= dist
    
    def __call__(self, image):
        cv2.CALIB_FIX_PRINCIPAL_POINT
#        print(image.shape)
        h, w = image.shape[:2]
        newcameramtx, roi=cv2.getOptimalNewCameraMatrix(self.mtx, self.dist, (w, h), 1, (w, h))
#        print(roi)
        
        # undistort
        dst = cv2.undistort(image, self.mtx, self.dist, None, newcameramtx)

        
        # crop the image
        x,y,w,h = roi
        dst = dst[y:y+h, x:x+w]
        
        return dst

if __name__ ==  "__main__":
    corr = Correction("mtx.csv", "dist.csv", formatType="csv")
    img = corr(cv2.imread("Name der einzugebenden Bilddatei"))
    cv2.imwrite( "Name der Ausgabedatei", img)

Fehlerbehebung

roi wird (0,0,0,0)

Dafür gibt es nicht viele japanische Materialien. Wenn Sie die Argumentation verstehen, mag es wahr sein, aber ich werde die Argumentation auf ein Minimum beschränken.

In Fragen und Antworten in Übersee sagt er so etwas wie "Mach ein besseres Bild vom Schachbrett". Um es einfach auszudrücken, ich interpretiere es als "** Nimm ein Schachbrett näher, größer und in einem nicht überlappenden Muster **". Die Tatsache, dass ** sich nicht überlappt **, ist ebenfalls sehr wichtig, und selbst wenn sie sich überlappen, kann (0,0,0,0) auftreten.

Recommended Posts

Kamerakalibrierung und Verwendung mit opencv-python [chang method]
Verwenden Sie die Kamerakalibrierungsdatei mit OpenCvSharp4
Kamerakalibrierung
Ergänzung zur "Kamerakalibrierung" von OpenCV-Python-Tutorials
Erstellen Sie einen Farbsensor mit einem Raspeltorte und einer Kamera