Lorsque je faisais des recherches sur l'étalonnage de la caméra, il y avait pas mal de sites avec du code source, mais il y avait beaucoup de codes désordonnés et il était difficile de comprendre ce qui fonctionne et comment cela fonctionne, alors je l'ai résumé.
Je ne suis pas un système de traitement d'image, donc je ne peux pas en dire beaucoup, mais j'aimerais le résumer pour ceux qui veulent quand même calibrer.
Je décrirai principalement les deux suivants.
\root
│ Calib.py //Dossier d'exécution
│
└─Image //Dossier pour stocker les fichiers d'image de l'échiquier
│ image1.jpg //Fichier d'image
│ ...
└─dist //Dossier de confirmation de l'état de détection d'angle
Le code source suivant est affiché. Si vous stockez le fichier image dans le dossier Image et l'exécutez, les résultats de l'étalonnage (mtx.csv et dist.csv) seront affichés dans le même dossier que Calib.py. La valeur d'évaluation est affichée sur le chemin (l'endroit appelé erreur totale), mais si la valeur est de 0,05 ou moins, c'est bon pour le moment.
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 "] #Format d'image
self.image = [] #Tampon d'image
self.imageSize = None #Taille de l'image
self.imagePath = Path(imagePath)#Chemin du fichier image
self.setConfig(cols, rows, squareSize)#Colonne x ligne
self.log.debug("initial Calb..")
def setConfig(self, cols, rows, squareSize):
self.log.debug("setConfig")
self.patternSize = (cols, rows)#Taille de l'image
self.patternPoints = np.zeros( (np.prod(self.patternSize), 3), np.float32 ) #Poitrine (X,Y,Z) Spécification des coordonnées(Z=0)
self.patternPoints[:,:2] = np.indices(self.patternSize).T.reshape(-1, 2)
self.patternPoints *= squareSize #La taille d'un côté du carré[cm]L'ensemble
self.criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 50, 0.0001)#Seuil de haute précision. Définissez la précision de la cible, combien de fois faire lorsque 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)) #Chargement d'image
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) #Correction de la précision de la position des coins.(11,11)Quelle est la taille de la fenêtre de recherche lors de l'amélioration de la précision?
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") #Matrice de caméra
np.savetxt("dist.csv", dist, delimiter=",", fmt="%0.14f") #Paramètre de distorsion
#Afficher le résultat du calcul
print("RMS:", ret)
print("mtx:", mtx)
print("dist:", dist)
#Évaluation par erreur de reprojection
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)) #Plus il est proche de 0, mieux c'est
return True
else:
self.log.debug("all image don't exist corner")
return False
if __name__ == "__main__":
path = "Image" #Dossier contenant les fichiers image de l'échiquier
#Intersection de la poitrine
rows = 10
cols = 7
size = 2.4 #Longueur du côté de la poitrine
calb = Calbration(path, cols=cols, rows=rows, squareSize=size)
calb.calbration()
Voici un exemple d'utilisation des paramètres de sortie. Vous pouvez modifier le programme ci-dessus pour afficher le tableau numpy, mais cette fois je l'ai fait lire le fichier csv de sortie.
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("Nom du fichier image à saisir"))
cv2.imwrite( "Nom du fichier de sortie", img)
Il n'y a pas beaucoup de matériaux japonais pour cela. Si vous comprenez le raisonnement, c'est peut-être vrai, mais j'expliquerai le raisonnement au minimum.
Dans les questions et réponses à l'étranger, il dit quelque chose comme «prenez une meilleure photo de l'échiquier». Pour le dire simplement, je l'interprète comme "** Prends un échiquier plus près, plus grand et dans un motif sans chevauchement **". Le fait que ** ne se chevauche pas ** est également très important, et même s'il se chevauche, (0,0,0,0) peut apparaître.
Recommended Posts