
    ^j5                        d Z ddlmZ ddlmZ ddlmZ ddlZddl	m
Z ddlmZ ddlmZ  e       Zddd	Z	 d	 	 	 	 	 	 	 	 	 dd
Z	 d	 	 	 	 	 	 	 ddZ	 	 d	 	 	 	 	 	 	 	 	 	 	 ddZy)u-  ONNX Runtime inference helpers for RF-DETR exported models.

These functions handle session creation, image preprocessing, and detection decoding without requiring PyTorch or the
RF-DETR training stack — only ``onnxruntime``, ``numpy``, ``supervision``, and ``Pillow`` are needed at inference time.
    )annotations)Path)AnyN)Image)
Detections)
get_loggerc                   	 ddl }|Kddg}|j                         }|D cg c]	  }||v s| c}xs dg}|d   dk(  rt        j	                  d       |j                  t        |       |      }t        j                  d|j                                |j                         D ]8  }t        j                  d	|j                  |j                  |j                         : |j                         D ]8  }	t        j                  d
|	j                  |	j                  |	j                         : |S # t        $ r}t        d      |d}~ww xY wc c}w )u^  Load an ONNX model and create an ONNX Runtime inference session.

    Imports ``onnxruntime`` at call time so that the rest of the package remains usable without it installed.  Input and
    output names / shapes are logged at DEBUG level for troubleshooting.

    When ``providers`` is ``None``, the session auto-selects the best available backend: CUDA if ``onnxruntime-gpu`` is
    installed, otherwise CPU (with a warning).  Pass an explicit list to pin the backend — useful for benchmarking
    CPU vs CUDA side-by-side.

    Args:
        model_path: Path to the ``.onnx`` model file.
        providers: Ordered list of ORT execution providers, e.g.
            ``["CUDAExecutionProvider", "CPUExecutionProvider"]``.  When ``None`` (default), the best available
            provider is selected automatically.

    Returns:
        An ``onnxruntime.InferenceSession`` ready for inference.

    Raises:
        ImportError: If ``onnxruntime`` is not installed.

    Examples:
        .. code-block:: python

            sess = _create_onnx_session("model.onnx")
            print(sess.get_inputs()[0].name)
    r   NzTONNX Runtime inference requires 'onnxruntime'. Install it: `pip install onnxruntime`CUDAExecutionProviderCPUExecutionProvideru   CUDAExecutionProvider not available — running ONNX inference on CPU. Install onnxruntime-gpu for GPU acceleration: `pip install onnxruntime-gpu`	providersz!ONNX Runtime providers in use: %sz#Input  : name=%s  shape=%s  type=%sz#Output : name=%s  shape=%s  type=%s)onnxruntimeImportErrorget_available_providersloggerwarningInferenceSessionstrdebugget_providers
get_inputsnameshapetypeget_outputs)

model_pathr   ortexc
_preferred
_availablepsessioninpouts
             h/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/export/_onnx/inference.py_create_onnx_sessionr&      sB   8! -/EF
002
 *>1a:oQ>ZCYBZ	Q<11NN^ ""3z?i"HG
LL4g6K6K6MN!!# [:CHHciiQTQYQYZ[""$ [:CHHciiQTQYQYZ[N)  b
	 ?s!   D+ 	EE+	E4E  Ec                   g d}g d}t        j                  t        |      D cg c]
  }||dz      c}t         j                        }t        j                  t        |      D cg c]
  }||dz      c}t         j                        }|dk(  rdnd}	t        j                  | j	                  |	      j                  ||ft        j                  j                        t         j                        dz  }
|
j                  d	k(  r|
d
d
d
d
t         j                  f   }
|
|z
  |z  }
|
j                  d	dd      }
t        j                  |
d      j                  t         j                        S c c}w c c}w )u  Resize and normalise a PIL image to an ``(1, C, H, W)`` float32 NCHW tensor.

    Resizes using ``BILINEAR`` to match ``torchvision.transforms.functional.resize()`` (PIL's default is ``BICUBIC``
    which produces slightly different values and can lower confidence scores).  Normalises with ImageNet statistics:
    ``mean=[0.485, 0.456, 0.406]``, ``std=[0.229, 0.224, 0.225]``.

    Args:
        image: Input PIL image; any mode — converted to ``"RGB"`` (3-channel) or ``"L"`` (1-channel) internally.
        height: Target spatial height expected by the model.
        width: Target spatial width expected by the model.
        channels: Number of channels the model expects (``1`` for grayscale, ``3`` for RGB).

    Returns:
        Float32 ndarray of shape ``(1, channels, height, width)``.

    Examples:
        .. code-block:: python

            inp = _preprocess_pil_to_nchw(image, height=640, width=640)
    )g
