Module deepsport_utilities.ds.instants_dataset.views_transforms
Expand source code
import os
import random
import cv2
import numpy as np
import skimage.draw
from calib3d import Point3D, Point2D
from deepsport_utilities.court import Court, BALL_DIAMETER
from deepsport_utilities.transforms import Transform, RandomCropperTransform
from deepsport_utilities.utils import gamma_correction, setdefaultattr
from deepsport_utilities.ds.instants_dataset import View, ViewKey, Ball, BallState
class AddBallAnnotation(Transform):
def __call__(self, key, view):
balls = [a for a in view.annotations if a.type == 'ball']
assert len(balls) == 1, f"Expected one ball. received {len(balls)}."
view.ball = balls[0]
return view
class UndistortTransform(Transform):
def __call__(self, key, view):
all_images = []
for image in view.all_images:
all_images.append(cv2.undistort(image, view.calib.K, view.calib.kc))
view.calib = view.calib.update(kc=np.array([0,0,0,0,0]))
return view
class ComputeDiff(Transform):
def __init__(self, squash=False, inplace=False):
self.squash = squash
self.inplace = inplace
def __call__(self, view_key: ViewKey, view: View):
diff = np.abs(view.image.astype(np.int32) - view.all_images[1].astype(np.int32)).astype(np.uint8)
if self.squash:
diff = np.mean(diff, axis=2).astype(np.uint8)
if self.inplace:
view.image = np.dstack((view.image, diff))
else:
view.diff = diff
return view
class GameGammaColorTransform(Transform):
def __init__(self, transform_dict):
assert all([isinstance(k, int) for k in transform_dict.keys()])
#29582 : [1.04, 1.02, 0.93],
#24651 : [1.05, 1.02, 0.92],
#30046 : [1.01, 1.01, 1.01]
self.transform_dict = transform_dict
def __call__(self, view_key, view):
if view_key.instant_key.game_id in self.transform_dict.keys():
gammas = np.array(self.transform_dict[view_key.instant_key.game_id])
view.image = gamma_correction(view.image, gammas)
return view
class BayeringTransform(Transform):
def __init__(self):
self.R_filter = np.array([[1,0],[0,0]])
self.G_filter = np.array([[0,1],[1,0]])
self.B_filter = np.array([[0,0],[0,1]])
def __call__(self, view_key, view):
height, width, _ = view.image.shape
R_mask = np.tile(self.R_filter, [height//2, width//2])
G_mask = np.tile(self.G_filter, [height//2, width//2])
B_mask = np.tile(self.B_filter, [height//2, width//2])
mask = np.stack((R_mask, G_mask, B_mask), axis=2)
mask = mask[np.newaxis]
for i, image in enumerate(view.all_images):
view.all_images[i] = np.sum(image*mask, axis=3)
view.image = view.all_images[0]
class GameRGBColorTransform(Transform):
def __init__(self, transform_dict):
assert all([isinstance(k, int) for k in transform_dict.keys()])
self.transform_dict = transform_dict
def __call__(self, view_key: ViewKey, view: View):
if view_key.instant_key.game_id in self.transform_dict.keys():
adaptation_vector = np.array(self.transform_dict[view_key.instant_key.game_id])
view.image = np.clip(view.image.astype(np.float32)*adaptation_vector, 0, 255).astype(np.uint8)
return view
class ViewRandomCropperTransform(RandomCropperTransform):
def _apply_transformation(self, view, A):
if self.debug:
w, h = self.output_shape
points = Point2D(np.linalg.inv(A)@Point2D([0,0,w,w],[0,h,h,0]).H)
cv2.polylines(view.image, [points.T.astype(np.int32)], True, (255,0,0), self.linewidth)
else:
view.image = cv2.warpAffine(view.image, A[0:2,:], self.output_shape, flags=cv2.INTER_LINEAR)
view.calib = view.calib.update(K=A@view.calib.K, width=self.output_shape[0], height=self.output_shape[1])
if hasattr(view, "all_images"):
for i in range(1, len(view.all_images)): # skip first image as it was already done
view.all_images[i] = cv2.warpAffine(view.all_images[i], A[0:2,:], self.output_shape, flags=cv2.INTER_LINEAR)
if hasattr(view, "human_masks") and view.human_masks is not None:
view.human_masks = cv2.warpAffine(view.human_masks, A[0:2,:], self.output_shape, flags=cv2.INTER_NEAREST)
return view
class NaiveViewRandomCropperTransform(ViewRandomCropperTransform):
def __init__(self, *args, scale_min=0.5, scale_max=2, **kwargs):
""" scale is the scale factor that will be applied to images
"""
super().__init__(*args, size_min=scale_min, size_max=scale_max, **kwargs)
def _get_current_parameters(self, view_key, view):
input_shape = view.calib.width, view.calib.height
return None, 1, input_shape
class CleverViewRandomCropperTransform(ViewRandomCropperTransform):
def __init__(self, *args, def_min=60, def_max=160, **kwargs):
"""
def - definition in pixels per meters. 60px/m = ball of 14px
"""
super().__init__(*args, size_min=def_min, size_max=def_max, **kwargs)
def random_ball(self, view_key, view):
court = setdefaultattr(view, "court", Court(getattr(view, "rule_type", "FIBA")))
#court_polygon = view.calib.get_region_visible_corners_2d(court.corners, 1)
top_edge = list(court.visible_edges(view.calib))[0]
start = top_edge[0][0][0]
stop = top_edge[1][0][0]
x = np.random.beta(2, 2)*(stop-start)+start
y = np.random.beta(2, 2)*court.h/2+court.h/4
z = 0
return Point3D(x,y,z)
def _get_current_parameters(self, view_key, view):
keypoints = self.random_ball(view_key, view)
size = float(view.calib.compute_length2D(keypoints, 100))
keypoints = view.calib.project_3D_to_2D(keypoints)
input_shape = view.calib.width, view.calib.height
if self.debug:
raise NotImplementedError("Selected keypoint should be drawn in view.image (as well as it's projection on court for better visualization)")
return keypoints, size, input_shape
class BallViewRandomCropperTransform(CleverViewRandomCropperTransform):
def __init__(self, *args, size_min=None, size_max=None, def_min=None, def_max=None, on_ball=False, **kwargs):
msg = "Only one of ('size_min' and 'size_max') or ('def_min' and 'def_max') should be defined"
if size_min is not None and size_max is not None:
assert def_min is None and def_max is None, msg
super().__init__(*args, def_min=size_min, def_max=size_max, **kwargs)
self.true_size = BALL_DIAMETER
elif def_min is not None and def_max is not None:
assert size_min is None and size_max is None, msg
super().__init__(*args, def_min=def_min, def_max=def_max, **kwargs)
self.true_size = 100
self.on_ball = {False: 0.5, True: 1.0, None: 0.0}.get(on_ball, on_ball)
def _get_current_parameters(self, view_key, view):
balls = [a for a in view.annotations if a.type == "ball" and a.camera == view_key.camera]
ball = random.sample(balls, 1)[0].center if balls else None
# If not `on_ball` use the ball anyway half of the samples
if random.random() < self.on_ball and ball is not None:
keypoint = ball
view.ball = ball
else:
keypoint = self.random_ball(view_key, view)
view.ball = Ball({
'center': keypoint,
'origin': "random",
'image': view_key.camera,
'visible': False,
})
# Use ball if any, else use the random ball (it only affects the strategy to scale)
size = float(view.calib.compute_length2D(ball if ball is not None else keypoint, self.true_size))
keypoints = view.calib.project_3D_to_2D(keypoint)
input_shape = view.calib.width, view.calib.height
return keypoints, size, input_shape
class BallCropperTransform(ViewRandomCropperTransform):
""" Create a random scaled thumbnail around view's ball.
- If [min_size, max_size] range is given, scale factor is chosen s.t.
ball size is in the [min_size, max_size] range.
- If [def_min, def_max] range is given, scale factor is chosen s.t.
image definition [px/m] is in the [def_min, def_max] range at ball
location.
- If [scale_min, scale_max] range is given, scale factor is chosen in
that range.
An error is raised if multiple options are given.
"""
def __init__(self, *args,
def_min=None, def_max=None,
size_min=None, size_max=None,
scale_min=None, scale_max=None,
**kwargs):
msg = "Only one of ('size_min' and 'size_max') or ('def_min' and 'def_max') or ('scale_min' and 'scale_max') should be defined"
if size_min is not None and size_max is not None:
assert all([x is None for x in [def_min, def_max, scale_min, scale_max]]), msg
super().__init__(*args, size_min=size_min, size_max=size_max, **kwargs)
self.true_size = BALL_DIAMETER
elif def_min is not None and def_max is not None:
assert all([x is None for x in [size_min, size_max, scale_min, scale_max]]), msg
super().__init__(*args, size_min=def_min, size_max=def_max, **kwargs)
self.true_size = 100
elif scale_min is not None and scale_max is not None:
assert all([x is None for x in [size_min, size_max, def_min, def_max]]), msg
super().__init__(*args, size_min=scale_min, size_max=scale_max, **kwargs)
self.true_size = None
else:
raise ValueError(msg)
def _get_current_parameters(self, view_key, view):
keypoints = view.calib.project_3D_to_2D(view.ball.center)
if self.true_size is None:
size = 1
else:
size = float(view.calib.compute_length2D(view.ball.center, self.true_size))
input_shape = view.calib.width, view.calib.height
return keypoints, size, input_shape
class PlayerViewRandomCropperTransform(ViewRandomCropperTransform):
def __init__(self, output_shape, def_min=60, def_max=160, margin=100, **kwargs):
"""
def - definition in pixels per meters. 60px/m = ball of 14px
margin - a margin in cm the keypoints
"""
super().__init__(output_shape=output_shape, size_min=def_min, size_max=def_max, **kwargs)
self.margin = margin
def focus_on_player(self, view_key, view):
players = [a for a in view.annotations if a.type == "player" and a.camera == view_key.camera]
if not players:
return None
player = random.sample(players, 1)[0]
keypoints = Point3D([player.head, player.hips, player.foot1, player.foot2])
return keypoints
def _get_current_parameters(self, view_key, view):
raise NotImplementedError("This code was not tested. Images should be visualized.")
keypoints = self.focus_on_player(view_key, view)
if keypoints is None:
return None
margin = float(view.calib.compute_length2D(Point3D(np.mean(keypoints, axis=1)), self.margin))
size = float(view.calib.compute_length2D(Point3D(np.mean(keypoints, axis=1)), 100))
keypoints = view.calib.project_3D_to_2D(keypoints)
input_shape = view.calib.width, view.calib.height
return keypoints, size, input_shape
class AddBallSizeFactory(Transform):
def __call__(self, view_key, view):
predicate = lambda a: a.camera == view_key.camera and a.type == "ball" and view.calib.projects_in(a.center) and a.visible is not False
balls = [a.center for a in view.annotations if predicate(a)]
return {"ball_size": view.calib.compute_length2D(Point3D(balls), BALL_DIAMETER)[0] if balls else np.nan} # takes the first ball by convention
class AddBallStateFactory(Transform):
def __call__(self, view_key, view):
predicate = lambda a: a.camera == view_key.camera and a.type == "ball" and view.calib.projects_in(a.center) and a.visible is not False
balls = [a for a in view.annotations if predicate(a)]
return {"ball_state": balls[0].state if balls and balls[0].state is not None else BallState.NONE} # takes the first ball by convention
class AddBallPositionFactory(Transform):
def __call__(self, view_key, view):
return {"ball": view.ball.center}
class AddBallVisibilityFactory(Transform):
def __call__(self, view_key, view):
return {"ball_visible": view.ball.visible}
class AddDiffFactory(Transform):
def __call__(self, view_key, view):
raise NotImplementedError() # code needs to be re-implemented: current implementation only adds next image
return {"input_image2": view.all_images[1]}
class AddNextImageFactory(Transform):
def __call__(self, view_key, view):
return {"input_image2": view.all_images[1]}
class AddCalibFactory(Transform):
def __init__(self, as_dict=False):
self.as_dict = as_dict
@staticmethod
def to_basic_dict(calib):
return {
"K": calib.K,
"r": cv2.Rodrigues(calib.R)[0].flatten(),
"T": calib.T,
"width": np.array([calib.width]),
"height": np.array([calib.height]),
"kc": np.array(calib.kc),
}
def __call__(self, view_key, view):
if self.as_dict:
return self.to_basic_dict(view.calib)
return {"calib": view.calib}
class AddCourtFactory(Transform):
def __call__(self, view_key, view):
if not getattr(view, "court", None):
view.court = Court()
return {
"court_width": np.array([view.court.w]),
"court_height": np.array([view.court.h])
}
class AddImageFactory(Transform):
def __call__(self, view_key, view):
return {"input_image": view.image}
class AddHumansSegmentationTargetViewFactory(Transform):
def __call__(self, view_key, view):
return {"human_masks": view.human_masks}
class AddBallSegmentationTargetViewFactory(Transform):
def __call__(self, view_key, view):
calib = view.calib
target = np.zeros((calib.height, calib.width), dtype=np.uint8)
for ball in [a for a in view.annotations if a.type == "ball" and calib.projects_in(a.center) and a.visible]:
diameter = calib.compute_length2D(ball.center, BALL_DIAMETER)
center = calib.project_3D_to_2D(ball.center)
#cv2.circle(target, center.to_int_tuple(), radius=int(diameter/2), color=1, thickness=-1)
target[skimage.draw.disk(center.flatten()[::-1], radius=float(diameter/2), shape=target.shape)] = 1
return {
"target": target
}
try:
from calib3d.pycuda import CudaCalib
import pycuda.driver as cuda
# pylint: disable=unused-import
import pycuda.autoinit
# pylint: enable=unused-import
from pycuda.compiler import SourceModule
class AddBallDistance(Transform):
def __init__(self):
self._calib_struct_ptr = cuda.mem_alloc(CudaCalib.memsize())
self._ball_ptr = cuda.mem_alloc(3*8)
cuda_code = open(os.path.join(os.path.dirname(__file__), "mod_source.c"), "r").read()
mod = SourceModule(str(CudaCalib.struct_str())+cuda_code)
self._ball_distance = mod.get_function("BallDistance")
self._bdim = (32,32,1)
def __repr__(self):
return "{}()".format(self.__class__.__name__)
def __call__(self, key, view: View):
# copy calib to GPU
calib = CudaCalib.from_calib(view.calib)
calib.memset(self._calib_struct_ptr, cuda.memcpy_htod)
# copy ball position to GPU
cuda.memcpy_htod(self._ball_ptr, memoryview(view.ball.center))
# create distmap on GPU
distmap_gpu = cuda.mem_alloc(calib.img_width * calib.img_height * 8)# 8 bytes per double
cuda.memset_d8(distmap_gpu, 0, calib.img_width * calib.img_height * 8)
# compute best block and grid dimensions
dx, mx = divmod(calib.img_width, self._bdim[0])
dy, my = divmod(calib.img_height, self._bdim[1])
gdim = ( (dx + (mx>0)) * self._bdim[0], (dy + (my>0)) * self._bdim[1])
# call gpu function
self._ball_distance(distmap_gpu, self._calib_struct_ptr, self._ball_ptr, block=self._bdim, grid=gdim)
# copy result to memory
view.ball_distance = np.zeros((calib.img_height,calib.img_width))#, np.int8)
cuda.memcpy_dtoh(view.ball_distance, distmap_gpu)
# cuda.Context.synchronize()
return view
except ModuleNotFoundError as e:
if e.name == "calib3d.pycuda":
raise e
except ImportError as e:
if "CudaCalib" not in str(e.msg):
raise e
Classes
class AddBallAnnotation
-
Expand source code
class AddBallAnnotation(Transform): def __call__(self, key, view): balls = [a for a in view.annotations if a.type == 'ball'] assert len(balls) == 1, f"Expected one ball. received {len(balls)}." view.ball = balls[0] return view
Ancestors
class AddBallPositionFactory
-
Expand source code
class AddBallPositionFactory(Transform): def __call__(self, view_key, view): return {"ball": view.ball.center}
Ancestors
class AddBallSegmentationTargetViewFactory
-
Expand source code
class AddBallSegmentationTargetViewFactory(Transform): def __call__(self, view_key, view): calib = view.calib target = np.zeros((calib.height, calib.width), dtype=np.uint8) for ball in [a for a in view.annotations if a.type == "ball" and calib.projects_in(a.center) and a.visible]: diameter = calib.compute_length2D(ball.center, BALL_DIAMETER) center = calib.project_3D_to_2D(ball.center) #cv2.circle(target, center.to_int_tuple(), radius=int(diameter/2), color=1, thickness=-1) target[skimage.draw.disk(center.flatten()[::-1], radius=float(diameter/2), shape=target.shape)] = 1 return { "target": target }
Ancestors
class AddBallSizeFactory
-
Expand source code
class AddBallSizeFactory(Transform): def __call__(self, view_key, view): predicate = lambda a: a.camera == view_key.camera and a.type == "ball" and view.calib.projects_in(a.center) and a.visible is not False balls = [a.center for a in view.annotations if predicate(a)] return {"ball_size": view.calib.compute_length2D(Point3D(balls), BALL_DIAMETER)[0] if balls else np.nan} # takes the first ball by convention
Ancestors
class AddBallStateFactory
-
Expand source code
class AddBallStateFactory(Transform): def __call__(self, view_key, view): predicate = lambda a: a.camera == view_key.camera and a.type == "ball" and view.calib.projects_in(a.center) and a.visible is not False balls = [a for a in view.annotations if predicate(a)] return {"ball_state": balls[0].state if balls and balls[0].state is not None else BallState.NONE} # takes the first ball by convention
Ancestors
class AddBallVisibilityFactory
-
Expand source code
class AddBallVisibilityFactory(Transform): def __call__(self, view_key, view): return {"ball_visible": view.ball.visible}
Ancestors
class AddCalibFactory (as_dict=False)
-
Expand source code
class AddCalibFactory(Transform): def __init__(self, as_dict=False): self.as_dict = as_dict @staticmethod def to_basic_dict(calib): return { "K": calib.K, "r": cv2.Rodrigues(calib.R)[0].flatten(), "T": calib.T, "width": np.array([calib.width]), "height": np.array([calib.height]), "kc": np.array(calib.kc), } def __call__(self, view_key, view): if self.as_dict: return self.to_basic_dict(view.calib) return {"calib": view.calib}
Ancestors
Static methods
def to_basic_dict(calib)
-
Expand source code
@staticmethod def to_basic_dict(calib): return { "K": calib.K, "r": cv2.Rodrigues(calib.R)[0].flatten(), "T": calib.T, "width": np.array([calib.width]), "height": np.array([calib.height]), "kc": np.array(calib.kc), }
class AddCourtFactory
-
Expand source code
class AddCourtFactory(Transform): def __call__(self, view_key, view): if not getattr(view, "court", None): view.court = Court() return { "court_width": np.array([view.court.w]), "court_height": np.array([view.court.h]) }
Ancestors
class AddDiffFactory
-
Expand source code
class AddDiffFactory(Transform): def __call__(self, view_key, view): raise NotImplementedError() # code needs to be re-implemented: current implementation only adds next image return {"input_image2": view.all_images[1]}
Ancestors
class AddHumansSegmentationTargetViewFactory
-
Expand source code
class AddHumansSegmentationTargetViewFactory(Transform): def __call__(self, view_key, view): return {"human_masks": view.human_masks}
Ancestors
class AddImageFactory
-
Expand source code
class AddImageFactory(Transform): def __call__(self, view_key, view): return {"input_image": view.image}
Ancestors
class AddNextImageFactory
-
Expand source code
class AddNextImageFactory(Transform): def __call__(self, view_key, view): return {"input_image2": view.all_images[1]}
Ancestors
class BallCropperTransform (*args, def_min=None, def_max=None, size_min=None, size_max=None, scale_min=None, scale_max=None, **kwargs)
-
Create a random scaled thumbnail around view's ball. - If [min_size, max_size] range is given, scale factor is chosen s.t. ball size is in the [min_size, max_size] range. - If [def_min, def_max] range is given, scale factor is chosen s.t. image definition [px/m] is in the [def_min, def_max] range at ball location. - If [scale_min, scale_max] range is given, scale factor is chosen in that range. An error is raised if multiple options are given.
Randomly scale, crop and rotate dataset items. The scale factor is randomly selected to keep the given keypoints of interest between
size_min
andsize_max
(At each call, the current keypoint size is returned by_get_current_parameters
).Arguments
output_shape: Tuple(int, int) final shape of image-like data. size_min: (int) lower bound of keypoints random size. If
0
size_min
andsize_max
are ignored and no random scaling is applied. size_max: (int) upper bound of keypoints random size. If0
size_min
andsize_max
are ignored and no random scaling is applied. max_angle: (degrees) positive and negative bounds for random rotation do_flip: (bool) tells if random flip should be applied padding: (px) amount of padding margin: (px) minimum margin between keypoints and output image border debug: (bool) ifTrue
, doesn't actually crop but display debug information on image instead. regenerate: (bool) ifTrue
, items are (deep)-copied before calling_apply_transformation
. Else, transformation can occur in-place.Expand source code
class BallCropperTransform(ViewRandomCropperTransform): """ Create a random scaled thumbnail around view's ball. - If [min_size, max_size] range is given, scale factor is chosen s.t. ball size is in the [min_size, max_size] range. - If [def_min, def_max] range is given, scale factor is chosen s.t. image definition [px/m] is in the [def_min, def_max] range at ball location. - If [scale_min, scale_max] range is given, scale factor is chosen in that range. An error is raised if multiple options are given. """ def __init__(self, *args, def_min=None, def_max=None, size_min=None, size_max=None, scale_min=None, scale_max=None, **kwargs): msg = "Only one of ('size_min' and 'size_max') or ('def_min' and 'def_max') or ('scale_min' and 'scale_max') should be defined" if size_min is not None and size_max is not None: assert all([x is None for x in [def_min, def_max, scale_min, scale_max]]), msg super().__init__(*args, size_min=size_min, size_max=size_max, **kwargs) self.true_size = BALL_DIAMETER elif def_min is not None and def_max is not None: assert all([x is None for x in [size_min, size_max, scale_min, scale_max]]), msg super().__init__(*args, size_min=def_min, size_max=def_max, **kwargs) self.true_size = 100 elif scale_min is not None and scale_max is not None: assert all([x is None for x in [size_min, size_max, def_min, def_max]]), msg super().__init__(*args, size_min=scale_min, size_max=scale_max, **kwargs) self.true_size = None else: raise ValueError(msg) def _get_current_parameters(self, view_key, view): keypoints = view.calib.project_3D_to_2D(view.ball.center) if self.true_size is None: size = 1 else: size = float(view.calib.compute_length2D(view.ball.center, self.true_size)) input_shape = view.calib.width, view.calib.height return keypoints, size, input_shape
Ancestors
Inherited members
class BallViewRandomCropperTransform (*args, size_min=None, size_max=None, def_min=None, def_max=None, on_ball=False, **kwargs)
-
def - definition in pixels per meters. 60px/m = ball of 14px
Expand source code
class BallViewRandomCropperTransform(CleverViewRandomCropperTransform): def __init__(self, *args, size_min=None, size_max=None, def_min=None, def_max=None, on_ball=False, **kwargs): msg = "Only one of ('size_min' and 'size_max') or ('def_min' and 'def_max') should be defined" if size_min is not None and size_max is not None: assert def_min is None and def_max is None, msg super().__init__(*args, def_min=size_min, def_max=size_max, **kwargs) self.true_size = BALL_DIAMETER elif def_min is not None and def_max is not None: assert size_min is None and size_max is None, msg super().__init__(*args, def_min=def_min, def_max=def_max, **kwargs) self.true_size = 100 self.on_ball = {False: 0.5, True: 1.0, None: 0.0}.get(on_ball, on_ball) def _get_current_parameters(self, view_key, view): balls = [a for a in view.annotations if a.type == "ball" and a.camera == view_key.camera] ball = random.sample(balls, 1)[0].center if balls else None # If not `on_ball` use the ball anyway half of the samples if random.random() < self.on_ball and ball is not None: keypoint = ball view.ball = ball else: keypoint = self.random_ball(view_key, view) view.ball = Ball({ 'center': keypoint, 'origin': "random", 'image': view_key.camera, 'visible': False, }) # Use ball if any, else use the random ball (it only affects the strategy to scale) size = float(view.calib.compute_length2D(ball if ball is not None else keypoint, self.true_size)) keypoints = view.calib.project_3D_to_2D(keypoint) input_shape = view.calib.width, view.calib.height return keypoints, size, input_shape
Ancestors
Inherited members
class BayeringTransform
-
Expand source code
class BayeringTransform(Transform): def __init__(self): self.R_filter = np.array([[1,0],[0,0]]) self.G_filter = np.array([[0,1],[1,0]]) self.B_filter = np.array([[0,0],[0,1]]) def __call__(self, view_key, view): height, width, _ = view.image.shape R_mask = np.tile(self.R_filter, [height//2, width//2]) G_mask = np.tile(self.G_filter, [height//2, width//2]) B_mask = np.tile(self.B_filter, [height//2, width//2]) mask = np.stack((R_mask, G_mask, B_mask), axis=2) mask = mask[np.newaxis] for i, image in enumerate(view.all_images): view.all_images[i] = np.sum(image*mask, axis=3) view.image = view.all_images[0]
Ancestors
class CleverViewRandomCropperTransform (*args, def_min=60, def_max=160, **kwargs)
-
def - definition in pixels per meters. 60px/m = ball of 14px
Expand source code
class CleverViewRandomCropperTransform(ViewRandomCropperTransform): def __init__(self, *args, def_min=60, def_max=160, **kwargs): """ def - definition in pixels per meters. 60px/m = ball of 14px """ super().__init__(*args, size_min=def_min, size_max=def_max, **kwargs) def random_ball(self, view_key, view): court = setdefaultattr(view, "court", Court(getattr(view, "rule_type", "FIBA"))) #court_polygon = view.calib.get_region_visible_corners_2d(court.corners, 1) top_edge = list(court.visible_edges(view.calib))[0] start = top_edge[0][0][0] stop = top_edge[1][0][0] x = np.random.beta(2, 2)*(stop-start)+start y = np.random.beta(2, 2)*court.h/2+court.h/4 z = 0 return Point3D(x,y,z) def _get_current_parameters(self, view_key, view): keypoints = self.random_ball(view_key, view) size = float(view.calib.compute_length2D(keypoints, 100)) keypoints = view.calib.project_3D_to_2D(keypoints) input_shape = view.calib.width, view.calib.height if self.debug: raise NotImplementedError("Selected keypoint should be drawn in view.image (as well as it's projection on court for better visualization)") return keypoints, size, input_shape
Ancestors
Subclasses
Methods
def random_ball(self, view_key, view)
-
Expand source code
def random_ball(self, view_key, view): court = setdefaultattr(view, "court", Court(getattr(view, "rule_type", "FIBA"))) #court_polygon = view.calib.get_region_visible_corners_2d(court.corners, 1) top_edge = list(court.visible_edges(view.calib))[0] start = top_edge[0][0][0] stop = top_edge[1][0][0] x = np.random.beta(2, 2)*(stop-start)+start y = np.random.beta(2, 2)*court.h/2+court.h/4 z = 0 return Point3D(x,y,z)
Inherited members
class ComputeDiff (squash=False, inplace=False)
-
Expand source code
class ComputeDiff(Transform): def __init__(self, squash=False, inplace=False): self.squash = squash self.inplace = inplace def __call__(self, view_key: ViewKey, view: View): diff = np.abs(view.image.astype(np.int32) - view.all_images[1].astype(np.int32)).astype(np.uint8) if self.squash: diff = np.mean(diff, axis=2).astype(np.uint8) if self.inplace: view.image = np.dstack((view.image, diff)) else: view.diff = diff return view
Ancestors
class GameGammaColorTransform (transform_dict)
-
Expand source code
class GameGammaColorTransform(Transform): def __init__(self, transform_dict): assert all([isinstance(k, int) for k in transform_dict.keys()]) #29582 : [1.04, 1.02, 0.93], #24651 : [1.05, 1.02, 0.92], #30046 : [1.01, 1.01, 1.01] self.transform_dict = transform_dict def __call__(self, view_key, view): if view_key.instant_key.game_id in self.transform_dict.keys(): gammas = np.array(self.transform_dict[view_key.instant_key.game_id]) view.image = gamma_correction(view.image, gammas) return view
Ancestors
class GameRGBColorTransform (transform_dict)
-
Expand source code
class GameRGBColorTransform(Transform): def __init__(self, transform_dict): assert all([isinstance(k, int) for k in transform_dict.keys()]) self.transform_dict = transform_dict def __call__(self, view_key: ViewKey, view: View): if view_key.instant_key.game_id in self.transform_dict.keys(): adaptation_vector = np.array(self.transform_dict[view_key.instant_key.game_id]) view.image = np.clip(view.image.astype(np.float32)*adaptation_vector, 0, 255).astype(np.uint8) return view
Ancestors
class NaiveViewRandomCropperTransform (*args, scale_min=0.5, scale_max=2, **kwargs)
-
scale is the scale factor that will be applied to images
Expand source code
class NaiveViewRandomCropperTransform(ViewRandomCropperTransform): def __init__(self, *args, scale_min=0.5, scale_max=2, **kwargs): """ scale is the scale factor that will be applied to images """ super().__init__(*args, size_min=scale_min, size_max=scale_max, **kwargs) def _get_current_parameters(self, view_key, view): input_shape = view.calib.width, view.calib.height return None, 1, input_shape
Ancestors
Inherited members
class PlayerViewRandomCropperTransform (output_shape, def_min=60, def_max=160, margin=100, **kwargs)
-
def - definition in pixels per meters. 60px/m = ball of 14px margin - a margin in cm the keypoints
Expand source code
class PlayerViewRandomCropperTransform(ViewRandomCropperTransform): def __init__(self, output_shape, def_min=60, def_max=160, margin=100, **kwargs): """ def - definition in pixels per meters. 60px/m = ball of 14px margin - a margin in cm the keypoints """ super().__init__(output_shape=output_shape, size_min=def_min, size_max=def_max, **kwargs) self.margin = margin def focus_on_player(self, view_key, view): players = [a for a in view.annotations if a.type == "player" and a.camera == view_key.camera] if not players: return None player = random.sample(players, 1)[0] keypoints = Point3D([player.head, player.hips, player.foot1, player.foot2]) return keypoints def _get_current_parameters(self, view_key, view): raise NotImplementedError("This code was not tested. Images should be visualized.") keypoints = self.focus_on_player(view_key, view) if keypoints is None: return None margin = float(view.calib.compute_length2D(Point3D(np.mean(keypoints, axis=1)), self.margin)) size = float(view.calib.compute_length2D(Point3D(np.mean(keypoints, axis=1)), 100)) keypoints = view.calib.project_3D_to_2D(keypoints) input_shape = view.calib.width, view.calib.height return keypoints, size, input_shape
Ancestors
Methods
def focus_on_player(self, view_key, view)
-
Expand source code
def focus_on_player(self, view_key, view): players = [a for a in view.annotations if a.type == "player" and a.camera == view_key.camera] if not players: return None player = random.sample(players, 1)[0] keypoints = Point3D([player.head, player.hips, player.foot1, player.foot2]) return keypoints
Inherited members
class UndistortTransform
-
Expand source code
class UndistortTransform(Transform): def __call__(self, key, view): all_images = [] for image in view.all_images: all_images.append(cv2.undistort(image, view.calib.K, view.calib.kc)) view.calib = view.calib.update(kc=np.array([0,0,0,0,0])) return view
Ancestors
class ViewRandomCropperTransform (output_shape, size_min, size_max, max_angle=0, do_flip=False, padding=0, margin=0, debug=False, regenerate=False)
-
Randomly scale, crop and rotate dataset items. The scale factor is randomly selected to keep the given keypoints of interest between
size_min
andsize_max
(At each call, the current keypoint size is returned by_get_current_parameters
).Arguments
output_shape: Tuple(int, int) final shape of image-like data. size_min: (int) lower bound of keypoints random size. If
0
size_min
andsize_max
are ignored and no random scaling is applied. size_max: (int) upper bound of keypoints random size. If0
size_min
andsize_max
are ignored and no random scaling is applied. max_angle: (degrees) positive and negative bounds for random rotation do_flip: (bool) tells if random flip should be applied padding: (px) amount of padding margin: (px) minimum margin between keypoints and output image border debug: (bool) ifTrue
, doesn't actually crop but display debug information on image instead. regenerate: (bool) ifTrue
, items are (deep)-copied before calling_apply_transformation
. Else, transformation can occur in-place.Expand source code
class ViewRandomCropperTransform(RandomCropperTransform): def _apply_transformation(self, view, A): if self.debug: w, h = self.output_shape points = Point2D(np.linalg.inv(A)@Point2D([0,0,w,w],[0,h,h,0]).H) cv2.polylines(view.image, [points.T.astype(np.int32)], True, (255,0,0), self.linewidth) else: view.image = cv2.warpAffine(view.image, A[0:2,:], self.output_shape, flags=cv2.INTER_LINEAR) view.calib = view.calib.update(K=A@view.calib.K, width=self.output_shape[0], height=self.output_shape[1]) if hasattr(view, "all_images"): for i in range(1, len(view.all_images)): # skip first image as it was already done view.all_images[i] = cv2.warpAffine(view.all_images[i], A[0:2,:], self.output_shape, flags=cv2.INTER_LINEAR) if hasattr(view, "human_masks") and view.human_masks is not None: view.human_masks = cv2.warpAffine(view.human_masks, A[0:2,:], self.output_shape, flags=cv2.INTER_NEAREST) return view
Ancestors
Subclasses
- BallCropperTransform
- CleverViewRandomCropperTransform
- NaiveViewRandomCropperTransform
- PlayerViewRandomCropperTransform
Inherited members