-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for complex_yolov4 model
- Loading branch information
1 parent
9be605e
commit d107322
Showing
14 changed files
with
2,969 additions
and
0 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,3 +51,4 @@ pytorch_forecasting==1.0.0 | |
patool | ||
openpyxl==3.1.5 | ||
GitPython==3.1.44 | ||
easydict==1.13 |
Empty file.
47 changes: 47 additions & 0 deletions
47
forge/test/models/pytorch/vision/complex_yolov4/test_complex_yolov4.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,47 @@ | ||
# SPDX-FileCopyrightText: (c) 2025 Tenstorrent AI ULC | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
import pytest | ||
import torch | ||
from easydict import EasyDict as edict | ||
|
||
import forge | ||
from forge.verify.verify import verify | ||
|
||
from test.models.pytorch.vision.complex_yolov4.utils.model_utils import create_model | ||
from test.models.utils import Framework, Source, Task, build_module_name | ||
|
||
|
||
@pytest.mark.parametrize("variant", ["complex_yolov4_tiny", "complex_yolov4"]) | ||
def test_compelx_yolov4(record_forge_property, variant): | ||
|
||
# Build Module Name | ||
module_name = build_module_name( | ||
framework=Framework.PYTORCH, | ||
model="complex_yolov4", | ||
variant=variant, | ||
source=Source.GITHUB, | ||
task=Task.OBJECT_DETECTION_3D, | ||
) | ||
|
||
# Record Forge Property | ||
record_forge_property("model_name", module_name) | ||
|
||
# Load model | ||
configs = edict( | ||
{ | ||
"arch": "darknet", | ||
"cfgfile": f"forge/test/models/pytorch/vision/complex_yolov4/utils/{variant}.cfg", | ||
} | ||
) | ||
model = create_model(configs) | ||
model.eval() | ||
|
||
# prepare sample input | ||
inputs = [torch.randn((1, 3, 608, 608))] | ||
|
||
# Forge compile framework model | ||
compiled_model = forge.compile(model, inputs, module_name=module_name) | ||
|
||
# Model Verification | ||
verify(inputs, model, compiled_model) |
Empty file.
175 changes: 175 additions & 0 deletions
175
forge/test/models/pytorch/vision/complex_yolov4/utils/cal_intersection_rotated_boxes.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,175 @@ | ||
# SPDX-FileCopyrightText: (c) 2025 Tenstorrent AI ULC | ||
# | ||
# SPDX-License-Identifier: Apache-2.0 | ||
""" | ||
# -*- coding: utf-8 -*- | ||
----------------------------------------------------------------------------------- | ||
# Author: Nguyen Mau Dung | ||
# DoC: 2020.07.20 | ||
# email: nguyenmaudung93.kstn@gmail.com | ||
----------------------------------------------------------------------------------- | ||
# Description: This script for intersection calculation of rotated boxes (on GPU) | ||
Refer from # https://stackoverflow.com/questions/44797713/calculate-the-area-of-intersection-of-two-rotated-rectangles-in-python?noredirect=1&lq=1 | ||
""" | ||
|
||
import torch | ||
|
||
|
||
class Line: | ||
# ax + by + c = 0 | ||
def __init__(self, p1, p2): | ||
""" | ||
Args: | ||
p1: (x, y) | ||
p2: (x, y) | ||
""" | ||
self.a = p2[1] - p1[1] | ||
self.b = p1[0] - p2[0] | ||
self.c = p2[0] * p1[1] - p2[1] * p1[0] # cross | ||
self.device = p1.device | ||
|
||
def cal_values(self, pts): | ||
return self.a * pts[:, 0] + self.b * pts[:, 1] + self.c | ||
|
||
def find_intersection(self, other): | ||
# See e.g. https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Using_homogeneous_coordinates | ||
if not isinstance(other, Line): | ||
return NotImplemented | ||
w = self.a * other.b - self.b * other.a | ||
return torch.tensor( | ||
[(self.b * other.c - self.c * other.b) / w, (self.c * other.a - self.a * other.c) / w], device=self.device | ||
) | ||
|
||
|
||
def intersection_area(rect1, rect2): | ||
"""Calculate the inter | ||
Args: | ||
rect1: vertices of the rectangles (4, 2) | ||
rect2: vertices of the rectangles (4, 2) | ||
Returns: | ||
""" | ||
|
||
# Use the vertices of the first rectangle as, starting vertices of the intersection polygon. | ||
intersection = rect1 | ||
|
||
# Loop over the edges of the second rectangle | ||
roll_rect2 = torch.roll(rect2, -1, dims=0) | ||
for p, q in zip(rect2, roll_rect2): | ||
if len(intersection) <= 2: | ||
break # No intersection | ||
|
||
line = Line(p, q) | ||
|
||
# Any point p with line(p) <= 0 is on the "inside" (or on the boundary), | ||
# any point p with line(p) > 0 is on the "outside". | ||
# Loop over the edges of the intersection polygon, | ||
# and determine which part is inside and which is outside. | ||
new_intersection = [] | ||
line_values = line.cal_values(intersection) | ||
roll_intersection = torch.roll(intersection, -1, dims=0) | ||
roll_line_values = torch.roll(line_values, -1, dims=0) | ||
for s, t, s_value, t_value in zip(intersection, roll_intersection, line_values, roll_line_values): | ||
if s_value <= 0: | ||
new_intersection.append(s) | ||
if s_value * t_value < 0: | ||
# Points are on opposite sides. | ||
# Add the intersection of the lines to new_intersection. | ||
intersection_point = line.find_intersection(Line(s, t)) | ||
new_intersection.append(intersection_point) | ||
|
||
if len(new_intersection) > 0: | ||
intersection = torch.stack(new_intersection) | ||
else: | ||
break | ||
|
||
# Calculate area | ||
if len(intersection) <= 2: | ||
return 0.0 | ||
|
||
return PolyArea2D(intersection) | ||
|
||
|
||
def PolyArea2D(pts): | ||
roll_pts = torch.roll(pts, -1, dims=0) | ||
area = (pts[:, 0] * roll_pts[:, 1] - pts[:, 1] * roll_pts[:, 0]).sum().abs() * 0.5 | ||
return area | ||
|
||
|
||
if __name__ == "__main__": | ||
import cv2 | ||
import numpy as np | ||
from shapely.geometry import Polygon | ||
|
||
def cvt_box_2_polygon(box): | ||
""" | ||
:param array: an array of shape [num_conners, 2] | ||
:return: a shapely.geometry.Polygon object | ||
""" | ||
# use .buffer(0) to fix a line polygon | ||
# more infor: https://stackoverflow.com/questions/13062334/polygon-intersection-error-in-shapely-shapely-geos-topologicalerror-the-opera | ||
return Polygon([(box[i, 0], box[i, 1]) for i in range(len(box))]).buffer(0) | ||
|
||
def get_corners_torch(x, y, w, l, yaw): | ||
device = x.device | ||
bev_corners = torch.zeros((4, 2), dtype=torch.float, device=device) | ||
cos_yaw = torch.cos(yaw) | ||
sin_yaw = torch.sin(yaw) | ||
# front left | ||
bev_corners[0, 0] = x - w / 2 * cos_yaw - l / 2 * sin_yaw | ||
bev_corners[0, 1] = y - w / 2 * sin_yaw + l / 2 * cos_yaw | ||
|
||
# rear left | ||
bev_corners[1, 0] = x - w / 2 * cos_yaw + l / 2 * sin_yaw | ||
bev_corners[1, 1] = y - w / 2 * sin_yaw - l / 2 * cos_yaw | ||
|
||
# rear right | ||
bev_corners[2, 0] = x + w / 2 * cos_yaw + l / 2 * sin_yaw | ||
bev_corners[2, 1] = y + w / 2 * sin_yaw - l / 2 * cos_yaw | ||
|
||
# front right | ||
bev_corners[3, 0] = x + w / 2 * cos_yaw - l / 2 * sin_yaw | ||
bev_corners[3, 1] = y + w / 2 * sin_yaw + l / 2 * cos_yaw | ||
|
||
return bev_corners | ||
|
||
# Show convex in an image | ||
|
||
img_size = 300 | ||
img = np.zeros((img_size, img_size, 3)) | ||
img = cv2.resize(img, (img_size, img_size)) | ||
|
||
box1 = torch.tensor([100, 100, 40, 10, np.pi / 2], dtype=torch.float).cuda() | ||
box2 = torch.tensor([100, 100, 40, 20, 0], dtype=torch.float).cuda() | ||
|
||
box1_conners = get_corners_torch(box1[0], box1[1], box1[2], box1[3], box1[4]) | ||
box1_polygon = cvt_box_2_polygon(box1_conners) | ||
box1_area = box1_polygon.area | ||
|
||
box2_conners = get_corners_torch(box2[0], box2[1], box2[2], box2[3], box2[4]) | ||
box2_polygon = cvt_box_2_polygon(box2_conners) | ||
box2_area = box2_polygon.area | ||
|
||
intersection = box2_polygon.intersection(box1_polygon).area | ||
union = box1_area + box2_area - intersection | ||
iou = intersection / (union + 1e-16) | ||
|
||
print( | ||
"Shapely- box1_area: {:.2f}, box2_area: {:.2f}, inter: {:.2f}, iou: {:.4f}".format( | ||
box1_area, box2_area, intersection, iou | ||
) | ||
) | ||
|
||
print("intersection from intersection_area(): {}".format(intersection_area(box1_conners, box2_conners))) | ||
|
||
img = cv2.polylines(img, [box1_conners.cpu().numpy().astype(np.int)], True, (255, 0, 0), 2) | ||
img = cv2.polylines(img, [box2_conners.cpu().numpy().astype(np.int)], True, (0, 255, 0), 2) | ||
|
||
while True: | ||
cv2.imshow("img", img) | ||
if cv2.waitKey(0) & 0xFF == 27: | ||
break |
Oops, something went wrong.