Ultralytics YOLO27:

المعالجة المسبقة المسرّعة بوحدة GPU باستخدام NVIDIA DALI#

عند نشر نماذج Ultralytics YOLO في بيئة الإنتاج، غالبًا ما تصبح المعالجة المسبقة عنق الزجاجة. فبينما يمكن لـ TensorRT تنفيذ الاستدلال بالنموذج في بضعة أجزاء من الألف من الثانية، قد تستغرق المعالجة المسبقة المعتمدة على CPU (تغيير الحجم، وإضافة الهوامش، والتطبيع) من 2 إلى 10 مللي ثانية لكل صورة، خصوصًا عند الدقات العالية. تحل NVIDIA DALI (مكتبة تحميل البيانات) هذه المشكلة بنقل مسار المعالجة المسبقة بأكمله إلى GPU.

يرشدك هذا الدليل إلى إنشاء مسارات DALI تطابق بدقة المعالجة المسبقة في Ultralytics YOLO، ودمجها مع model.predict()، ومعالجة تدفقات الفيديو، ونشر الحل من البداية إلى النهاية باستخدام Triton Inference Server.

لمن هذا الدليل؟

هذا الدليل موجّه إلى المهندسين الذين ينشرون نماذج YOLO في بيئات الإنتاج التي ثبت فيها أن المعالجة المسبقة على CPU تمثل عنق زجاجة — وغالبًا ما تكون هذه بيئات نشر TensorRT على وحدات GPU من NVIDIA، أو مسارات فيديو عالية الإنتاجية، أو إعدادات Triton Inference Server. إذا كنت تجري استدلالًا اعتياديًا باستخدام model.predict() ولا تواجه عنق زجاجة في المعالجة المسبقة، فسيؤدي مسار CPU الافتراضي المهمة بكفاءة.

ملخص سريع
  • هل تبني مسار DALI؟ استخدم fn.resize(mode="not_larger") + fn.crop(out_of_bounds_policy="pad") + fn.crop_mirror_normalize لمحاكاة المعالجة المسبقة باستخدام letterbox في YOLO على GPU.
  • هل تدمج الحل مع Ultralytics؟ مرّر مخرجات DALI على هيئة torch.Tensor إلى model.predict() — وستتخطى Ultralytics معالجة الصور المسبقة تلقائيًا.
  • هل تنشر باستخدام Triton؟ استخدم خلفية DALI مع مجموعة نماذج TensorRT لتنفيذ المعالجة المسبقة دون استخدام CPU.

لماذا تستخدم DALI للمعالجة المسبقة في YOLO؟#

في مسار الاستدلال المعتاد في YOLO، تُنفّذ خطوات المعالجة المسبقة على CPU:

  1. فك الترميز للصورة (JPEG/PNG)
  2. تغيير الحجم مع الحفاظ على نسبة العرض إلى الارتفاع
  3. إضافة الهوامش للوصول إلى الحجم المستهدف (letterbox)
  4. تطبيع قيم البكسل من [0, 255] إلى [0, 1]
  5. تحويل تنسيق البيانات من HWC إلى CHW

باستخدام DALI، تُنفّذ جميع هذه العمليات على GPU، ما يزيل عنق الزجاجة المرتبط بـ CPU. وتكون هذه الميزة قيّمة على وجه الخصوص في الحالات التالية:

السيناريومزايا DALI
استدلال سريع على GPUتجعل محركات TensorRT ذات زمن الاستدلال الذي يقل عن مللي ثانية المعالجة المسبقة على CPU التكلفة الأكبر
مدخلات عالية الدقةتتطلب تدفقات الفيديو بدقة 1080p و4K عمليات مكلفة لتغيير الحجم
أحجام دفعات كبيرةاستدلال على الخادم يعالج صورًا كثيرة بالتوازي
عدد محدود من أنوية CPUأجهزة طرفية مثل NVIDIA Jetson، أو خوادم GPU كثيفة التجهيز لا يتوفر فيها سوى عدد قليل من أنوية CPU لكل GPU

