Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix DEKR's replace_head & improve __repr__ for keypoints transforms #1227

Merged
merged 65 commits into from
Jun 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
b6ccadd
YOLO-NAS Pose Estimation Experiment
BloodAxe May 25, 2023
84db65a
Added logging
BloodAxe May 25, 2023
12ac13b
Remove test
BloodAxe May 25, 2023
87cf779
Tune recipe for L
BloodAxe May 25, 2023
a668531
Tune recipe for S
BloodAxe May 26, 2023
475d4c7
Tune optimizer params
BloodAxe May 27, 2023
94d98ee
Lower LR
BloodAxe May 27, 2023
c2866d4
Lower LR
BloodAxe May 27, 2023
3615b24
Added __repr__ for keypoint transforms to improve their printing in n…
BloodAxe Jun 2, 2023
7613c25
If OKS sigmas are not given explicitly, initialize with default 17 ke…
BloodAxe Jun 2, 2023
f09fbb9
Added recipe to train rescoring for yolo_nas_pose_l
BloodAxe Jun 2, 2023
1839115
Added YOLO-NAS-POSE scores
BloodAxe Jun 2, 2023
6fe9e13
Added YOLO-NAS-POSE-M recipe
BloodAxe Jun 2, 2023
f9c7a11
Fine-tuning notebook for pose est
BloodAxe Jun 2, 2023
3f56b22
Added lings to pretrained models
BloodAxe Jun 5, 2023
8d8aafa
Remove links to S & M models
BloodAxe Jun 5, 2023
8101c8b
Update notebook
BloodAxe Jun 5, 2023
e21910e
Fix apply_sigmoid=True
BloodAxe Jun 5, 2023
fbca2a5
Increased eps to prevent divizion by zero
BloodAxe Jun 5, 2023
b98e8f8
Update notebook
BloodAxe Jun 5, 2023
7fa318a
Merge branch 'master' into feature/SG-000-yolo-nas-pose
BloodAxe Jun 6, 2023
06080a6
Merge branch 'master' into feature/SG-000-yolo-nas-pose
BloodAxe Jun 6, 2023
a0a3330
Adding predict
BloodAxe Jun 6, 2023
62c38b8
Predict
BloodAxe Jun 6, 2023
90c0566
Predict
BloodAxe Jun 6, 2023
447e7f5
Adding predict
BloodAxe Jun 6, 2023
e9cbb12
Adding predict
BloodAxe Jun 7, 2023
117bb78
Adding joint information to dataset configs
BloodAxe Jun 7, 2023
38f9066
Merge branch 'master' into feature/SG-910-predict-for-pose
BloodAxe Jun 7, 2023
3971a88
Added makefile target recipe_accuracy_tests
BloodAxe Jun 7, 2023
10b2dd8
Remove temp files
BloodAxe Jun 7, 2023
3bfe555
Rename variables for better clarity
BloodAxe Jun 7, 2023
a36fd5f
Merge branch 'master' into feature/SG-000-yolo-nas-pose
BloodAxe Jun 7, 2023
16c29cc
Move predict() related files to super_gradients.training.utils.predict
BloodAxe Jun 7, 2023
c319886
Move predict() related files to super_gradients.training.utils.predict
BloodAxe Jun 7, 2023
fb9f7d9
Rename file poses.py -> pose_estimation.py
BloodAxe Jun 7, 2023
e27d373
Rename joint_colors/joint_links -> edge_colors/edge_links
BloodAxe Jun 7, 2023
c181f46
Disable showing bounding box by default
BloodAxe Jun 7, 2023
6d914b5
Allow passing edge & keypoints as None, in this case colors will be g…
BloodAxe Jun 8, 2023
d2f8494
Update docstrings
BloodAxe Jun 8, 2023
670c1c2
Fix test
BloodAxe Jun 8, 2023
3fedce2
Merge branch 'master' into feature/SG-910-predict-for-pose
BloodAxe Jun 8, 2023
d40b48a
Added unit tests to verify settings preprocesisng params from dataset…
BloodAxe Jun 9, 2023
4a6293f
Merge remote-tracking branch 'origin/feature/SG-910-predict-for-pose'…
BloodAxe Jun 9, 2023
aef4bb7
Merge branch 'master' into feature/SG-000-yolo-nas-pose
BloodAxe Jun 9, 2023
9bb581a
Merge predict branch
BloodAxe Jun 9, 2023
ec53f5b
Added predict
BloodAxe Jun 9, 2023
7144e78
Added default prerprocessing settings for yolo-nas-pose
BloodAxe Jun 9, 2023
2b119d1
Added default prerprocessing settings for yolo-nas-pose
BloodAxe Jun 9, 2023
02330d5
Added __repr__ to KeypointsImageToTensor
BloodAxe Jun 9, 2023
1abbc07
_pad_image cannot work with pad_value that is tuple (r,g,b).
BloodAxe Jun 9, 2023
0cf0092
_pad_image cannot work with pad_value that is tuple (r,g,b).
BloodAxe Jun 9, 2023
02d1931
_pad_image cannot work with pad_value that is tuple (r,g,b).
BloodAxe Jun 9, 2023
19f1061
Fix pad_value in keypoints transforms to accept single scalar value t…
BloodAxe Jun 11, 2023
3e4b24b
Merge branch 'feature/SG-910-predict-for-pose' into feature/SG-000-yo…
BloodAxe Jun 11, 2023
d2c30f3
Merge branch 'master' into feature/SG-000-yolo-nas-pose
BloodAxe Jun 11, 2023
572ad68
Update signature of base YoloNasPose class (dropped arch_params)
BloodAxe Jun 12, 2023
8de2595
Simplify recipes to train YOLO-NAS-POSE
BloodAxe Jun 12, 2023
3676a1a
Add human-friendly __repr__ to keypoint transforms which should impro…
BloodAxe Jun 27, 2023
556c7c8
Implement replace_head for dekr
BloodAxe Jun 27, 2023
f0dd8d2
Implement replace_head for dekr
BloodAxe Jun 27, 2023
dafa537
Merge branch 'master' into feature/SG-000-repr-for-transforms
BloodAxe Jun 28, 2023
99fd7f6
Make more beautiful __repr__ implementation
BloodAxe Jun 28, 2023
f2abdad
Change .format to string interpolation
BloodAxe Jun 28, 2023
2d4e1c8
Merge branch 'master' into feature/SG-000-repr-for-transforms
BloodAxe Jun 28, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,14 @@ def __init__(
self.greater_component_is_better = dict((k, True) for k in self.stats_names)

if oks_sigmas is None:
oks_sigmas = np.array([0.26, 0.25, 0.25, 0.35, 0.35, 0.79, 0.79, 0.72, 0.72, 0.62, 0.62, 1.07, 1.07, 0.87, 0.87, 0.89, 0.89]) / 10.0
if num_joints == 17:
oks_sigmas = np.array([0.26, 0.25, 0.25, 0.35, 0.35, 0.79, 0.79, 0.72, 0.72, 0.62, 0.62, 1.07, 1.07, 0.87, 0.87, 0.89, 0.89]) / 10.0
else:
oks_sigmas = np.array([0.1] * num_joints)
logger.warning(
f"Using default OKS sigmas of `0.1` for a custom dataset with {num_joints} joints. "
f"To silence this warning, you may want to specify OKS sigmas explicitly as it has direct impact on the AP score."
)

if len(oks_sigmas) != num_joints:
raise ValueError(f"Length of oks_sigmas ({len(oks_sigmas)}) should be equal to num_joints {num_joints}")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,22 +328,32 @@ def __init__(self, arch_params):
setattr(self, "stage{}".format(i + 2), stage)

# build head net
inp_channels = int(sum(self.stages_spec.NUM_CHANNELS[-1]))
config_heatmap = self.spec.HEAD_HEATMAP
config_offset = self.spec.HEAD_OFFSET
self.head_inp_channels = int(sum(self.stages_spec.NUM_CHANNELS[-1]))
self.config_heatmap = self.spec.HEAD_HEATMAP
self.config_offset = self.spec.HEAD_OFFSET
self.num_joints = arch_params.num_classes
self.num_offset = self.num_joints * 2
self.num_joints_with_center = self.num_joints + 1
self.offset_prekpt = config_offset["NUM_CHANNELS_PERKPT"]
self.offset_prekpt = self.config_offset["NUM_CHANNELS_PERKPT"]

offset_channels = self.num_joints * self.offset_prekpt
self.transition_heatmap = self._make_transition_for_head(inp_channels, config_heatmap["NUM_CHANNELS"])
self.transition_offset = self._make_transition_for_head(inp_channels, offset_channels)
self.head_heatmap = self._make_heatmap_head(config_heatmap)
self.offset_feature_layers, self.offset_final_layer = self._make_separete_regression_head(config_offset)
self.heatmap_activation = nn.Sigmoid() if config_heatmap["HEATMAP_APPLY_SIGMOID"] else nn.Identity()
self.transition_heatmap = self._make_transition_for_head(self.head_inp_channels, self.config_heatmap["NUM_CHANNELS"])
self.transition_offset = self._make_transition_for_head(self.head_inp_channels, offset_channels)
self.head_heatmap = self._make_heatmap_head(self.config_heatmap)
self.offset_feature_layers, self.offset_final_layer = self._make_separete_regression_head(self.config_offset)
self.heatmap_activation = nn.Sigmoid() if self.config_heatmap["HEATMAP_APPLY_SIGMOID"] else nn.Identity()
self.init_weights()

def replace_head(self, new_num_classes: int):
self.num_joints = new_num_classes
self.num_offset = new_num_classes * 2
self.num_joints_with_center = new_num_classes + 1

offset_channels = self.num_joints * self.offset_prekpt
self.head_heatmap = self._make_heatmap_head(self.config_heatmap)
self.transition_offset = self._make_transition_for_head(self.head_inp_channels, offset_channels)
self.offset_feature_layers, self.offset_final_layer = self._make_separete_regression_head(self.config_offset)

def _make_transition_for_head(self, inplanes: int, outplanes: int) -> nn.Module:
transition_layer = [nn.Conv2d(inplanes, outplanes, 1, 1, 0, bias=False), nn.BatchNorm2d(outplanes), nn.ReLU(True)]
return nn.Sequential(*transition_layer)
Expand Down
48 changes: 48 additions & 0 deletions src/super_gradients/training/transforms/keypoint_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,13 @@ def get_equivalent_preprocessing(self) -> List:
preprocessing += t.get_equivalent_preprocessing()
return preprocessing

def __repr__(self):
format_string = self.__class__.__name__ + "("
for t in self.transforms:
format_string += f"\t{repr(t)}"
format_string += "\n)"
return format_string


@register_transform(Transforms.KeypointsImageToTensor)
class KeypointsImageToTensor(KeypointTransform):
Expand All @@ -87,6 +94,9 @@ def get_equivalent_preprocessing(self) -> List:
{Processings.ImagePermute: {"permutation": (2, 0, 1)}},
]

