import numpy as np
import cv2
import glob
import os
import json
from PIL import Image
[docs]class Cam:
"""
Class for the camera object
"""
def __init__(self, calib_file=None, picDir=None, firstPic=None, pic_to_cm=None,
framerate=None, camRes=None, res=None, cropsize=0, cam_thres=20.):
"""Camera object initialisation
:param calib_file: calibration file for the camera
:param picDir: shot pictures directory
:param firstPic: first picture index
:param pic_to_cm: pixel to cm ratio
:param framerate: camera framerate
:param camRes: camera resolution
:param res: picture resolution (W, H). Warning, it is the picture resolution written on each picture e.g. not accounting for the banner.
:param cropsize: size of the screen to crop (usefull when pictures information was written on each pictures)
:param cam_thres: shot detection trheshold
"""
if not(calib_file is None):
self.load_calibration_file(calib_file)
self.dir = picDir
self.firstPic = firstPic
self.pic_to_cm = pic_to_cm
self.framerate = framerate
self.cropSize = cropsize
self.res = res
self.camRes = camRes
if not(res is None) and not(camRes is None):
self.set_crop_size()
self.cam_thres = cam_thres
self.mask_w = 0
self.mask_h = 0
[docs] def undistort(self):
"""Undistort the camera pictures and change the picture file to the undistorted one"""
if "corrected" in self.dir.split("/"):
print("The camera was already calibrated.. exiting")
else:
images = glob.glob(self.dir+'/*.jpg')
if not("corrected" in os.listdir(self.dir)):
print('mkdir "'+self.dir+'/corrected"')
os.system('mkdir "'+self.dir+'/corrected"')
for elem in images:
img = cv2.imread(elem)
h, w = img.shape[:2]
newcameramtx, roi = cv2.getOptimalNewCameraMatrix(self.mtx, self.dist, (w, h), 0, (w, h))
dst = cv2.undistort(img, self.mtx, self.dist, None, newcameramtx)
dst = cv2.cvtColor(dst, cv2.COLOR_BGR2GRAY)
cv2.imwrite(self.dir+'/corrected/'+elem.split('\\')[1], dst)
self.dir = self.dir+"/corrected"
[docs] def set_mask(self, mask_w, mask_h):
"""Set black mask to apply on each picture to remove reflexive surfaces
:param mask_w, mask_h: mask width and height in pixels
"""
self.mask_w = mask_w
self.mask_h = mask_h
[docs] def set_mtx(self, mtx):
"""Set camera intrinsic matrix for a given path name (deprecated)
:param mtx: camera intrinsic matrix file name
"""
self.mtx = np.loadtxt(mtx)
[docs] def set_dist(self, dist):
"""Set camera distorsion matrix for a given path name (deprecated)
:param dist: camera distorsion matrix file name
"""
self.dist = np.loadtxt(dist)
[docs] def set_R(self, R):
"""Set camera rotation matrix for a given Rodrigues vector
:param R: camera rotation Rodrigues vector
"""
self.R = np.zeros((3, 3))
cv2.Rodrigues(R, self.R)
[docs] def set_R_by_matrix(self, R):
"""Set camera rotation matrix for a given matrix
:param R: camera rotation matrix
"""
self.R = R
[docs] def set_T(self, T):
"""Set camera Translation vector
:param T: camera Translation vector
"""
self.T = np.loadtxt(T)
[docs] def write_cam_data(self):
"""Writes all camera data into a formated tring
:return: String containing all relevant camera data
"""
out_str = ''
out_str += "Screen resolution:\n" + json.dumps(self.camRes) + '\n'
out_str += "Acquisition resolution:\n" + json.dumps(self.res) + '\n'
out_str += "Crop array:\n" + json.dumps(self.cropSize) + '\n'
out_str += "Intrisinc matrix:\n" + json.dumps(self.mtx.tolist())+'\n'
out_str += "Distorsion matrix:\n" + json.dumps(self.dist.tolist())+'\n'
out_str += "Rotation matrix:\n" + json.dumps(self.R.tolist()) + '\n'
out_str += "Translation vector:\n" + json.dumps(self.T.tolist()) + '\n'
out_str += "Picture directory: \n"+self.dir + '\n'
out_str += "First picture: \n"+str(self.firstPic) + '\n'
return out_str
[docs] def load_calibration_file(self, f_name):
"""Load camera intrinsic, distorsion and transformation matrices from a calibration file
:param f_name:calibration file path
"""
fichier = open(f_name)
lines = fichier.read().split('\n')
fichier.close()
self.mtx = np.matrix(json.loads(lines[1]))
self.dist = np.matrix(json.loads(lines[3]))
R = np.matrix(json.loads(lines[5]))
self.set_R(R)
self.T = np.array(json.loads(lines[7])[0])
[docs] def load_from_string(self, data):
"""Initialize a camera object from a formatted string such as produced by write_cam_data
:param data: formated string to parse
"""
lines = data.split('\n')
self.camRes = tuple(json.loads(lines[1]))
self.res = tuple(json.loads(lines[3]))
self.cropSize = json.loads(lines[5])
self.mtx = np.matrix(json.loads(lines[7]))
self.dist = np.matrix(json.loads(lines[9]))
self.R = np.matrix(json.loads(lines[11]))
self.T = np.matrix(json.loads(lines[13]))
self.dir = lines[15]
self.firstPic = int(lines[17])
[docs] def set_crop_size(self):
"""Set camera crop size to remove the banner according to the picture effective resolution and the picture target resolution (cam.res)"""
picList = glob.glob(self.dir + "/*.tif")
picList += glob.glob(self.dir + "/*.jpg")
if len(picList) == 0:
raise NameError("Empty camera picture file :"+self.dir)
else:
testpic = np.array(Image.open(picList[0]))
hor_crop = int(np.abs(testpic.shape[1] - self.res[0])/2.)
self.cropSize = [hor_crop, hor_crop, 0, testpic.shape[0] - self.res[1]]