Skip to content

Commit

Permalink
Issue #46: Import video_calibration apps from scikit-surgerycalibration.
Browse files Browse the repository at this point in the history
  • Loading branch information
MattClarkson committed Mar 18, 2022
1 parent c040539 commit 1ee579d
Show file tree
Hide file tree
Showing 8 changed files with 373 additions and 3 deletions.
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@
six>=1.10
numpy>=1.11
opencv-contrib-python-headless>=4.1.1.26
PySide2<5.15.0
scikit-surgerycore>=0.1.7
scikit-surgeryimage>=0.2.0
scikit-surgeryvtk>=0.19.1
scikit-surgeryarucotracker>=0.0.4
scikit-surgerycalibration>=0.2.0
PySide2<5.15.0
5 changes: 3 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
name='scikit-surgeryutils',
version=versioneer.get_version(),
cmdclass=versioneer.get_cmdclass(),
description='scikit-surgeryutils - Tests/demos utilities, based around opencv-contrib and PySide2',
description='scikit-surgeryutils - Tests/demos utilities, based around opencv-contrib-python-headless and PySide2',
long_description=long_description,
long_description_content_type='text/x-rst',
url='https://github.com/SciKit-Surgery/scikit-surgeryutils',
Expand Down Expand Up @@ -59,7 +59,6 @@
'scikit-surgeryimage>=0.2.0',
'scikit-surgeryvtk>=0.19.1',
'scikit-surgeryarucotracker>=0.0.4'

],

entry_points={
Expand All @@ -71,6 +70,8 @@
'sksurgeryreslice=sksurgeryutils.ui.sksurgeryreslice_command_line:main',
'sksurgerytextoverlay=sksurgeryutils.ui.sksurgerytextoverlay_command_line:main',
'sksurgerytransformpolydata=sksurgeryutils.ui.sksurgeryrendermodelslikecamera_command_line:main',
'sksurgeryvideocalibration=sksurgeryutils.ui.sksurgeryvideocalibration_command_line:main',
'sksurgeryvideocalibrationchecker=sksurgeryutils.ui.sksurgeryvideocalibrationchecker_command_line:main',
],
},
)
121 changes: 121 additions & 0 deletions sksurgeryutils/ui/sksurgeryvideocalibration_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# coding=utf-8

""" Functions to run video calibration. """

import os
import cv2
import sksurgeryimage.calibration.chessboard_point_detector as cpd
import sksurgerycalibration.video.video_calibration_driver_mono as mc

# pylint:disable=too-many-nested-blocks,too-many-branches


def run_video_calibration(configuration = None, save_dir = None, prefix = None):
"""
Performs Video Calibration using OpenCV
source and scikit-surgerycalibration.
Currently only chessboards are supported
:param config_file: location of a configuration file.
:param save_dir: optional directory name to dump calibrations to.
:param prefix: file name prefix when saving
:raises ValueError: if method is not supported
"""
if prefix is not None and save_dir is None:
save_dir = "./"

if configuration is None:
configuration = {}

# For now just doing chessboards.
# The underlying framework works for several point detectors,
# but each would have their own parameters etc.
method = configuration.get("method", "chessboard")
if method != "chessboard":
raise ValueError("Only chessboard calibration is currently supported")

source = configuration.get("source", 0)
corners = configuration.get("corners", [14, 10])
corners = (corners[0], corners[1])
size = configuration.get("square size in mm", 3)
window_size = configuration.get("window size", None)
min_num_views = configuration.get("minimum number of views", 5)
keypress_delay = configuration.get("keypress delay", 10)
interactive = configuration.get("interactive", True)
sample_frequency = configuration.get("sample frequency", 1)

cap = cv2.VideoCapture(source)
if not cap.isOpened():
raise RuntimeError("Failed to open camera.")

if window_size is not None:
cap.set(cv2.CAP_PROP_FRAME_WIDTH, window_size[0])
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, window_size[1])
print("Video feed set to ("
+ str(window_size[0]) + " x " + str(window_size[1]) + ")")
else:
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print("Video feed defaults to ("
+ str(width) + " x " + str(height) + ")")

detector = cpd.ChessboardPointDetector(corners, size)
calibrator = mc.MonoVideoCalibrationDriver(detector,
corners[0] * corners[1])

