Calibrage et utilisation de la caméra avec opencv-python [méthode chang]

Aperçu

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.

Structure des dossiers

\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

Calibrage de la caméra

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()

Utilisation des paramètres de la caméra et des paramètres de distorsion

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)

dépannage

ROI devient (0,0,0,0)

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

Calibrage et utilisation de la caméra avec opencv-python [méthode chang]
Utiliser le fichier d'étalonnage de la caméra avec OpenCvSharp4
Calibrage de la caméra
Supplément à "Calibration de la caméra" des didacticiels OpenCV-Python
Créez un capteur de couleur à l'aide d'une tarte à la râpe et d'une caméra