import numpy as np
import cv2
import os
import sys
from upsp.cam_cal_utils import img_utils
#---------------------------------------------------------------------------------------
# Generic Blob Detector Parameters
# Define the parameters for the Simple Blob Detectors for the target detection
# These need to be manually tuned per test
# Currently set up to have decent performance on both dots and kulites
params_all = cv2.SimpleBlobDetector_Params()
params_all.minThreshold = 0
params_all.maxThreshold = 225
# Cannot filter by these criteria
params_all.filterByCircularity = False
params_all.filterByInertia = False
# Filtering by area is tough, even for the larger dots since the aspect
# ratio can be very low. The smallest dot is only ~3px in area
# Additionally, the size parameter for blob detection seems to be screwy
params_all.filterByArea = False
# We should be able to filter by convexity well, but this may be an artifact
# of the assumption that the targets are perfect ellispes
params_all.filterByConvexity = True
params_all.minConvexity = 0.9
# We should be able to filter by color, but at this time it is not implemented
params_all.filterByColor = False
# Create the detector
detector_all = cv2.SimpleBlobDetector_create(params_all)
# Create the detectors
target_detectors = {'detector_all': detector_all}
[docs]def blob_func(detection_method, decide_method=None):
"""Returns a function that acts as a wrapper around openCV's blob detection function
to act as a target center localizer
Parameters
----------
detection_method : string
String must be a key in the global dict target_detectors. The key selects the
blob detection parameters to use
decide_method : {'biggest', 'smallest', 'first', 'last', 'random', None}, optional
decision method if more than 1 blob is detected in the image. Biggest and
smallest selects the largest and smallest (respectfully) based on the keypoint
size parameter. First and last select the first and last (respectfully) based on
the order returned by the blob detector. Random selects a random blob. None uses
the default method, which is last
Returns
-------
callable
Blob detector localization function - Wrapper around a blob detector to act as a
localization function. The function has the signature::
func(img, target_type=None, return_keypoint=False) -> keypoint
where ``img`` is a 2D image array containing one target, ``target_type`` does
nothing, and ``return_keypoint`` specifies whether the keypoint itself is
returned (``True``) or just the center position (x, y).
"""
if (detection_method in list(target_detectors.keys())):
detector = target_detectors[detection_method]
else:
print("Specified detector does not exist.")
print("detector must be one of the following:", *list(target_detectors.keys()))
quit()
# If no method was given, use 'last' method
if decide_method is None:
decide_method = 'last'
valid_methods = ['biggest', 'smallest', 'first', 'last', 'random']
# Decide method must be in the valid_methods list
assert (decide_method in valid_methods), "Error in blob_func. Input 'decide_method \
must be one of the following: 'biggest', 'smallest', 'first', 'last', \
or 'random'"
# Blob Localization Function
# return_keypoint is a flag to return the entire keypoint, and no confidence value
def fit_blob(img, target_type=None, return_keypoint=False):
"""Wrapper around a blob detector to act as a localization function
Parameters
----------
img : np.ndarray, 2D, uint8 or uint16
Image containing one target
target_type : optional, default=None
This input does nothing. Localizer functions need this input due to
stanardization of how localizer functions should be implemented for
uPSP to make them modular. However, the blob detector function does not
need this input to function.
return_keypoint : optional, boolean.
If True returns openCV keypoint object. If False, returns only estimated
target center location
Returns
----------
Tuple if return_keypoint is False, and keypoint is return_keypoint is True
Tuple's first index is the center. The center is a length 2 tuple of floats.
The first item of the center is the x coordiante and the second item is the
y coordinate
"""
# If it is not an 8 bit image, convert it to an 8 bit image to use
if (img.dtype != np.uint8):
img_8bit = img_utils.scale_image_max_inlier(img)
else:
img_8bit = img
# Detect the keypoints in the image
keypoints = detector.detect(img_8bit)
# If there is exactly one blob found, make best_keypoint as the only keypoint
if (len(keypoints) == 1):
best_keypoint = keypoints[0]
# If there were multiple found, use the decider method to get the best one
elif (len(keypoints) > 1):
# Select the biggest keypoint based on keypoint.size
if (decide_method == 'biggest'):
biggest_size = -np.inf
biggest_keypoint = None
for keypoint in keypoints:
if (keypoint.size > biggest_size):
biggest_size, biggest_keypoint = keypoint.size, keypoint
best_keypoint = biggest_keypoint
# Select the smallest keypoint based on keypoint.size
elif (decide_method == 'smallest'):
smallest_size = np.inf
smallest_keypoint = None
for keypoint in keypoints:
if (keypoint.size < smallest_size):
smallest_size, smallest_keypoint = keypoint.size, keypoint
best_keypoint = smallest_keypoint
# For first, chose the first keypoint in the list
elif (decide_method == 'first'):
best_keypoint = keypoints[0]
# For last, chose the last keypoint in the list
elif (decide_method == 'last'):
best_keypoint = keypoints[-1]
# Pick a random keypoint in the list
elif (decide_method == 'random'):
best_keypoint = np.random.choice(keypoints, 1)[0]
# If there is no Keypoint, it will return None
else:
best_keypoint = None
# If the return_keypoint flag is on, only return the keypoint, and
# return the whole keypoint
if return_keypoint:
return best_keypoint
# If a keypoint was not found, return (-1, -1) as the center
# Otherwise return the keypoint location
if best_keypoint is None:
center = (-1, -1)
conf = -1
else:
center = best_keypoint.pt
conf = best_keypoint.size
return (center,)
return fit_blob