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.
\root
│ Calib.py //Ausführungsdatei
│
└─Image //Ordner zum Speichern von Schachbrettbilddateien
│ image1.jpg //Bilddatei
│ ...
└─dist //Ordner zur Bestätigung des Status der Eckenerkennung
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()
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)
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.