المعالجة المسبقة GPU باستخدام NVIDIA
مقدمة
عند النشر Ultralytics YOLO في الإنتاج، غالبًا ما تصبح المعالجة المسبقة هي العائق. في حين أن TensorRT تشغيل استدلال النموذج في غضون بضع ميلي ثوانٍ فقط، فإن المعالجة المسبقة CPU(تغيير الحجم، التعبئة، التوحيد) قد تستغرق من 2 إلى 10 مللي ثانية لكل صورة، خاصة عند الدرجات العالية من الدقة. تعمل NVIDIA (مكتبة تحميل البيانات) على حل هذه المشكلة عن طريق نقل مسار المعالجة المسبقة بالكامل إلى GPU.
يوجهك هذا الدليل خطوة بخطوة نحو إنشاء مسارات معالجة DALI تحاكي بالضبطYOLO Ultralytics YOLO ودمجها مع model.predict()، ومعالجة تدفقات الفيديو، ونشر الحلول الشاملة مع Triton خادم الاستدلال.
لمن هذا الدليل؟
هذا الدليل موجه للمهندسين الذين يقومون بنشر YOLO في بيئات الإنتاج حيث تشكل CPU عنق زجاجة ملحوظًا — عادةً TensorRT عمليات النشر على NVIDIA ، أو مسارات معالجة الفيديو عالية الإنتاجية، أو Triton خادم الاستدلال الإعدادات. إذا كنت تقوم بإجراء استدلال قياسي باستخدام model.predict() وإذا لم تكن هناك عقبة في مرحلة المعالجة المسبقة، فإن CPU المعالجة الافتراضي CPU يعمل بشكل جيد.
ملخص سريع
- هل تقوم ببناء مسار DALI؟ استخدم
fn.resize(mode="not_larger")+fn.crop(out_of_bounds_policy="pad")+fn.crop_mirror_normalizeلتكرار عملية المعالجة المسبقة لنظام "ليتربوكس" YOLO على GPU. - التكامل مع Ultralytics؟ قم بتمرير خرج DALI كـ
torch.Tensorإلىmodel.predict()— Ultralytics مرحلة المعالجة المسبقة للصور تلقائيًا. - هل تستخدم Triton؟ استخدم الخلفية DALI مع TensorRT لإجراءCPU .
لماذا نستخدم DALI في YOLO
في مسار YOLO النموذجي YOLO ، تُنفَّذ خطوات المعالجة المسبقة على وحدة CPU:
- فك تشفير الصورة (JPEG/PNG)
- تغيير الحجم مع الحفاظ على نسبة العرض إلى الارتفاع
- تعديل الحجم ليتناسب مع الحجم المستهدف (صندوق البريد)
- تطبيع قيم البكسل من
[0, 255]إلى[0, 1] - تحويل التخطيط من HWC إلى CHW
مع DALI، تُنفَّذ جميع هذه العمليات على وحدة معالجة GPU، مما يزيل CPU . ويكون هذا مفيدًا بشكل خاص في الحالات التالية:
| السيناريو | لماذا تساعد DALI |
|---|---|
| GPU سريع باستخدام و GPU معالجة GPU | TensorRT المحركات ذات الاستدلال الذي يستغرق أقل من مللي ثانية تجعل CPU هي التكلفة الرئيسية |
| مدخلات عالية الدقة | تتطلب عمليات بث الفيديو بدقة 1080p و4K عمليات تغيير الحجم المكلفة |
| كميات كبيرة | معالجة الاستدلال من جانب الخادم للعديد من الصور بشكل متوازٍ |
| عدد محدود من CPU | الأجهزة الطرفية مثل NVIDIA ، أو GPU عالية الكثافة التي تحتوي على عدد قليل من CPU لكل GPU |
المتطلبات الأساسية
لنظام لينكس فقط
يدعم NVIDIA نظام Linux فقط. وهو غير متوفر على أنظمة Windows أو macOS.
قم بتثبيت الحزم المطلوبة:
pip install ultralytics
pip install --extra-index-url https://pypi.nvidia.com nvidia-dali-cuda120
pip install ultralytics
pip install --extra-index-url https://pypi.nvidia.com nvidia-dali-cuda110
المتطلبات:
- GPU NVIDIA GPU قدرة حسابية 5.0+ / معالج Maxwell أو أحدث)
- CUDA .0 أو أعلى أو 12.0 أو أعلى
- Python .10-3.14
- نظام التشغيل لينكس
فهم YOLO
قبل إنشاء مسار DALI، من المفيد فهم ما Ultralytics بالضبط خلال مرحلة المعالجة المسبقة. الفئة الرئيسية هي LetterBox في ultralytics/data/augment.py:
from ultralytics.data.augment import LetterBox
letterbox = LetterBox(
new_shape=(640, 640), # Target size
center=True, # Center the image (pad equally on both sides)
stride=32, # Stride alignment
padding_value=114, # Gray padding (114, 114, 114)
)
مسار المعالجة المسبقة الكامل في ultralytics/engine/predictor.py تقوم بتنفيذ الخطوات التالية:
| خطوة | التشغيل | CPU | ما يعادل DALI |
|---|---|---|---|
| 1 | تغيير حجم صندوق البريد | cv2.resize | fn.resize(mode="not_larger") |
| 2 | تباعد مركزي | cv2.copyMakeBorder | fn.crop(out_of_bounds_policy="pad") |
| 3 | BGR → RGB | im[..., ::-1] | fn.decoders.image(output_type=types.RGB) |
| 4 | HWC → CHW + تطبيع /255 | np.transpose + tensor / 255 | fn.crop_mirror_normalize(std=[255,255,255]) |
تحافظ عملية "صندوق البريد" على نسبة العرض إلى الارتفاع من خلال:
- مقياس الحوسبة:
r = min(target_h / h, target_w / w) - تغيير الحجم إلى
(round(w * r), round(h * r)) - ملء المساحة المتبقية باللون الرمادي (
114) للوصول إلى الحجم المستهدف - توسيط الصورة بحيث يتم توزيع المسافات البادئة بالتساوي على كلا الجانبين
خط أنابيب DALI لـ YOLO
استخدم مسار العمل الموضح أدناه كمرجع افتراضي. وهو يتوافق مع Ultralytics LetterBox(center=True) السلوك، وهو ما يستخدمه YOLO القياسي YOLO .
خط أنابيب مركزي (موصى به، يتوافق مع Ultralytics )
تحاكي هذه النسخة بالضبط عملية Ultralytics الافتراضية Ultralytics باستخدام الحشو المركزي والمطابقة LetterBox(center=True):
خط أنابيب DALI مع تباعد مركزي (موصى به)
import nvidia.dali as dali
import nvidia.dali.fn as fn
import nvidia.dali.types as types
@dali.pipeline_def(batch_size=8, num_threads=4, device_id=0)
def yolo_dali_pipeline_centered(image_dir, target_size=640):
"""DALI pipeline replicating YOLO preprocessing with centered padding.
Matches Ultralytics LetterBox(center=True) behavior exactly.
"""
# Read and decode images on GPU
jpegs, _ = fn.readers.file(file_root=image_dir, random_shuffle=False, name="Reader")
images = fn.decoders.image(jpegs, device="mixed", output_type=types.RGB)
# Aspect-ratio-preserving resize
resized = fn.resize(
images,
resize_x=target_size,
resize_y=target_size,
mode="not_larger",
interp_type=types.INTERP_LINEAR,
antialias=False, # Match cv2.INTER_LINEAR (no antialiasing)
)
# Centered padding using fn.crop with out_of_bounds_policy
# When crop size > image size, fn.crop centers the image and pads symmetrically
padded = fn.crop(
resized,
crop=(target_size, target_size),
out_of_bounds_policy="pad",
fill_values=114, # YOLO padding value
)
# Normalize and convert layout
output = fn.crop_mirror_normalize(
padded,
dtype=types.FLOAT,
output_layout="CHW",
mean=[0.0, 0.0, 0.0],
std=[255.0, 255.0, 255.0],
)
return output
متى fn.pad هل هذا كافٍ؟
إذا لم تكن بحاجة إلى الرقم الدقيق LetterBox(center=True) التكافؤ، يمكنك تبسيط خطوة الحشو باستخدام fn.pad(...) بدلاً من fn.crop(..., out_of_bounds_policy="pad"). هذا النوع من المتغيرات يملأ فقط على اليمين وفي الأسفل الحواف، وهو ما قد يكون مقبولاً في مسارات النشر المخصصة، لكنه لن يتطابق تماماً مع سلوك "ليتربوكس" المركّز افتراضيّاً Ultralytics.
لماذا fn.crop للحشو المركزي؟
دالي fn.pad تقوم هذه العملية بإضافة مسافة فارغة إلى على اليمين وفي الأسفل الحواف. للحصول على تباعد داخلي مركزي (مطابق لـ Ultralytics LetterBox(center=True))، استخدم fn.crop مع out_of_bounds_policy="pad". مع الإعداد الافتراضي crop_pos_x=0.5 و crop_pos_y=0.5، يتم توسيط الصورة تلقائيًا مع توزيع متماثل على الجوانب.
عدم تطابق تنعيم الحواف
دالي fn.resize يقوم بتفعيل ميزة تنعيم الحواف بشكل افتراضي (antialias=True)، في حين أن OpenCV cv2.resize مع INTER_LINEAR هل لا تطبيق تنعيم الحواف. اضبط دائمًا antialias=False في DALI لتتوافق مع CPU . ويؤدي تجاهل ذلك إلى اختلافات رقمية طفيفة قد تؤثر على دقة النموذج.
تشغيل خط الأنابيب
إنشاء وتشغيل مسار DALI
# Build and run the pipeline
pipe = yolo_dali_pipeline_centered(image_dir="/path/to/images", target_size=640)
pipe.build()
# Get a batch of preprocessed images
(output,) = pipe.run()
# Convert to numpy or PyTorch tensors
batch_np = output.as_cpu().as_array() # Shape: (batch_size, 3, 640, 640)
print(f"Output shape: {batch_np.shape}, dtype: {batch_np.dtype}")
print(f"Value range: [{batch_np.min():.4f}, {batch_np.max():.4f}]")
استخدام DALI مع Ultralytics
يمكنك تمرير ملف تمت معالجته مسبقًا PyTorch tensor إلى model.predict(). عندما torch.Tensor تم تمريرها، Ultralytics يتخطى معالجة الصور المسبقة (تحديد نطاق الألوان، وتحويل BGR إلى RGB، وتحويل HWC إلى CHW، وتوحيد القيم إلى /255) ولا تقوم إلا بعملية نقل البيانات بين الأجهزة وتحويل أنواع البيانات قبل إرسالها إلى النموذج.
نظرًا لأن Ultralytics يمكنها الوصول إلى أبعاد الصورة الأصلية في هذه الحالة، يتم إرجاع إحداثيات مربع الكشف ضمن مساحة 640×640 مع حواف سوداء. لتحويلها مرة أخرى إلى إحداثيات الصورة الأصلية، استخدم scale_boxes التي تتولى منطق التقريب الدقيق الذي تستخدمه LetterBox:
from ultralytics.utils.ops import scale_boxes
# boxes: tensor of shape (N, 4) in xyxy format, in 640x640 letterboxed coords
# Scale boxes from letterboxed (640, 640) back to original (orig_h, orig_w)
boxes = scale_boxes((640, 640), boxes, (orig_h, orig_w))
وينطبق هذا على جميع مسارات المعالجة المسبقة الخارجية — tensor المباشر tensor وتدفقات الفيديو، Triton .
Ultralytics DALI + Ultralytics
from nvidia.dali.plugin.pytorch import DALIGenericIterator
from ultralytics import YOLO
# Load model
model = YOLO("yolo26n.pt")
# Create DALI iterator
pipe = yolo_dali_pipeline_centered(image_dir="/path/to/images", target_size=640)
pipe.build()
dali_iter = DALIGenericIterator(pipe, ["images"], reader_name="Reader")
# Run inference with DALI-preprocessed tensors
for batch in dali_iter:
images = batch[0]["images"] # Already on GPU, shape (B, 3, 640, 640)
results = model.predict(images, verbose=False)
for result in results:
print(f"Detected {len(result.boxes)} objects")
لا توجد تكلفة إضافية للمعالجة المسبقة
عندما تمر بـ torch.Tensor إلى model.predict()، تستغرق خطوة المعالجة المسبقة للصورة حوالي 0.004 مللي ثانية (أي تقريبًا صفر) مقارنةً بحوالي 1-10 مللي ثانية عند استخدام CPU . tensor يكون tensor بتنسيق BCHW، وذو دقة float32 (أو float16)، ومُعَدَّلًا إلى [0, 1]. Ultralytics تتولى عملية نقل الأجهزة وتحويل أنواع البيانات تلقائيًا.
DALI مع تدفقات الفيديو
لمعالجة الفيديو في الوقت الفعلي، استخدم fn.external_source لتغذية الإطارات من أي مصدر — OpenCVأو GStreamer أو مكتبات التقاط مخصصة:
مسار DALI لمعالجة الفيديو الأولية
import nvidia.dali as dali
import nvidia.dali.fn as fn
import nvidia.dali.types as types
@dali.pipeline_def(batch_size=1, num_threads=4, device_id=0)
def yolo_video_pipeline(target_size=640):
"""DALI pipeline for processing video frames from external source."""
# External source for feeding frames from OpenCV, GStreamer, etc.
frames = fn.external_source(device="cpu", name="input")
frames = fn.reshape(frames, layout="HWC")
# Move to GPU and preprocess
frames_gpu = frames.gpu()
resized = fn.resize(
frames_gpu,
resize_x=target_size,
resize_y=target_size,
mode="not_larger",
interp_type=types.INTERP_LINEAR,
antialias=False,
)
padded = fn.crop(
resized,
crop=(target_size, target_size),
out_of_bounds_policy="pad",
fill_values=114,
)
output = fn.crop_mirror_normalize(
padded,
dtype=types.FLOAT,
output_layout="CHW",
mean=[0.0, 0.0, 0.0],
std=[255.0, 255.0, 255.0],
)
return output
import cv2
import numpy as np
import torch
from ultralytics import YOLO
model = YOLO("yolo26n.engine") # TensorRT model
pipe = yolo_video_pipeline(target_size=640)
pipe.build()
cap = cv2.VideoCapture("video.mp4")
while cap.isOpened():
ret, frame = cap.read()
if not ret:
break
# Feed BGR frame (convert to RGB for DALI)
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
pipe.feed_input("input", [np.array(frame_rgb)])
(output,) = pipe.run()
# Convert DALI output to torch tensor for inference.
# This is a simple fallback path: using feed_input() with pipe.run() keeps a GPU->CPU->GPU copy.
# For high-throughput deployments, prefer a reader-based pipeline plus DALIGenericIterator to keep data on GPU.
tensor = torch.tensor(output.as_cpu().as_array()).to("cuda")
results = model.predict(tensor, verbose=False)
خادم Triton المزود بـ DALI
لنشر الإنتاج، قم بدمج المعالجة المسبقة لـ DALI مع TensorRT في Triton Server باستخدام نموذج تجميعي. وهذا يلغي CPU تمامًا — حيث يتم إدخال بايتات JPEG الخام، ويخرج الكشف، مع معالجة كل شيء على وحدة GPU.
هيكل مستودع النماذج
model_repository/
├── dali_preprocessing/
│ ├── 1/
│ │ └── model.dali
│ └── config.pbtxt
├── yolo_trt/
│ ├── 1/
│ │ └── model.plan
│ └── config.pbtxt
└── ensemble_dali_yolo/
├── 1/ # Empty directory (required by Triton)
└── config.pbtxt
الخطوة 1: إنشاء مسار DALI
تحويل مسار DALI إلى صيغة متسلسلة من أجل الخلفية Triton :
تحويل مسار DALI إلى صيغة متسلسلة من أجل Triton
import nvidia.dali as dali
import nvidia.dali.fn as fn
import nvidia.dali.types as types
@dali.pipeline_def(batch_size=8, num_threads=4, device_id=0)
def triton_dali_pipeline():
"""DALI preprocessing pipeline for Triton deployment."""
# Input: raw encoded image bytes from Triton
images = fn.external_source(device="cpu", name="DALI_INPUT_0")
images = fn.decoders.image(images, device="mixed", output_type=types.RGB)
resized = fn.resize(
images,
resize_x=640,
resize_y=640,
mode="not_larger",
interp_type=types.INTERP_LINEAR,
antialias=False,
)
padded = fn.crop(
resized,
crop=(640, 640),
out_of_bounds_policy="pad",
fill_values=114,
)
output = fn.crop_mirror_normalize(
padded,
dtype=types.FLOAT,
output_layout="CHW",
mean=[0.0, 0.0, 0.0],
std=[255.0, 255.0, 255.0],
)
return output
# Serialize pipeline to model repository
pipe = triton_dali_pipeline()
pipe.serialize(filename="model_repository/dali_preprocessing/1/model.dali")
الخطوة 2: تصدير YOLO TensorRT
تصدير YOLO إلى TensorRT
from ultralytics import YOLO
model = YOLO("yolo26n.pt")
model.export(format="engine", imgsz=640, half=True, batch=8)
# Copy the .engine file to model_repository/yolo_trt/1/model.plan
الخطوة 3: تهيئة Triton
dali_preprocessing/config.pbtxt:
name: "dali_preprocessing"
backend: "dali"
max_batch_size: 8
input [
{
name: "DALI_INPUT_0"
data_type: TYPE_UINT8
dims: [ -1 ]
}
]
output [
{
name: "DALI_OUTPUT_0"
data_type: TYPE_FP32
dims: [ 3, 640, 640 ]
}
]
yolo.pbtxt:
name: "yolo_trt"
platform: "tensorrt_plan"
max_batch_size: 8
input [
{
name: "images"
data_type: TYPE_FP32
dims: [ 3, 640, 640 ]
}
]
output [
{
name: "output0"
data_type: TYPE_FP32
dims: [ 300, 6 ]
}
]
ensemble_dali_yolo/config.pbtxt:
name: "ensemble_dali_yolo"
platform: "ensemble"
max_batch_size: 8
input [
{
name: "INPUT"
data_type: TYPE_UINT8
dims: [ -1 ]
}
]
output [
{
name: "OUTPUT"
data_type: TYPE_FP32
dims: [ 300, 6 ]
}
]
ensemble_scheduling {
step [
{
model_name: "dali_preprocessing"
model_version: -1
input_map {
key: "DALI_INPUT_0"
value: "INPUT"
}
output_map {
key: "DALI_OUTPUT_0"
value: "preprocessed_image"
}
},
{
model_name: "yolo_trt"
model_version: -1
input_map {
key: "images"
value: "preprocessed_image"
}
output_map {
key: "output0"
value: "OUTPUT"
}
}
]
}
كيفية عمل "رسم الخرائط الجماعي"
يربط هذا النظام النماذج من خلال tensor الافتراضية. إن output_map القيمة "preprocessed_image" في خطوة DALI تتطابق مع input_map القيمة "preprocessed_image" في TensorRT . وهذه أسماء عشوائية تربط ناتج خطوة ما بمدخلات الخطوة التالية — ولا يلزم أن تتطابق مع أي من tensor الداخلية للنموذج.
الخطوة 4: إرسال طلبات الاستدلال
لماذا tritonclient بدلاً من YOLO(\"http://...\")?
Ultralytics شركة Ultralytics Triton مدمج لـ Triton الذي يتولى المعالجة المسبقة واللاحقة تلقائيًا. ومع ذلك، فإنه لن يعمل مع مجموعة DALI لأن YOLO() يرسل tensor float32 تمت معالجته مسبقًا tensor المجموعة تتوقع بايتات JPEG أولية. استخدم tritonclient مباشرة لمجموعات DALI، و التكامل المدمج لعمليات النشر القياسية التي لا تستخدم DALI.
إرسال الصور إلى Triton
import numpy as np
import tritonclient.http as httpclient
client = httpclient.InferenceServerClient(url="localhost:8000")
# Load image as raw bytes (JPEG/PNG encoded)
image_data = np.fromfile("image.jpg", dtype="uint8")
image_data = np.expand_dims(image_data, axis=0) # Add batch dimension
# Create input
input_tensor = httpclient.InferInput("INPUT", image_data.shape, "UINT8")
input_tensor.set_data_from_numpy(image_data)
# Run inference through the ensemble
result = client.infer(model_name="ensemble_dali_yolo", inputs=[input_tensor])
detections = result.as_numpy("OUTPUT") # Shape: (1, 300, 6) -> [x1, y1, x2, y2, conf, class_id]
# Filter by confidence (no NMS needed — YOLO26 is end-to-end)
detections = detections[0] # First image
detections = detections[detections[:, 4] > 0.25] # Confidence threshold
print(f"Detected {len(detections)} objects")
تجميع صور JPEG
عند إرسال مجموعة من صور JPEG إلى Triton قم بتكملة جميع مصفوفات البايتات المشفرة لتصل إلى نفس الطول (الحد الأقصى لعدد البايتات في المجموعة). Triton أن تكون أشكال المجموعات متجانسة بالنسبة tensor المدخلات.
المهام المدعومة
تعمل المعالجة المسبقة DALI مع جميع YOLO التي تستخدم المعيار LetterBox خط الأنابيب:
| المهمة | مدعوم | ملاحظات |
|---|---|---|
| اكتشاف | ✅ | المعالجة المسبقة القياسية لصندوق البريد |
| التجزئة | ✅ | نفس المعالجة المسبقة المستخدمة في عملية الكشف |
| تقدير الوضع | ✅ | نفس المعالجة المسبقة المستخدمة في عملية الكشف |
| الكشف الموجه (OBB) | ✅ | نفس المعالجة المسبقة المستخدمة في عملية الكشف |
| التصنيف | ❌ | يستخدم تحويلات "تورشفيجن" (القص المركزي)، وليس صيغة "ليتربوكس" |
القيود
- لنظام Linux فقط: لا يدعم DALI نظامي التشغيل Windows أو macOS
- GPU NVIDIA : لا يوجد خيار بديل CPU
- خط الأنابيب الثابت: يتم تحديد بنية خط الأنابيب في وقت الإنشاء ولا يمكن تغييرها ديناميكيًا
fn.padيظهر في اليمين/أسفل فقط: استخدمfn.cropمعout_of_bounds_policy="pad"لتوسيع المسافة البينية في الوسط- لا يوجد وضع "rect": تنتج خطوط أنابيب DALI مخرجات ذات أحجام ثابتة (مثل 640×640).
auto=Trueلا يتم دعم الوضع «rect» الذي ينتج مخرجات ذات أحجام متغيرة (مثل 384×640). لاحظ أنه في حين TensorRT على الرغم من أنه يدعم أشكال الإدخال الديناميكية، فإن مسار DALI ذي الحجم الثابت يتوافق بشكل طبيعي مع محرك ذي حجم ثابت لتحقيق أقصى قدر من الإنتاجية - ذاكرة ذات مثيلات متعددة: باستخدام
instance_groupمعcount> الإصدار 1 في Triton يؤدي إلى ارتفاع استهلاك الذاكرة. استخدم مجموعة المثيلات الافتراضية لنموذج DALI
الأسئلة الشائعة
كيف تقارن سرعة المعالجة المسبقة باستخدام DALI بسرعة CPU باستخدام CPU ؟
تعتمد الفائدة على مسار المعالجة الخاص بك. عندما يكون GPU سريعًا بالفعل مع TensorRT، يمكن أن تصبح CPU التي تستغرق من 2 إلى 10 مللي ثانية هي التكلفة الرئيسية. تعمل DALI على التخلص من هذا العائق من خلال تشغيل المعالجة المسبقة على وحدة GPU. وتظهر أكبر المكاسب مع المدخلات عالية الدقة (1080p، 4K)، وأحجام الدُفعات الكبيرة، والأنظمة ذات عدد محدود CPU لكل GPU.
هل يمكنني استخدام DALI مع PyTorch (وليس TensorRT فقط)؟
نعم. استخدم DALIGenericIterator لإجراء المعالجة المسبقة torch.Tensor المخرجات، ثم قم بتمريرها إلى model.predict(). ومع ذلك، فإن الفائدة من حيث الأداء تكون أكبر مع TensorRT النماذج التي يكون فيها الاستدلال سريعًا للغاية بالفعل، وتصبح CPU هي العائق.
ما الفرق بين fn.pad و fn.crop للتعبئة؟
fn.pad يضيف مسافة بادئة فقط إلى على اليمين وفي الأسفل الحواف. fn.crop مع out_of_bounds_policy="pad" يُركز الصورة ويضيف مسافة تباعد متناظرة من جميع الجوانب، بما يتوافق مع Ultralytics LetterBox(center=True) السلوك.
هل تنتج تقنية DALI نتائج مطابقة تمامًا لتلك الناتجة عن CPU بواسطة CPU ؟
متطابقة تقريبًا. مجموعة antialias=False في fn.resize لتتوافق مع OpenCV cv2.INTER_LINEAR. Minor floating-point differences (< 0.001) may occur due to GPU vs CPU arithmetic, but these have no measurable impact on detection الدقة.
ماذا عنCUDA بديل لـ DALI؟
CUDA هي NVIDIA أخرى NVIDIA لمعالجة الصور GPU. وهي توفر تحكماً لكل عامل (مثل OpenCV (ولكن على GPU) بدلاً من نهج مسار المعالجة الذي تتبعه DALI.CUDA cvcuda.copymakeborder() يدعم الحشو الصريح لكل جانب، مما يجعل إنشاء نافذة العرض المركزية أمرًا سهلاً. اختر DALI لسير العمل القائم على الأنابيب (خاصةً مع Triton)، وCUDA الدقيق على مستوى المشغلات في كود الاستدلال المخصص.