المتطلبات الأساسية#

Linux فقط

تدعم NVIDIA DALI نظام Linux فقط. وهي غير متاحة على Windows أو macOS.

ثبّت الحزم المطلوبة:

pip install ultralytics
pip install --extra-index-url https://pypi.nvidia.com nvidia-dali-cuda130

المتطلبات:

  • وحدة GPU من NVIDIA (قدرة حوسبة 5.0+ / Maxwell أو أحدث)
  • CUDA 11.0+ أو 12.0+ أو 13.0+
  • Python 3.10-3.14
  • نظام تشغيل Linux

فهم المعالجة المسبقة في YOLO#

قبل إنشاء مسار DALI، يجدر فهم ما تنفّذه Ultralytics تحديدًا أثناء المعالجة المسبقة. الفئة الأساسية هي LetterBox في ultralytics/data/augment.py:

from ultralytics.data.augment import LetterBox

letterbox = LetterBox(
    new_shape=(640, 640),  # ⁨الحجم المستهدف⁩
    center=True,  # ⁨توسيط الصورة (إضافة هوامش متساوية من الجانبين)⁩
    stride=32,  # ⁨المحاذاة مع مقدار الخطوة⁩
    padding_value=114,  # ⁨هوامش رمادية (114, 114, 114)⁩
)

ينفّذ مسار المعالجة المسبقة الكامل في ultralytics/engine/predictor.py الخطوات التالية:

الخطوةالعمليةدالة CPUالمكافئ في DALI
1تغيير الحجم باستخدام letterboxcv2.resizefn.resize(mode="not_larger")
2إضافة هوامش في الوسطcv2.copyMakeBorderfn.crop(out_of_bounds_policy="pad")
3BGR → RGBim[..., ::-1]fn.decoders.image(output_type=types.RGB)
4HWC → CHW + التطبيع /255np.transpose + tensor / 255fn.crop_mirror_normalize(std=[255,255,255])

تحافظ عملية letterbox على نسبة العرض إلى الارتفاع من خلال:

  1. حساب معامل القياس: r = min(target_h / h, target_w / w)
  2. تغيير الحجم إلى (round(w * r), round(h * r))
  3. إضافة هوامش رمادية (114) إلى المساحة المتبقية للوصول إلى الحجم المستهدف
  4. توسيط الصورة بحيث تتوزع الهوامش بالتساوي على الجانبين

مسار DALI لـ YOLO#

يحاكي مسار DALI الموصى به سلوك LetterBox(center=True) الافتراضي في Ultralytics، وهو السلوك المستخدم في استدلال YOLO الاعتيادي.

المسار المتمركز (موصى به، ويطابق LetterBox في Ultralytics)#

تحاكي هذه النسخة المعالجة المسبقة الافتراضية في Ultralytics بدقة، مع إضافة هوامش في الوسط، بما يطابق LetterBox(center=True):

