Skip to content

Commit

Permalink
82 test dot patterns (#83)
Browse files Browse the repository at this point in the history
* Add example data from Mock OR

* Linting.

* Issue #82: Add adaptive thresholding and more constructor params.

* Issue #82: Fix unit tests. Minor difference in number of points detected, normally +/- 1, so ok.

* Issue #82: Linting.

* Issue #82: Only use 1 test image from mock OR, too much storage to test them all

* Fix typo in image path

* Linting

Co-authored-by: Matt Clarkson <m.clarkson@ucl.ac.uk>
  • Loading branch information
tdowrick and MattClarkson committed Oct 29, 2020
1 parent fd40a39 commit d86229b
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 26 deletions.
47 changes: 41 additions & 6 deletions sksurgeryimage/calibration/dotty_grid_point_detector.py
Expand Up @@ -4,6 +4,8 @@
Dotty Grid implementation of PointDetector.
"""

# pylint:disable=too-many-instance-attributes

import copy
import logging
import cv2
Expand All @@ -26,7 +28,12 @@ def __init__(self,
distortion_coefficients,
scale=(1, 1),
reference_image_size=None,
rms=30
rms=30,
gaussian_sigma=5,
threshold_window_size=151,
threshold_offset=20,
min_area=50,
max_area=50000
):
"""
Constructs a PointDetector that extracts a grid of dots,
Expand All @@ -46,6 +53,11 @@ def __init__(self,
:param scale: if you want to resize the image, specify scale factors
:param reference_image_size: used to warp undistorted image to reference
:param rms: max root mean square error when finding grid points
:param gaussian_sigma: sigma for Gaussian blurring
:param threshold_window_size: window size for adaptive thresholding
:param threshold_offset: offset for adaptive thresholding
:param min_area: minimum area when filtering by area
:param max_area: maximum area when filtering by area
"""
super(DottyGridPointDetector, self).__init__(scale=scale)

Expand All @@ -67,6 +79,11 @@ def __init__(self,
self.model_fiducials = self.model_points[self.list_of_indexes]
self.reference_image_size = reference_image_size
self.rms_tolerance = rms
self.gaussian_sigma = gaussian_sigma
self.threshold_window_size = threshold_window_size
self.threshold_offset = threshold_offset
self.min_area = min_area
self.max_area = max_area

def _internal_get_points(self, image):
"""
Expand All @@ -79,26 +96,44 @@ def _internal_get_points(self, image):
# pylint:disable=too-many-locals, invalid-name, too-many-branches
# pylint:disable=too-many-statements

smoothed = cv2.GaussianBlur(image, (5, 5), 0)
smoothed = cv2.GaussianBlur(image,
(self.gaussian_sigma, self.gaussian_sigma),
0)

thresholded = cv2.adaptiveThreshold(smoothed,
255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY,
self.threshold_window_size,
self.threshold_offset)

params = cv2.SimpleBlobDetector_Params()
params.filterByConvexity = False
params.filterByInertia = True
params.filterByCircularity = True
params.filterByArea = True
params.minArea = 50
params.maxArea = 50000
params.minArea = self.min_area
params.maxArea = self.max_area

# Detect points in the distorted image
detector = cv2.SimpleBlobDetector_create(params)
keypoints = detector.detect(smoothed)
keypoints = detector.detect(thresholded)

# Also detect them in an undistorted image.
undistorted_image = cv2.undistort(smoothed,
self.intrinsics,
self.distortion_coefficients
)
undistorted_keypoints = detector.detect(undistorted_image)

undistorted_thresholded = \
cv2.adaptiveThreshold(undistorted_image,
255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY,
self.threshold_window_size,
self.threshold_offset)

undistorted_keypoints = detector.detect(undistorted_thresholded)

# Note that keypoints and undistorted_keypoints
# can be of different length
Expand Down
24 changes: 19 additions & 5 deletions tests/calibration/test_dotty_grid_detector_metal.py
Expand Up @@ -6,6 +6,7 @@

import numpy as np
import pytest
import cv2
import tests.calibration.test_dotty_grid_utils as tdgu


Expand All @@ -20,6 +21,19 @@ def test_metal_1(setup_dotty_metal_model):
assert (224 == number_of_points)


def test_metal_1a(setup_dotty_metal_model_OR):
img_path = 'tests/data/calib-ucl-circles/snapshots-metal-1/mock_or/calib.left.images.1.png'
model_points = setup_dotty_metal_model_OR
number_of_points = tdgu.__check_real_OR_image(model_points,
img_path,
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
True
)

assert (326 == number_of_points)


def test_metal_2(setup_dotty_metal_model):
model_points = setup_dotty_metal_model
number_of_points = tdgu.__check_real_image(model_points,
Expand All @@ -39,7 +53,7 @@ def test_metal_3(setup_dotty_metal_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
True
)
assert (224 == number_of_points)
assert (223 == number_of_points)


def test_metal_4(setup_dotty_metal_model):
Expand Down Expand Up @@ -237,7 +251,7 @@ def test_metal_21(setup_dotty_metal_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
True
)
assert (223 == number_of_points)
assert (224 == number_of_points)


def test_metal_22(setup_dotty_metal_model):
Expand All @@ -248,7 +262,7 @@ def test_metal_22(setup_dotty_metal_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.right.distortion.txt',
True
)
assert (223 == number_of_points)
assert (224 == number_of_points)


def test_metal_23(setup_dotty_metal_model):
Expand Down Expand Up @@ -281,7 +295,7 @@ def test_metal_25(setup_dotty_metal_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
True
)
assert (223 == number_of_points)
assert (224 == number_of_points)


def test_metal_26(setup_dotty_metal_model):
Expand Down Expand Up @@ -336,4 +350,4 @@ def test_metal_30(setup_dotty_metal_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.right.distortion.txt',
True
)
assert (222 == number_of_points)
assert (224 == number_of_points)
26 changes: 13 additions & 13 deletions tests/calibration/test_dotty_grid_point_detector.py
Expand Up @@ -36,7 +36,7 @@ def test_dotty_uncalibrated_3(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/calib.left.distortion.txt',
)
assert(377 == number_of_points)
assert(376 == number_of_points)


def test_dotty_uncalibrated_4(setup_dotty_calibration_model):
Expand Down Expand Up @@ -96,7 +96,7 @@ def test_dotty_uncalibrated_9(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/calib.left.distortion.txt',
)
assert(408 == number_of_points)
assert(409 == number_of_points)


def test_dotty_uncalibrated_10(setup_dotty_calibration_model):
Expand All @@ -116,7 +116,7 @@ def test_dotty_uncalibrated_11(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/calib.left.distortion.txt',
)
assert(353 == number_of_points)
assert(352 == number_of_points)


def test_dotty_uncalibrated_12(setup_dotty_calibration_model):
Expand All @@ -136,7 +136,7 @@ def test_dotty_uncalibrated_13(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/calib.left.distortion.txt',
)
assert(362 == number_of_points)
assert(363 == number_of_points)


def test_dotty_uncalibrated_14(setup_dotty_calibration_model):
Expand Down Expand Up @@ -166,7 +166,7 @@ def test_dotty_uncalibrated_16(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/calib.right.intrinsics.txt',
'tests/data/calib-ucl-circles/calib.right.distortion.txt',
)
assert(367 == number_of_points)
assert(366 == number_of_points)


def test_dotty_uncalibrated_17(setup_dotty_calibration_model):
Expand Down Expand Up @@ -226,7 +226,7 @@ def test_calibration_1(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
)
assert(336 == number_of_points)
assert(335 == number_of_points)


def test_calibration_2(setup_dotty_calibration_model):
Expand All @@ -246,7 +246,7 @@ def test_calibration_3(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
)
assert(310 == number_of_points)
assert(309 == number_of_points)


def test_calibration_4(setup_dotty_calibration_model):
Expand Down Expand Up @@ -296,7 +296,7 @@ def test_calibration_8(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
)
assert(292 == number_of_points)
assert(293 == number_of_points)


def test_calibration_9(setup_dotty_calibration_model):
Expand Down Expand Up @@ -346,7 +346,7 @@ def test_calibration_13(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.right.intrinsics.txt',
'tests/data/calib-ucl-circles/10_54_44/viking.calib.right.distortion.txt',
)
assert (298 == number_of_points)
assert (299 == number_of_points)


def test_calibration_14(setup_dotty_calibration_model):
Expand All @@ -356,7 +356,7 @@ def test_calibration_14(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.right.intrinsics.txt',
'tests/data/calib-ucl-circles/10_54_44/viking.calib.right.distortion.txt',
)
assert (354 == number_of_points)
assert (355 == number_of_points)


def test_calibration_15(setup_dotty_calibration_model):
Expand Down Expand Up @@ -386,7 +386,7 @@ def test_calibration_17(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.right.intrinsics.txt',
'tests/data/calib-ucl-circles/10_54_44/viking.calib.right.distortion.txt',
)
assert (285 == number_of_points)
assert (286 == number_of_points)


def test_calibration_18(setup_dotty_calibration_model):
Expand Down Expand Up @@ -416,7 +416,7 @@ def test_calibration_20(setup_dotty_calibration_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.intrinsics.txt',
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
)
assert (291 == number_of_points)
assert (290 == number_of_points)


def test_calibration_21(setup_dotty_calibration_model):
Expand Down Expand Up @@ -459,7 +459,7 @@ def test_metal_3(setup_dotty_metal_model):
'tests/data/calib-ucl-circles/10_54_44/viking.calib.left.distortion.txt',
True
)
assert (224 == number_of_points)
assert (223 == number_of_points)


def test_metal_4(setup_dotty_metal_model):
Expand Down
42 changes: 41 additions & 1 deletion tests/calibration/test_dotty_grid_utils.py
Expand Up @@ -58,5 +58,45 @@ def __check_real_image(model_points,
model = detector.get_model_points()
assert model.shape[0] == model_points.shape[0]

return ids.shape[0]
return ids.shape[0] # , image_points

def __check_real_OR_image(model_points,
image_file_name,
intrinsics_file_name,
distortion_file_name,
is_metal=False
):
logging.basicConfig(level=logging.DEBUG)
image = cv2.imread(image_file_name)
intrinsics = np.loadtxt(intrinsics_file_name)
distortion = np.loadtxt(distortion_file_name)

size = (2600, 1900)
fiducials = [133, 141, 308, 316]
if is_metal:
fiducials = [133, 141, 308, 316]
size = (2600, 1900)

detector = DottyGridPointDetector(model_points,
fiducials,
intrinsics,
distortion,
reference_image_size=size
)

time_before = datetime.datetime.now()

ids, object_points, image_points = detector.get_points(image)

time_after = datetime.datetime.now()
time_diff = time_after - time_before

print("__check_real_image:time_diff=" + str(time_diff))

__write_annotated_image(image, ids, image_points, image_file_name)

model = detector.get_model_points()
assert model.shape[0] == model_points.shape[0]

return ids.shape[0] # , image_points

15 changes: 15 additions & 0 deletions tests/conftest.py
Expand Up @@ -38,6 +38,21 @@ def setup_dotty_metal_model():
counter = counter + 1
return model_points

@pytest.fixture(scope="function")
def setup_dotty_metal_model_OR():
number_of_points = 18 * 25
model_points = np.zeros((number_of_points, 6))
counter = 0
for y_index in range(18):
for x_index in range(25):
model_points[counter][0] = counter
model_points[counter][1] = (x_index + 1) * 20
model_points[counter][2] = (y_index + 1) * 20
model_points[counter][3] = x_index * 5
model_points[counter][4] = y_index * 5
model_points[counter][5] = 0
counter = counter + 1
return model_points

@pytest.fixture(scope="function")
def load_reference_charuco_chessboard_image():
Expand Down
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion tests/pylintrc
Expand Up @@ -65,7 +65,7 @@ confidence=
# --enable=similarities". If you want to run only the classes checker, but have
# no Warning level messages displayed, use"--disable=all --enable=classes
# --disable=W"
disable=len-as-condition,no-self-use,too-many-arguments,no-member,long-suffix,standarderror-builtin,indexing-exception,delslice-method,unichr-builtin,dict-view-method,parameter-unpacking,unicode-builtin,cmp-builtin,intern-builtin,round-builtin,backtick,nonzero-method,xrange-builtin,coerce-method,raw_input-builtin,old-division,filter-builtin-not-iterating,old-octal-literal,input-builtin,map-builtin-not-iterating,buffer-builtin,basestring-builtin,zip-builtin-not-iterating,using-cmp-argument,unpacking-in-except,old-raise-syntax,coerce-builtin,dict-iter-method,hex-method,range-builtin-not-iterating,useless-suppression,cmp-method,print-statement,reduce-builtin,file-builtin,long-builtin,getslice-method,execfile-builtin,no-absolute-import,metaclass-assignment,oct-method,reload-builtin,import-star-module-level,suppressed-message,apply-builtin,raising-string,next-method-called,setslice-method,old-ne-operator,arguments-differ,wildcard-import,locally-disabled
disable=super-with-arguments,len-as-condition,no-self-use,too-many-arguments,no-member,long-suffix,standarderror-builtin,indexing-exception,delslice-method,unichr-builtin,dict-view-method,parameter-unpacking,unicode-builtin,cmp-builtin,intern-builtin,round-builtin,backtick,nonzero-method,xrange-builtin,coerce-method,raw_input-builtin,old-division,filter-builtin-not-iterating,old-octal-literal,input-builtin,map-builtin-not-iterating,buffer-builtin,basestring-builtin,zip-builtin-not-iterating,using-cmp-argument,unpacking-in-except,old-raise-syntax,coerce-builtin,dict-iter-method,hex-method,range-builtin-not-iterating,useless-suppression,cmp-method,print-statement,reduce-builtin,file-builtin,long-builtin,getslice-method,execfile-builtin,no-absolute-import,metaclass-assignment,oct-method,reload-builtin,import-star-module-level,suppressed-message,apply-builtin,raising-string,next-method-called,setslice-method,old-ne-operator,arguments-differ,wildcard-import,locally-disabled


[REPORTS]
Expand Down

0 comments on commit d86229b

Please sign in to comment.