Skip to content

Commit c37ed06

Browse files
Add cuda-cv backend
Test Plan: ```bash python benchmarks/benchmark_transforms.py --device cuda --batch-size 32 ```
1 parent a85db8e commit c37ed06

File tree

2 files changed

+63
-5
lines changed

2 files changed

+63
-5
lines changed

benchmarks/benchmark_transforms.py

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,22 @@
5050
except ImportError:
5151
HAS_KORNIA = False
5252

53+
54+
try:
55+
import cvcuda
56+
import nvcv
57+
58+
HAS_CUDA_CV = True
59+
except ImportError:
60+
HAS_CUDA_CV = False
61+
5362
from PIL import Image
5463

5564
# ImageNet normalization constants
5665
NORM_MEAN = [0.485, 0.456, 0.406]
5766
NORM_STD = [0.229, 0.224, 0.225]
58-
67+
NORM_MEAN_CUDA_CV = cvcuda.as_tensor(torch.Tensor(NORM_MEAN).reshape(1, 1, 1, 3).cuda(), "NHWC")
68+
NORM_STD_CUDA_CV = cvcuda.as_tensor(torch.Tensor(NORM_STD).reshape(1, 1, 1, 3).cuda(), "NHWC")
5969

6070
def torchvision_pipeline(images: torch.Tensor, target_size: int) -> torch.Tensor:
6171
images = F.resize(
@@ -91,6 +101,25 @@ def pil_pipeline(image: Image.Image, target_size: int) -> torch.Tensor:
91101
return img
92102

93103

104+
def cudacv_pipeline(image: cvcuda.Tensor, target_size: int) -> torch.Tensor:
105+
106+
img: cvcuda.Tensor = cvcuda.resize(
107+
image,
108+
(image.shape[0], target_size, target_size, image.shape[-1]), # N, H, W, C
109+
interp=cvcuda.Interp.LINEAR,
110+
)
111+
img: cvcuda.Tensor = cvcuda.convertto(
112+
src=img,
113+
dtype=np.float32,
114+
scale=1.0/255.0,
115+
)
116+
117+
img: cvcuda.Tensor = cvcuda.normalize(
118+
img, NORM_MEAN_CUDA_CV, NORM_STD_CUDA_CV
119+
)
120+
return torch.as_tensor(img.cuda())
121+
122+
94123
def albumentations_pipeline(image: np.ndarray, target_size: int) -> torch.Tensor:
95124
transform = A.Compose(
96125
[
@@ -125,7 +154,7 @@ def run_benchmark(args) -> Dict[str, Any]:
125154

126155
device = args.device.lower()
127156
# Check device compatibility
128-
if device == "cuda" and backend not in ["tv", "tv-compiled"]:
157+
if device == "cuda" and backend not in ["tv", "tv-compiled", "cudacv"]:
129158
raise RuntimeError(
130159
f"CUDA device not supported for {backend} backend. Only 'tv' and 'tv-compiled' support CUDA."
131160
)
@@ -135,6 +164,8 @@ def run_benchmark(args) -> Dict[str, Any]:
135164

136165
if backend == "opencv" and not HAS_OPENCV:
137166
raise RuntimeError("OpenCV not available. Install with: pip install opencv-python")
167+
if backend == "cudacv" and not HAS_CUDA_CV:
168+
raise RuntimeError("CudaCV not available. Install with: pip install cudacv")
138169
if backend == "albumentations" and not HAS_ALBUMENTATIONS:
139170
raise RuntimeError("Albumentations not available. Install with: pip install albumentations")
140171
if backend == "kornia" and not HAS_KORNIA:
@@ -163,6 +194,9 @@ def run_benchmark(args) -> Dict[str, Any]:
163194
elif backend == "pil":
164195
torch.set_num_threads(args.num_threads)
165196
pipeline = pil_pipeline
197+
elif backend == "cudacv":
198+
torch.set_num_threads(args.num_threads)
199+
pipeline = cudacv_pipeline
166200
elif backend == "albumentations":
167201
cv2.setNumThreads(args.num_threads)
168202
pipeline = albumentations_pipeline
@@ -180,7 +214,7 @@ def generate_test_images():
180214
images = images.to(memory_format=torch.channels_last)
181215

182216
# Move to device for torchvision backends
183-
if backend in ["tv", "tv-compiled"]:
217+
if backend in ["tv", "tv-compiled", "cudacv"]:
184218
images = images.to(device)
185219

186220
if args.batch_size == 1:
@@ -197,6 +231,12 @@ def generate_test_images():
197231
# Convert to PIL Image (CHW -> HWC)
198232
images = images.numpy().transpose(1, 2, 0)
199233
images = Image.fromarray(images)
234+
elif backend == "cudacv":
235+
if images.ndim == 3: # no batch dimension
236+
images = images.unsqueeze(0)
237+
# Permute from NCHW to NHWC and ensure contiguity
238+
images = images.permute(0, 2, 3, 1).contiguous()
239+
images = cvcuda.as_tensor(images, nvcv.TensorLayout.NHWC)
200240
elif backend == "albumentations":
201241
if args.batch_size > 1:
202242
# TODO is that true????
@@ -243,7 +283,16 @@ def main():
243283
default="CF",
244284
help="Memory format: CL (channels_last) or CF (channels_first, i.e. contiguous)",
245285
)
246-
all_backends = ["tv", "tv-v1", "tv-compiled", "opencv", "pil", "albumentations", "kornia"]
286+
all_backends = [
287+
"tv",
288+
"tv-v1",
289+
"tv-compiled",
290+
"opencv",
291+
"pil",
292+
"cudacv",
293+
"albumentations",
294+
"kornia",
295+
]
247296
parser.add_argument(
248297
"--backends",
249298
type=str,
@@ -279,7 +328,7 @@ def main():
279328
except Exception as e:
280329
print(f"ERROR with {backend}: {e}")
281330

282-
if len(results) > 1:
331+
if len(results) >= 1:
283332
print_comparison_table(results)
284333

285334

benchmarks/utils.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,14 @@
3030
except ImportError:
3131
HAS_KORNIA = False
3232

33+
try:
34+
import cvcuda
35+
import nvcv
36+
37+
HAS_CUDA_CV = True
38+
except ImportError:
39+
HAS_CUDA_CV = False
40+
3341
from PIL import Image
3442

3543

@@ -122,6 +130,7 @@ def print_benchmark_info(args):
122130
["TorchVision", torchvision.__version__],
123131
["OpenCV", cv2.__version__ if HAS_OPENCV else "Not available"],
124132
["PIL/Pillow", getattr(Image, "__version__", "Version unavailable")],
133+
["CUDA-CV", cvcuda.__version__ if HAS_CUDA_CV else "Not available"],
125134
["Albumentations", A.__version__ if HAS_ALBUMENTATIONS else "Not available"],
126135
["Kornia", K.__version__ if HAS_KORNIA else "Not available"],
127136
]

0 commit comments

Comments
 (0)