مسار DALI مع إضافة هوامش في الوسط (موصى به)
from nvidia import dali
from nvidia.dali import fn, 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.
    """
    # ⁨قراءة الصور وفك ترميزها على 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)

    # ⁨تغيير الحجم مع الحفاظ على نسبة العرض إلى الارتفاع⁩
    resized = fn.resize(
        images,
        resize_x=target_size,
        resize_y=target_size,
        mode="not_larger",
        interp_type=types.INTERP_LINEAR,
        antialias=False,  # ⁨مطابقة cv2.INTER_LINEAR (من دون تنعيم الحواف)⁩
    )

    # ⁨إضافة هوامش في الوسط باستخدام fn.crop مع out_of_bounds_policy⁩
    # ⁨عندما يكون حجم القص أكبر من حجم الصورة، يوسّط fn.crop الصورة ويضيف هوامش متماثلة⁩
    padded = fn.crop(
        resized,
        crop=(target_size, target_size),
        out_of_bounds_policy="pad",
        fill_values=114,  # ⁨قيمة الهوامش في YOLO⁩
    )

    # ⁨التطبيع وتحويل تنسيق البيانات⁩
    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"). يضيف هذا الخيار الهوامش إلى الحافتين اليمنى والسفلية فقط، وقد يكون ذلك مقبولًا لمسارات النشر المخصصة، لكنه لن يطابق تمامًا سلوك letterbox الافتراضي في Ultralytics، الذي يضيف الهوامش في الوسط.

لماذا نستخدم `fn.crop` لإضافة الهوامش في الوسط؟

لا يضيف العامل fn.pad في DALI الهوامش إلا إلى الحافتين اليمنى والسفلية. لإضافة الهوامش في الوسط (بما يطابق LetterBox(center=True) في Ultralytics)، استخدم fn.crop مع out_of_bounds_policy="pad". عند استخدام crop_pos_x=0.5 وcrop_pos_y=0.5 الافتراضيين، تُوسّط الصورة تلقائيًا مع إضافة هوامش متماثلة.

اختلاف التنعيم

يفعّل fn.resize في DALI التنعيم افتراضيًا (antialias=True)، بينما لا يطبّق cv2.resize في OpenCV مع 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](https://ultralytics-translation-0.invalid معالج مسبقًا مباشرةً إلى model.predict(). عند تمرير torch.Tensor، تتخطى Ultralytics المعالجة المسبقة للصورة (letterbox، وBGR→RGB، وHWC→CHW، والتطبيع /255)، ولا تنفّذ سوى نقل البيانات إلى الجهاز وتحويل نوع البيانات قبل إرسالها إلى النموذج.

نظرًا إلى أن Ultralytics لا يمكنها الوصول إلى أبعاد الصورة الأصلية في هذه الحالة، تُعاد إحداثيات مربعات الكشف ضمن مساحة الصورة 640×640 بعد تطبيق letterbox. لإعادتها إلى إحداثيات الصورة الأصلية، استخدم scale_boxes، الذي يتعامل مع منطق التقريب الدقيق المستخدم في LetterBox:

from ultralytics.utils.ops import scale_boxes

# ⁨المربعات: موتر بالشكل (N, 4) بتنسيق xyxy، وإحداثياتها ضمن مساحة 640x640 بعد تطبيق letterbox⁩
# ⁨تحجيم المربعات من مساحة letterbox (640, 640) إلى أبعاد الصورة الأصلية (orig_h, orig_w)⁩
boxes = scale_boxes((640, 640), boxes, (orig_h, orig_w))

ينطبق ذلك على جميع مسارات المعالجة المسبقة الخارجية — إدخال الموتر مباشرةً، وتدفقات الفيديو، والنشر باستخدام Triton.

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.004ms (أي ما يقارب الصفر)، مقارنةً بنحو ~1-10ms عند استخدام المعالجة المسبقة على CPU. يجب أن يكون الموتر بتنسيق BCHW، ومن النوع float32 (أو float16)، ومطبّعًا إلى [0, 1]. وستظل Ultralytics تتولى تلقائيًا نقل البيانات إلى الجهاز وتحويل نوع البيانات.

DALI مع تدفقات الفيديو#

لمعالجة الفيديو في الوقت الفعلي، استخدم fn.external_source لإدخال الإطارات من أي مصدر — OpenCV، أو GStreamer، أو مكتبات الالتقاط المخصصة:

مسار DALI للمعالجة المسبقة لتدفق الفيديو
from nvidia import dali
from nvidia.dali import fn, 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."""
    # ⁨مصدر خارجي لإدخال الإطارات من OpenCV وGStreamer وغيرهما.⁩
    frames = fn.external_source(device="cpu", name="input")
    frames = fn.reshape(frames, layout="HWC")

    # ⁨النقل إلى GPU والمعالجة المسبقة⁩
    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

Triton Inference Server مع DALI#

