**File:** modules/3d/include/opencv2/3d/volume.hpp
**Change Type:** modified
**Context:** PR #22925: Volume gets Python bindings and examples
**Review Line:** 58

**Code Changes:**
```diff
     * @param pose the pose of camera in global coordinates.
     */
-    void integrate(InputArray depth, InputArray image, InputArray pose);
+    CV_WRAP_AS(integrateColor) void integrate(InputArray depth, InputArray image, InputArray pose);
+
+    // Python bindings do not process noArray() default argument correctly, so the raycast() method is repeated several times
+
+    /** @brief Renders the volume contents into an image. The resulting points and normals are in camera's coordinate system.
+
+    Rendered image size and camera intrinsics are taken from volume settings structure.
+
```

Review comment:
I've found no proof of what you're saying but can prove the opposite. Here's the code generated for `calcHist` wrapper on my machine: <details><summary>calcHist()</summary> <p> ``` PyObject* pyobj_images = NULL; vector_Mat images; PyObject* pyobj_channels = NULL; vector_int channels; PyObject* pyobj_mask = NULL; Mat mask; PyObject* pyobj_hist = NULL; Mat hist; PyObject* pyobj_histSize = NULL; vector_int histSize; PyObject* pyobj_ranges = NULL; vector_float ranges; PyObject* pyobj_accumulate = NULL; bool accumulate=false; const char* keywords[] = { "images", "channels", "mask", "histSize", "ranges", "hist", "accumulate", NULL }; if( PyArg_ParseTupleAndKeywords(py_args, kw, "OOOOO|OO:calcHist", (char**)keywords, &pyobj_images, &pyobj_channels, &pyobj_mask, &pyobj_histSize, &pyobj_ranges, &pyobj_hist, &pyobj_accumulate) && pyopencv_to_safe(pyobj_images, images, ArgInfo("images", 0)) && pyopencv_to_safe(pyobj_channels, channels, ArgInfo("channels", 0)) && pyopencv_to_safe(pyobj_mask, mask, ArgInfo("mask", 0)) && pyopencv_to_safe(pyobj_hist, hist, ArgInfo("hist", 1)) && pyopencv_to_safe(pyobj_histSize, histSize, ArgInfo("histSize", 0)) && pyopencv_to_safe(pyobj_ranges, ranges, ArgInfo("ranges", 0)) && pyopencv_to_safe(pyobj_accumulate, accumulate, ArgInfo("accumulate", 0)) ) { ERRWRAP2(cv::calcHist(images, channels, mask, hist, histSize, ranges, accumulate)); return pyopencv_from(hist); } ``` </p> </details> And here's the code generated for `raycast`: <details><summary>raycast()</summary> <p> ``` PyObject* pyobj_cameraPose = NULL; Mat cameraPose; PyObject* pyobj_points = NULL; Mat points; PyObject* pyobj_normals = NULL; Mat normals; PyObject* pyobj_colors = NULL; Mat colors; const char* keywords[] = { "cameraPose", "points", "normals", "colors", NULL }; if( PyArg_ParseTupleAndKeywords(py_args, kw, "O|OOO:Volume.raycast", (char**)keywords, &pyobj_cameraPose, &pyobj_points, &pyobj_normals, &pyobj_colors) && pyopencv_to_safe(pyobj_cameraPose, cameraPose, ArgInfo("cameraPose", 0)) && pyopencv_to_safe(pyobj_points, points, ArgInfo("points", 1)) && pyopencv_to_safe(pyobj_normals, normals, ArgInfo("normals", 1)) && pyopencv_to_safe(pyobj_colors, colors, ArgInfo("colors", 1)) ) { ERRWRAP2(_self_->raycast(cameraPose, points, normals, colors)); return Py_BuildValue("(NNN)", pyopencv_from(points), pyopencv_from(normals), pyopencv_from(colors)); } ``` </p> </details> As can be seen (and what I've encountered during tests), `None` cannot be translated to `noArray()` since empty `Mat` is never a `noArray()` object. Here in the `ColorTSDF::raycast()` call a distinction should be made between these situations: * A user hasn't passed `colors` argument because they doesn't need color output * A user has passed `colors` argument for output but it's empty yet So we need a proper `noArray()` passing or have to duplicate methods.