1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 23:33:05 +04:00

Merge branch 4.x

This commit is contained in:
Alexander Smorkalov
2026-03-19 09:19:35 +03:00
67 changed files with 1881 additions and 555 deletions
@@ -8,7 +8,7 @@ from .nodes import (NamespaceNode, FunctionNode, OptionalTypeNode, TypeNode,
ClassProperty, PrimitiveTypeNode, ASTNodeTypeNode,
AggregatedTypeNode, CallableTypeNode, AnyTypeNode,
TupleTypeNode, UnionTypeNode, ProtocolClassNode,
DictTypeNode, ClassTypeNode)
DictTypeNode, ClassTypeNode, AliasRefTypeNode)
from .ast_utils import (find_function_node, SymbolName,
for_each_function_overload)
from .types_conversion import create_type_node
@@ -376,6 +376,62 @@ def _find_argument_index(arguments: Sequence[FunctionNode.Arg],
return None
def make_matlike_or_scalar_arg(*arg_names: str) -> Callable[[NamespaceNode, SymbolName], None]:
"""Make arguments accept both MatLike and Scalar types.
This is used for functions like inRange where the C++ InputArray parameter
can accept both Mat objects and Scalar values (tuples, floats, etc.).
Example: cv2.inRange(img, (0, 0, 0), (255, 255, 255)) should be valid.
"""
def _make_matlike_or_scalar_arg(root_node: NamespaceNode,
function_symbol_name: SymbolName) -> None:
from .predefined_types import PREDEFINED_TYPES
function = find_function_node(root_node, function_symbol_name)
for arg_name in arg_names:
found_overload_with_arg = False
for overload in function.overloads:
arg_idx = _find_argument_index(overload.arguments, arg_name)
# skip overloads without this argument
if arg_idx is None:
continue
current_type = overload.arguments[arg_idx].type_node
# Check if it's already a union or if it already includes Scalar
if isinstance(current_type, UnionTypeNode):
# Check if Scalar is already in the union
has_scalar = any(
isinstance(item, AliasRefTypeNode) and item.typename == "Scalar"
for item in current_type.items
)
if has_scalar:
continue
# Add Scalar to existing union
scalar_ref = AliasRefTypeNode("Scalar")
current_type.items = current_type.items + (scalar_ref,)
else:
# Create a union of current type and Scalar
scalar_ref = AliasRefTypeNode("Scalar")
overload.arguments[arg_idx].type_node = UnionTypeNode(
f"{arg_name}_type",
(cast(TypeNode, current_type), scalar_ref)
)
found_overload_with_arg = True
if not found_overload_with_arg:
raise RuntimeError(
f"Failed to find argument with name: '{arg_name}'"
f" in '{function_symbol_name.name}' overloads"
)
return _make_matlike_or_scalar_arg
NODES_TO_REFINE = {
SymbolName(("cv", ), (), "resize"): make_optional_arg("dsize"),
SymbolName(("cv", ), (), "calcHist"): make_optional_arg("mask"),
@@ -401,6 +457,11 @@ NODES_TO_REFINE = {
SymbolName(("cv", "fisheye"), (), "initUndistortRectifyMap"): make_optional_arg("D"),
SymbolName(("cv", ), (), "imread"): make_optional_none_return,
SymbolName(("cv", ), (), "imdecode"): make_optional_none_return,
SymbolName(("cv", ), (), "HoughCircles"): make_optional_none_return,
SymbolName(("cv", ), (), "HoughLines"): make_optional_none_return,
SymbolName(("cv", ), (), "HoughLinesP"): make_optional_none_return,
# Fix for issue #28534: inRange should accept Scalar for lowerb and upperb
SymbolName(("cv", ), (), "inRange"): make_matlike_or_scalar_arg("lowerb", "upperb"),
}
ERROR_CLASS_PROPERTIES = (
@@ -3,6 +3,7 @@ __all__ = ("generate_typing_stubs", )
from io import StringIO
from pathlib import Path
import re
import shutil
from typing import (Callable, NamedTuple, Union, Set, Dict,
Collection, Tuple, List)
import warnings
@@ -24,6 +25,22 @@ from .nodes.type_node import (TypeNode, AliasTypeNode, AliasRefTypeNode,
ConditionalAliasTypeNode, PrimitiveTypeNode)
def _clean_stale_stubs_dirs(stubs_root: Path) -> None:
"""Remove all subdirectories under stubs_root.
During incremental builds, disabling a previously enabled module leaves
behind its typing stub directory (e.g. cv2/gapi/). Removing all
subdirectories before regeneration ensures only stubs for currently
enabled modules are present. Top-level files (py.typed, __init__.pyi)
are kept because they are managed separately.
"""
if not stubs_root.is_dir():
return
for item in stubs_root.iterdir():
if item.is_dir():
shutil.rmtree(item)
def generate_typing_stubs(root: NamespaceNode, output_path: Path):
"""Generates typing stubs for the AST with root `root` and outputs
created files tree to directory pointed by `output_path`.
@@ -88,6 +105,12 @@ def generate_typing_stubs(root: NamespaceNode, output_path: Path):
# The whole process should fail !only! when all possible scopes are
# checked and at least 1 node is still unresolved.
root.resolve_type_nodes()
# Remove stale typing stub subdirectories from previous builds.
# In incremental builds, disabling a module (e.g. -DBUILD_opencv_gapi=OFF)
# no longer generates its stubs, but leftover directories from a previous
# build persist and propagate through the copy/install steps, causing
# type-checker errors for stubs referencing unavailable modules.
_clean_stale_stubs_dirs(Path(output_path) / root.export_name)
_generate_typing_module(root, output_path)
_populate_reexported_symbols(root)
_generate_typing_stubs(root, output_path)
@@ -708,7 +731,7 @@ def _generate_typing_module(root: NamespaceNode, output_path: Path) -> None:
"""
def has_all_required_modules(type_node: TypeNode) -> bool:
return all(em in root.namespaces for em in node.required_modules)
return all(em in root.namespaces for em in type_node.required_modules)
def register_alias_links_from_aggregated_type(type_node: TypeNode) -> None:
assert isinstance(type_node, AggregatedTypeNode), \