للنشر في بيئة الإنتاج، ادمج المعالجة المسبقة باستخدام DALI مع الاستدلال باستخدام TensorRT في خادم Triton للاستدلال باستخدام نموذج تجميعي. يؤدي ذلك إلى إلغاء المعالجة المسبقة على 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:

تسلسل مسار DALI لاستخدامه مع Triton
from nvidia import dali
from nvidia.dali import fn, types

@dali.pipeline_def(batch_size=8, num_threads=4, device_id=0)
def triton_dali_pipeline():
    """DALI preprocessing pipeline for Triton deployment."""
    # ⁨الإدخال: بايتات الصور المشفّرة الخام من 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

# ⁨تسلسل المسار إلى مستودع النماذج⁩
pipe = triton_dali_pipeline()
pipe.serialize(filename="model_repository/dali_preprocessing/1/model.dali")

الخطوة 2: تصدير YOLO إلى TensorRT#

تصدير نموذج YOLO إلى محرك TensorRT
from pathlib import Path

from ultralytics import YOLO

model = YOLO("yolo26n.pt")
engine_path = model.export(
    format="engine", imgsz=640, quantize=16, batch=8, dynamic=True, nms=False
)  # ⁨خالٍ من NMS ‏(N, 300, 6)؛ TensorRT >= 8.5⁩

# ⁨تضيف Ultralytics ترويسة بيانات وصفية إلى ملفات .engine؛ أزلها كي يتمكن Triton من تحميل خطة TensorRT الخام⁩
with open(engine_path, "rb") as f:
    meta_len = int.from_bytes(f.read(4), byteorder="little")  # ⁨طول ترويسة بيانات JSON الوصفية⁩
    f.seek(4 + meta_len)
    plan = f.read()
Path("model_repository/yolo_trt/1").mkdir(parents=True, exist_ok=True)
Path("model_repository/yolo_trt/1/model.plan").write_bytes(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_trt/config.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"
      }
    }
  ]
}
آلية عمل تعيين النموذج التجميعي

يربط النموذج التجميعي النماذج عبر أسماء موترات افتراضية. تتطابق القيمة output_map مع "preprocessed_image" في خطوة DALI مع القيمة input_map مع "preprocessed_image" في خطوة TensorRT. هذه أسماء اعتباطية تربط مخرجات إحدى الخطوات بمدخلات الخطوة التالية — ولا يلزم أن تطابق أسماء الموترات الداخلية لأي نموذج.

الخطوة 4: إرسال طلبات الاستدلال#

لماذا نستخدم `tritonclient` بدلًا من `YOLO('http://...')`؟

تتضمن Ultralytics دعمًا مدمجًا لـ Triton يتولى المعالجة المسبقة واللاحقة تلقائيًا. لكن هذا الدعم لا يعمل مع النموذج التجميعي لـ DALI، لأن YOLO() يرسل موترًا من النوع float32 تمت معالجته مسبقًا، بينما يتوقع النموذج التجميعي بايتات 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 for the nms=False export)
detections = detections[0]  # First image
detections = detections[detections[:, 4] > 0.25]  # Confidence threshold
print(f"Detected {len(detections)} objects")
تجميع صور JPEG

عند إرسال مجموعة من صور JPEG إلى Triton، أضف حشوًا إلى جميع مصفوفات البايتات المشفّرة لتوحيد أطوالها (بحيث يساوي كل طول أكبر عدد بايتات في المجموعة). يتطلب Triton أن تكون أشكال دفعة موتر الإدخال متجانسة.

المهام المدعومة#

تعمل المعالجة المسبقة باستخدام DALI مع جميع مهام YOLO التي تستخدم مسار LetterBox القياسي:

المهمةمدعومملاحظات
الكشف✅معالجة مسبقة قياسية باستخدام letterbox
تجزئة المثيلات✅المعالجة المسبقة نفسها المستخدمة للكشف
التجزئة الدلالية✅معالجة الصور المسبقة نفسها المستخدمة للكشف
تقدير العمق✅معالجة الصور المسبقة نفسها المستخدمة للكشف
التصنيف❌يستخدم تحويلات torchvision (اقتصاصًا مركزيًا)، وليس letterbox
تقدير الوضعيات✅المعالجة المسبقة نفسها المستخدمة للكشف
الكشف الموجّه (OBB)✅المعالجة المسبقة نفسها المستخدمة للكشف