ףp=
?gv/?gCl?)gZd;O?gy&1?g?   dtype   LRGBg     o@   Nr   axis)nparrayrangefloat32convertresizePILImage
ResamplingBILINEARndimnewaxis	transposeexpand_dimsastype)imageheightwidthchannels_imagenet_mean_imagenet_stdimeanstdpil_modearrs              r%   _preprocess_pil_to_nchwrJ   O   s+   4 +N)M88E(ODq^AE*DBJJWD
((%/BQM!a%(B"**
UC!msH 	MM(#**E6?H<O<O<X<XY**	
 		  xx1}!Q

"#:
C
--1a
 C>>#A&--bjj99 EBs   E/'E4c           
        | j                         }| j                         }|d   j                  }|d   j                  \  }}}}	t	        j
                  |      }
t        |
||	|      }| j                  d||i      }|D cg c]  }|j                   }}t        d t        |      D        d      }t        d t        |      D        d      }||t        j                  d|       t        |      D cg c]*  \  }}|j                  dk(  s|j                  d   dk(  s)|, }}}t        |      D cg c]*  \  }}|j                  dk(  s|j                  d   dk7  s)|, }}}t        |      d	k(  rt        |      d	k(  r|d   }|d   }nXt        |      d
k(  rt        j                  d       d}d	}n0|D cg c]  }t        |j                         }}t        d|       ||   d   }||   dddddf   }t        j!                  d|j                  t#        |j%                               t#        |j'                               t#        |j)                                      t+        j,                  d	|j.                        }||t+        j0                  |j3                  dd             z   z  }|j'                  d      }|j5                  d      }t        j!                  dt#        |j%                               t#        |j'                               |t7        ||kD  j9                                      ||kD  }||   j:                  \  }}}} |
j<                  \  }!}"t+        j>                  ||d
z  z
  || d
z  z
  ||d
z  z   || d
z  z   gd	      }#|#t+        j@                  |!|"|!|"gt*        jB                        z  }#tE        |#||   ||   jG                  t6                    |
fS c c}w c c}}w c c}}w c c}w )u  Preprocess one image, run ONNX Runtime inference, and decode detections.

    Reads input shape from the session (NCHW ``float32``), resizes and normalises the image with ImageNet statistics,
    invokes the model, then decodes the ``dets`` / ``labels`` output tensors into a :class:`supervision.Detections`
    object with pixel-space ``xyxy`` boxes.

    **Input contract** (must match ``RFDETR.predict()`` preprocessing exactly):

    - Image is opened as-is and converted to ``"RGB"`` (3-channel) or ``"L"``
      (1-channel greyscale) depending on the model's channel count.
    - Resize uses ``PIL.Image.Resampling.BILINEAR`` — matching
      ``torchvision.transforms.functional.resize()`` which defaults to ``InterpolationMode.BILINEAR``.  Using PIL's
      default (``BICUBIC``) would produce slightly different pixel values and can degrade confidence.
    - Pixel values are scaled to ``[0, 1]`` then normalised with ImageNet
      statistics: ``mean=[0.485, 0.456, 0.406]``, ``std=[0.229, 0.224, 0.225]``.
    - The tensor is kept as ``[1, C, H, W]`` (NCHW) — unlike the TFLite helper
      which uses NHWC because ``onnx2tf`` transposes at export time.  ONNX RT consumes the native ONNX NCHW layout
      directly.

    Args:
        session: ONNX Runtime ``InferenceSession`` returned by
            ``_create_onnx_session``.
        image_path: Path to the input image (any format supported by Pillow).
            RGB images are used as-is; RGBA / palette images are converted.
        threshold: Confidence threshold; detections below this are discarded.

    Returns:
        A tuple of ``(detections, pil_img)`` where ``detections`` contains pixel-space ``xyxy`` boxes and ``pil_img`` is
        the original PIL image at its original resolution.

    Examples:
        .. code-block:: python

            sess = _create_onnx_session("model.onnx")
            dets, img = _run_inference(sess, "photo.jpg", threshold=0.3)
            print(dets.confidence)
    r   Nc              3  0   K   | ]  \  }}d |v s|  yw)detsN .0rE   r   s      r%   	<genexpr>z!_run_inference.<locals>.<genexpr>   s     QGAt&D.aQ   c              3  0   K   | ]  \  }}d |v s|  yw)labelsNrN   rO   s      r%   rQ   z!_run_inference.<locals>.<genexpr>   s     TWQ8tCSqTrR   zcName-based ONNX output matching failed (available names: %s). Falling back to shape-based matching.r(      r+   r.   a2  Shape-based ONNX output matching is ambiguous (both outputs have last dim==4, which happens when num_classes==3).  Falling back to positional order: output 0 = boxes ('dets'), output 1 = logits ('labels').  If detections look wrong, inspect output names with _create_onnx_session() and set LOG_LEVEL=DEBUG.zShape-based ONNX output matching failed. Expected exactly one rank-3 tensor with last dim == 4 (boxes) and one rank-3 tensor with last dim != 4 (logits). Available output shapes: z2Logits stats: shape=%s min=%.3f max=%.3f mean=%.3fr)   iX   r/   uG   Scores stats: min=%.3f max=%.3f — detections above threshold %.2f: %d)xyxy