def __repr__(self):
return self.__class__.__name__ + f"(permutation={self.permutation})"


@register_transform(Transforms.KeypointsImageStandardize)
class KeypointsImageStandardize(KeypointTransform):
Expand All @@ -107,6 +117,9 @@ def __call__(self, image: np.ndarray, mask: np.ndarray, joints: np.ndarray, area
def get_equivalent_preprocessing(self) -> List[Dict]:
return [{Processings.StandardizeImage: {"max_value": self.max_value}}]

def __repr__(self):
return self.__class__.__name__ + f"(max_value={self.max_value})"


@register_transform(Transforms.KeypointsImageNormalize)
class KeypointsImageNormalize(KeypointTransform):
Expand All @@ -122,6 +135,9 @@ def __call__(self, image: np.ndarray, mask: np.ndarray, joints: np.ndarray, area
image = (image - self.mean) / self.std
return image, mask, joints, areas, bboxes

def __repr__(self):
return self.__class__.__name__ + f"(mean={self.mean}, std={self.std})"

def get_equivalent_preprocessing(self) -> List:
return [{Processings.NormalizeImage: {"mean": self.mean, "std": self.std}}]

Expand All @@ -143,6 +159,9 @@ def __init__(self, flip_index: List[int], prob: float = 0.5):
self.flip_index = flip_index
self.prob = prob

def __repr__(self):
return self.__class__.__name__ + f"(flip_index={self.flip_index}, prob={self.prob})"

def __call__(self, image, mask, joints, areas: Optional[np.ndarray], bboxes: Optional[np.ndarray]):
if image.shape[:2] != mask.shape[:2]:
raise RuntimeError(f"Image shape ({image.shape[:2]}) does not match mask shape ({mask.shape[:2]}).")
Expand Down Expand Up @@ -218,6 +237,9 @@ def apply_to_bboxes(self, bboxes, rows):
def get_equivalent_preprocessing(self) -> List:
raise RuntimeError("KeypointsRandomHorizontalFlip does not have equivalent preprocessing.")

def __repr__(self):
return self.__class__.__name__ + f"(prob={self.prob})"


@register_transform(Transforms.KeypointsLongestMaxSize)
class KeypointsLongestMaxSize(KeypointTransform):
Expand Down Expand Up @@ -278,6 +300,13 @@ def apply_to_keypoints(cls, keypoints, scale):
def apply_to_bboxes(cls, bboxes, scale):
return bboxes * scale

def __repr__(self):
return (
self.__class__.__name__ + f"(max_height={self.max_height}, "
f"max_width={self.max_width}, "
f"interpolation={self.interpolation}, prob={self.prob})"
)

def get_equivalent_preprocessing(self) -> List:
return [{Processings.KeypointsLongestMaxSizeRescale: {"output_shape": (self.max_height, self.max_width)}}]

Expand Down Expand Up @@ -318,6 +347,14 @@ def __call__(self, image, mask, joints, areas: Optional[np.ndarray], bboxes: Opt

return image, mask, joints, areas, bboxes

def __repr__(self):
return (
self.__class__.__name__ + f"(min_height={self.min_height}, "
f"min_width={self.min_width}, "
f"image_pad_value={self.image_pad_value}, "
f"mask_pad_value={self.mask_pad_value})"
)

def get_equivalent_preprocessing(self) -> List:
return [{Processings.KeypointsBottomRightPadding: {"output_shape": (self.min_height, self.min_width), "pad_value": self.image_pad_value}}]

Expand Down Expand Up @@ -353,6 +390,17 @@ def __init__(
self.mask_pad_value = mask_pad_value
self.prob = prob

def __repr__(self):
return (
self.__class__.__name__ + f"(max_rotation={self.max_rotation}, "
f"min_scale={self.min_scale}, "
f"max_scale={self.max_scale}, "
f"max_translate={self.max_translate}, "
f"image_pad_value={self.image_pad_value}, "
f"mask_pad_value={self.mask_pad_value}, "
f"prob={self.prob})"
)

def _get_affine_matrix(self, img, angle, scale, dx, dy):
"""

Expand Down
7 changes: 7 additions & 0 deletions tests/unit_tests/replace_head_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ def test_yolo_nas_replace_head(self):
(_, pred_scores), _ = model.forward(input)
self.assertEqual(pred_scores.size(2), 100)

def test_dekr_replace_head(self):
input = torch.randn(1, 3, 640, 640).to(self.device)
model = models.get(Models.DEKR_W32_NO_DC, num_classes=20, pretrained_weights="coco_pose").to(self.device).eval()
heatmap, offsets = model.forward(input)
self.assertEqual(heatmap.size(1), 20 + 1)
self.assertEqual(offsets.size(1), 20 * 2)

def tearDown(self) -> None:
if os.path.exists("~/.cache/torch/hub/"):
shutil.rmtree("~/.cache/torch/hub/")
Expand Down