print("Press 'q' to quit and 'c' to capture an image.")
print("Minimum number of views to calibrate:" + str(min_num_views))

frames_sampled = 0
while True:
frame_ok, frame = cap.read()

key = None
frames_sampled += 1

if not frame_ok:
print("Reached end of video source or read failure.")
key = ord('q')
else:
if interactive:
cv2.imshow("live image", frame)
key = cv2.waitKey(keypress_delay)
else:
if frames_sampled % sample_frequency == 0:
key = ord('c')

if key == ord('q'):
break
if key == ord('c'):
number_points = calibrator.grab_data(frame)
if number_points > 0:
img_pts = calibrator.video_data.image_points_arrays[-1]
img = cv2.drawChessboardCorners(frame, corners,
img_pts,
number_points)
if interactive:
cv2.imshow("detected points", img)

number_of_views = calibrator.get_number_of_views()
print("Number of frames = " + str(number_of_views))

if number_of_views >= min_num_views:
proj_err, params = calibrator.calibrate()
print("Reprojection (2D) error is:" + str(proj_err))
print("Intrinsics are:")
print(params.camera_matrix)
print("Distortion matrix is:")
print(params.dist_coeffs)

if save_dir is not None:
if not os.path.isdir(save_dir):
os.makedirs(save_dir)

calibrator.save_data(save_dir, prefix)
calibrator.save_params(save_dir, prefix)
else:
print("Failed to detect points")

cap.release()
cv2.destroyAllWindows()
46 changes: 46 additions & 0 deletions sksurgeryutils/ui/sksurgeryvideocalibration_command_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# coding=utf-8

""" Command line processing for video calibration app. """

import argparse
from sksurgerycore.configuration.configuration_manager import \
ConfigurationManager
from sksurgerycalibration import __version__
from sksurgerycalibration.ui.video_calibration_app import run_video_calibration


def main(args=None):
"""Entry point for simple video calibration application. """

parser = argparse.ArgumentParser(description='videocalibration')

parser.add_argument("-c", "--config",
required=True,
type=str,
help="Configuration file containing the parameters "
"(see config/video_chessboard_conf.json "
"for example).")

parser.add_argument("-s", "--save",
required=False,
type=str,
help="Directory to save to.")

parser.add_argument("-p", "--prefix",
required=False,
type=str,
help="Filename prefix to save to.")

version_string = __version__
friendly_version_string = version_string if version_string else 'unknown'
parser.add_argument(
"-v", "--version",
action='version',
version='sksurgerycalibration version ' + friendly_version_string)

args = parser.parse_args(args)

configurer = ConfigurationManager(args.config)
configuration = configurer.get_copy()

run_video_calibration(configuration, args.save, args.prefix)
136 changes: 136 additions & 0 deletions sksurgeryutils/ui/sksurgeryvideocalibrationchecker_app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# coding=utf-8

""" Application to detect chessboards, and assess calibration accuracy. """

import numpy as np
import cv2
import sksurgeryimage.calibration.chessboard_point_detector as cpd
from sksurgerycalibration.video.video_calibration_params import \
MonoCalibrationParams

# pylint: disable=too-many-branches

def run_video_calibration_checker(configuration = None,
calib_dir = './', prefix = None):
"""
Application that detects a calibration pattern, runs
solvePnP, and prints information out to enable you to
check how accurate a calibration actually is.
:param config_file: location of configuration file.
:param calib_dir: the location of the calibration directory you want to
check
:param prefix: the file prefix for the calibration data you want to check
:raises ValueError: if no configuration provided.
:raises RuntimeError: if can't open source.
"""
if configuration is None:
raise ValueError("Calibration Checker requires a config file")

source = configuration.get("source", 0)
corners = configuration.get("corners", [14, 10])
corners = (corners[0], corners[1])
size = configuration.get("square size in mm", 3)
window_size = configuration.get("window size", None)
keypress_delay = configuration.get("keypress delay", 10)
interactive = configuration.get("interactive", True)
sample_frequency = configuration.get("sample frequency", 1)

existing_calibration = MonoCalibrationParams()
existing_calibration.load_data(calib_dir, prefix, halt_on_ioerror = False)
intrinsics = existing_calibration.camera_matrix
distortion = existing_calibration.dist_coeffs