confidenceclass_id)$r   r   r   r   r7   openrJ   runnext	enumerater   r   r:   lenlist
ValueErrorr   floatminmaxrF   r1   asarrayr*   expclipargmaxintsumTsizestackr2   r4   r   r>   )$r"   
image_path	thresholdinputsoutputs
input_name_rB   r@   rA   pil_img
inp_tensorraw_outputsr$   output_names	boxes_idx
logits_idxrE   arr_outshape_boxes_candidatesshape_logits_candidatesavailable_shapes	boxes_cwhlogitsone
scores_allscoresclskeepcxcybwbhowohrX   s$                                       r%   _run_inferencer   }   s   T !F!!#GJ!'AxmmJ'G(&%JJ++dZ$<=K )00CHH0L0Qy'>QSWXIT	,(?TVZ[JJ.q	

 !*+ 6"
!W',,!:KPWP]P]^`PaefPfA"
 "
 !*+ 6#
!W',,!:KPWP]P]^`PaefPfA#
 #
 %&!+4K0LPQ0Q.q1I03J" NN+ IJCNOW]] 3OO,,<+=?  I&q)I $Q3B3Y/F LL<fjjlfjjlfkkm **Qfll
+Cbfffkk#r&:%:;;<J^^^$F




$C
LLQfjjlfjjlVi$$&' IDt_&&NBB\\FB88R"q&["rAv+rBF{BaKHqQDBHHb"b"%RZZ88D4F4L3t9CSCSTWCXY[bbbK 1"
#
&  Ps0   ?O'5O,O, O,5O2O2 O2:O8c                   ddl }t        | |      }|j                         d   }||d   k7  rt        d|d   d|d      |j	                         d   }|j
                  \  }	}
}}t        ||||
      }|j                  |i}t        |      D ]  }	|j                  d|        g }t        |      D ]I  }	|j                         }|j                  d|       |j                  |j                         |z
  dz         K t        j                  |      }|j                         d   j                  dd	      }t        |j!                               t        |j#                               |fS )
u  Benchmark ONNX Runtime inference for one image and provider list.

    Creates a fresh ``InferenceSession`` with the requested providers, preprocesses ``image`` once using ImageNet
    normalisation, then runs timed inference with ``time.perf_counter``.  GPU timings may underestimate real latency
    if the CUDA execution provider is configured for asynchronous execution; for accurate GPU timing use CUDA events.

    Args:
        onnx_path: Path to the ``.onnx`` model file.
        image: Input image (any size); resized to the model's expected spatial resolution.
        providers: Ordered list of ORT execution providers, e.g.
            ``["CUDAExecutionProvider", "CPUExecutionProvider"]``.
        warmup: Number of un-timed warm-up runs before measurement begins.
        runs: Number of timed runs used to compute statistics.

    Returns:
        A ``(mean_ms, std_ms, provider_label)`` tuple where ``provider_label`` is the first active provider with
        ``"ExecutionProvider"`` stripped, e.g. ``"CUDA"`` or ``"CPU"``.

    Examples:
        .. code-block:: python

            mean_ms, std_ms, label = _onnx_runtime("model.onnx", image, ["CPUExecutionProvider"])
            print(f"{label}: {mean_ms:.1f} ms ± {std_ms:.1f}")
    r   Nr   zRequested provider u!    not active — ORT fell back to z8. Install onnxruntime-gpu: `pip install onnxruntime-gpu`g     @@ExecutionProvider )timer&   r   RuntimeErrorr   r   rJ   r   r3   r\   perf_counterappendr1   r2   replacerb   rF   rG   )	onnx_pathr?   r   warmuprunsr   sessactive
input_metars   rB   r@   rA   r#   feedtimingst0arr_tprovider_labels                      r%   _onnx_runtimer      sn   > 	Y?D!!$F1!)A,!11RSYR\ ]E E
 	
 "1%J!+!1!1Ax
!%
ACOOS!D6] tG4[ < t))+b0F:;< HHWE'')!,445H"MNeiik 2NBB    )N)r   
str | Pathr   zlist[str] | Nonereturnr   )r(   )
r?   PILImage.Imager@   ri   rA   ri   rB   ri   r   z
np.ndarray)g333333?)r"   r   rn   r   ro   rb   r   z!tuple[Detections, PILImage.Image])   d   )r   z
Path | strr?   r   r   z	list[str]r   ri   r   ri   r   ztuple[float, float, str])__doc__
__future__r   pathlibr   typingr   numpyr1   PILr   r7   supervisionr   rfdetr.utilities.loggerr   r   r&   rJ   r   r   rN   r   r%   <module>r      s    #    ! " .	2r 	+:+:+: +: 	+:
 +:b |c|c|c |c '	|cH 6C6C6C 6C 	6C
 6C 6Cr   