القيود#

  • Linux فقط: لا يدعم DALI نظامَي Windows أو macOS
  • يلزم وجود NVIDIA GPU: لا يتوفر بديل يعمل على CPU فقط
  • مسار ثابت: تُحدَّد بنية المسار عند إنشائه ولا يمكن تغييرها ديناميكيًا
  • يضيف fn.pad الحشو إلى اليمين والأسفل فقط: استخدم fn.crop مع out_of_bounds_policy="pad" لتوسيط الحشو
  • لا يتوفر وضع rect: تنتج مسارات DALI مخرجات بأبعاد ثابتة (مثل 640×640). وضع rect في auto=True، الذي ينتج مخرجات متغيرة الأبعاد (مثل 384×640)، غير مدعوم. تجدر الإشارة إلى أن TensorRT يدعم أشكال إدخال ديناميكية، لكن إقران مسار DALI ثابت الأبعاد بمحرك ثابت الأبعاد يتيح تحقيق أقصى معدل نقل
  • الذاكرة عند استخدام مثيلات متعددة: قد يؤدي استخدام instance_group مع count > 1 في Triton إلى استهلاك مرتفع للذاكرة. استخدم مجموعة المثيلات الافتراضية لنموذج DALI

الأسئلة الشائعة#

  • تعتمد الفائدة على مسار المعالجة لديك. عندما يكون الاستدلال على GPU سريعًا بالفعل باستخدام TensorRT، قد تصبح المعالجة المسبقة على CPU، التي تستغرق 2-10ms، العامل الأكبر في زمن المعالجة. يزيل DALI هذا الاختناق بإجراء المعالجة المسبقة على GPU. وتظهر أكبر المكاسب مع المدخلات عالية الدقة (1080p و4K)، وأحجام الدفعات الكبيرة، والأنظمة التي تحتوي على عدد محدود من أنوية CPU لكل GPU.

  • نعم. استخدم DALIGenericIterator للحصول على مخرجات torch.Tensor بعد معالجتها مسبقًا، ثم مرّرها إلى model.predict(). لكن أكبر فائدة للأداء تتحقق مع نماذج TensorRT، حيث يكون الاستدلال سريعًا بالفعل وتصبح المعالجة المسبقة على CPU عنق الزجاجة.

  • يضيف fn.pad حشوًا إلى الحافتين اليمنى والسفلية فقط. أما fn.crop مع out_of_bounds_policy="pad" فيوسّط الصورة ويضيف حشوًا متماثلًا إلى جميع الجوانب، بما يطابق سلوك LetterBox(center=True) في Ultralytics.

  • النتائج متطابقة تقريبًا. اضبط antialias=False في fn.resize لمطابقة cv2.INTER_LINEAR في OpenCV. قد تظهر فروق طفيفة في الفاصلة العائمة (< 0.001) بسبب اختلاف العمليات الحسابية على GPU وCPU، لكنها لا تؤثر بشكل قابل للقياس في دقة الكشف.

  • CV-CUDA مكتبة أخرى من NVIDIA لمعالجة الرؤية المعزّزة بـ GPU. وهي تتيح التحكم بكل مُشغّل على حدة (مثل OpenCV، ولكن على GPU)، بدلًا من نهج المسارات الذي يتبعه DALI. يدعم cvcuda.copymakeborder() في CV-CUDA تحديد الحشو لكل جانب على حدة، ما يجعل توسيط letterbox مباشرًا. اختر DALI لسير العمل القائم على المسارات (خصوصًا مع Triton)، واختر CV-CUDA للتحكم الدقيق على مستوى المُشغّلات في شيفرة الاستدلال المخصصة.

التعليقات