cap = cv2.VideoCapture(source)
if not cap.isOpened():
raise RuntimeError("Failed to open camera:" + str(source))

if window_size is not None:
cap.set(cv2.CAP_PROP_FRAME_WIDTH, window_size[0])
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, window_size[1])
print("Video feed set to ("
+ str(window_size[0]) + " x " + str(window_size[1]) + ")")
else:
width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
print("Video feed defaults to ("
+ str(width) + " x " + str(height) + ")")

# For detecting the chessboard points
detector = cpd.ChessboardPointDetector(corners, size)
num_pts = corners[0] * corners[1]
captured_positions = np.zeros((0, 3))
frames_sampled = 0
while True:
frame_ok, frame = cap.read()

key = None
frames_sampled += 1

if not frame_ok:
print("Reached end of video source or read failure.")
key = ord('q')
else:
undistorted = cv2.undistort(frame, intrinsics, distortion)
if interactive:
cv2.imshow("live image", undistorted)
key = cv2.waitKey(keypress_delay)
else:
if frames_sampled % sample_frequency == 0:
key = ord('a')

if key == ord('q'):
break

image_points = np.array([])
if key in [ord('c'), ord('m'), ord('t'), ord('a')]:
_, object_points, image_points = \
detector.get_points(undistorted)

pnp_ok = False
img = None
tvec = None
if image_points.shape[0] > 0:
img = cv2.drawChessboardCorners(undistorted, corners,
image_points,
num_pts)
if interactive:
cv2.imshow("detected points", img)

pnp_ok, _, tvec = cv2.solvePnP(object_points,
image_points,
intrinsics,
None)
if pnp_ok:
captured_positions = np.append(captured_positions,
np.transpose(tvec),
axis=0)

if key in [ord('t'), ord('a')] and captured_positions.shape[0] > 1:
print(str(captured_positions[-1][0]
- captured_positions[-2][0]) + " "
+ str(captured_positions[-1][1]
- captured_positions[-2][1]) + " "
+ str(captured_positions[-1][2]
- captured_positions[-2][2]) + " ")
if key in [ord('m'), ord('a')] and \
captured_positions.shape[0] > 1:
print("Mean:"
+ str(np.mean(captured_positions, axis=0)))
print("StdDev:"
+ str(np.std(captured_positions, axis=0)))

if key in [ord('c'), ord('a')] and pnp_ok:
print("Pose" + str(tvec[0][0]) + " "
+ str(tvec[1][0]) + " "
+ str(tvec[2][0]))

if not pnp_ok and image_points.shape[0] > 0:
print("Failed to solve PnP")

if image_points.shape[0] == 0:
print("Failed to detect points")

cap.release()
cv2.destroyAllWindows()
49 changes: 49 additions & 0 deletions sksurgeryutils/ui/sksurgeryvideocalibrationchecker_command_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# coding=utf-8

""" CLI for sksurgerycalibration video calibration checker. """

import argparse

from sksurgerycore.configuration.configuration_manager import \
ConfigurationManager
from sksurgerycalibration import __version__
from sksurgerycalibration.ui.video_calibration_checker_app \
import run_video_calibration_checker


def main(args=None):

""" Entry point for bardVideoCalibrationChecker application. """

parser = argparse.ArgumentParser(
description='SciKit-Surgery Calibration '
'Video Calibration Checker')

parser.add_argument("-c", "--config",
required=True,
type=str,
help="Configuration file containing the parameters.")

parser.add_argument("-d", "--calib_dir",
required=True,
type=str,
help="Directory containing calibration data.")

parser.add_argument("-p", "--prefix",
required=True,
type=str,
help="Prefix for calibration data.")

version_string = __version__
friendly_version_string = version_string if version_string else 'unknown'
parser.add_argument(
"--version",
action='version',
version='scikit-surgerycalibration version ' + friendly_version_string)

args = parser.parse_args(args)

configurer = ConfigurationManager(args.config)
configuration = configurer.get_copy()

run_video_calibration_checker(configuration, args.calib_dir, args.prefix)
Loading

0 comments on commit 1ee579d

Please sign in to comment.