-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #46: Import video_calibration apps from scikit-surgerycalibration.
- Loading branch information
1 parent
c040539
commit 1ee579d
Showing
8 changed files
with
373 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
46
sksurgeryutils/ui/sksurgeryvideocalibration_command_line.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
136
sksurgeryutils/ui/sksurgeryvideocalibrationchecker_app.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
49
sksurgeryutils/ui/sksurgeryvideocalibrationchecker_command_line.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
Oops, something went wrong.