Skip to content

Fix Python Scalar typing issue #27528#27620

Merged
asmorkalov merged 3 commits intoopencv:4.xfrom
killerdevildog:fix-scalar-typing-issue-27528
Sep 10, 2025
Merged

Fix Python Scalar typing issue #27528#27620
asmorkalov merged 3 commits intoopencv:4.xfrom
killerdevildog:fix-scalar-typing-issue-27528

Conversation

@killerdevildog
Copy link
Copy Markdown
Contributor

@killerdevildog killerdevildog commented Aug 1, 2025

  • Add ScalarInput and ScalarOutput types for better type safety
  • ScalarInput: Union[Sequence[float], float] for function parameters
  • ScalarOutput: Sequence[float] for function return values
  • Keep original Scalar type for backwards compatibility (deprecated)
  • Add refinement functions to apply new types to specific functions
  • Functions returning scalars now use ScalarOutput (mean, sumElems, trace)
  • Drawing functions now use ScalarInput for color parameters
  • Resolves MyPy compatibility issues with scalar return values
  • Maintains full backwards compatibility

closes #27528

Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

  • I agree to contribute to the project under Apache 2 License.
  • To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
  • [x ] The PR is proposed to the proper branch
  • [x ] There is a reference to the original bug report and related work
  • There is accuracy test, performance test and test data in opencv_extra repository, if applicable
    Patch to opencv_extra has the same branch name.
  • [x ] The feature is well documented and sample code can be built with the project CMake

@VadimLevin
Copy link
Copy Markdown
Contributor

Thank you for contribution.
I believe this approach is suboptimal, as it necessitates manual adjustments for each individual function.
The alternative is to generate function overloads that accept float argument and defining Scalar as:

AliasTypeNode.sequence_("Scalar", PrimitiveTypeNode.float_(),
                            doc="Max sequence length is at most 4"),

instead of

    AliasTypeNode.union_(
        "Scalar",
        items=(SequenceTypeNode("Scalar", PrimitiveTypeNode.float_()),
               PrimitiveTypeNode.float_()),
        doc="Max sequence length is at most 4"
    ),

Example:

@_typing.overload
def line(img: cv2.typing.MatLike, pt1: cv2.typing.Point, pt2: cv2.typing.Point, color: cv2.typing.Scalar, thickness: int = ..., lineType: int = ..., shift: int = ...) -> cv2.typing.MatLike: ...

# HERE GOES float OVERLOAD
@_typing.overload
def line(img: cv2.typing.MatLike, pt1: cv2.typing.Point, pt2: cv2.typing.Point, color: float, thickness: int = ..., lineType: int = ..., shift: int = ...) -> cv2.typing.MatLike: ...

@_typing.overload
def line(img: UMat, pt1: cv2.typing.Point, pt2: cv2.typing.Point, color: cv2.typing.Scalar, thickness: int = ..., lineType: int = ..., shift: int = ...) -> UMat: ...

# HERE GOES float OVERLOAD
@_typing.overload
def line(img: UMat, pt1: cv2.typing.Point, pt2: cv2.typing.Point, color: float, thickness: int = ..., lineType: int = ..., shift: int = ...) -> UMat: ...

Functions returning Scalar should be left as is

@_typing.overload
def mean(src: cv2.typing.MatLike, mask: cv2.typing.MatLike | None = ...) -> cv2.typing.Scalar: ...

@_typing.overload
def mean(src: UMat, mask: UMat | None = ...) -> cv2.typing.Scalar: ...

Addresses issues opencv#27528, opencv#26818, opencv#26826 by implementing the function
overloads solution suggested by VadimLevin in PR opencv#27620.

Changes:
1. predefined_types.py: Change Scalar from Union to Sequence[float]
2. api_refinement.py: Add global function overload generation
3. Generate both Scalar and float overloads for drawing functions

This provides better MyPy compatibility while maintaining backward
compatibility. All existing code continues to work unchanged.

Testing: Comprehensive test suite validates all scenarios pass
@killerdevildog killerdevildog force-pushed the fix-scalar-typing-issue-27528 branch from a9b79c8 to f23ce03 Compare August 12, 2025 06:46
@killerdevildog
Copy link
Copy Markdown
Contributor Author

@VadimLevin

FYI, I've rebased the branch and added a test, followed by a force push.
I'm still familiarizing myself with the codebase, github, and git so please be forgiving of any misunderstanding or sub optimal code submissions, and am eager to contribute. ( but please don't merge if its not up to par, most of these are learning experiences and trying to help with open source )
I reviewed the original issue #27528 which addressed issues #26818 and PR #26826 .
To accelerate my understanding, I consulted GitHub Copilot—please let me know if anything seems off!
I've built the code, and it compiled successfully. The test I added passed after a few iterations of failing.
If you have any further adjustments or feedback, please let me know! I tested it using a python venv, so I hope everything is good to go. let me know what you think.

@VadimLevin
Copy link
Copy Markdown
Contributor

Change is targeting only returned Scalar types:
Before:

@_typing.overload
def mean(src: cv2.typing.MatLike, mask: cv2.typing.MatLike | None = ...) -> cv2.typing.Scalar: ...
@_typing.overload
def mean(src: UMat, mask: UMat | None = ...) ->cv2.typing.Scalar: ...

@_typing.overload
def trace(mtx: cv2.typing.MatLike) -> cv2.typing.Scalar: ...
@_typing.overload
def trace(mtx: UMat) -> cv2.typing.Scalar: ...

@_typing.overload
def sumElems(src: cv2.typing.MatLike) -> cv2.typing.Scalar: ...
@_typing.overload
def sumElems(src: UMat) -> cv2.typing.Scalar: ...

After:

@_typing.overload
def mean(src: cv2.typing.MatLike, mask: cv2.typing.MatLike | None = ...) -> tuple[float, float, float, float]: ...
@_typing.overload
def mean(src: UMat, mask: UMat | None = ...) -> tuple[float, float, float, float]: ...

@_typing.overload
def trace(mtx: cv2.typing.MatLike) -> tuple[float, float, float, float]: ...
@_typing.overload
def trace(mtx: UMat) -> tuple[float, float, float, float]: ...

@_typing.overload
def sumElems(src: cv2.typing.MatLike) -> tuple[float, float, float, float]: ...
@_typing.overload
def sumElems(src: UMat) -> tuple[float, float, float, float]: ...

while Scalar is remained the same alias for union of sequence and single float value:

    AliasTypeNode.union_(
        "Scalar",
        items=(SequenceTypeNode("Scalar", PrimitiveTypeNode.float_()),
               PrimitiveTypeNode.float_()),
        doc="Max sequence length is at most 4"
    ),

@asmorkalov asmorkalov merged commit 1fdff6d into opencv:4.x Sep 10, 2025
50 of 55 checks passed
@asmorkalov asmorkalov mentioned this pull request Sep 26, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: Scalar typing change complicates usage

3 participants