mirror of
https://github.com/opencv/opencv.git
synced 2026-07-29 07:13:02 +04:00
Merge branch 4.x
This commit is contained in:
@@ -20,11 +20,6 @@ add_subdirectory(bindings)
|
||||
|
||||
add_subdirectory(test)
|
||||
|
||||
if(NOT OPENCV_SKIP_PYTHON_LOADER)
|
||||
include("./python_loader.cmake")
|
||||
message(STATUS "OpenCV Python: during development append to PYTHONPATH: ${CMAKE_BINARY_DIR}/python_loader")
|
||||
endif()
|
||||
|
||||
if(__disable_python2)
|
||||
ocv_module_disable_(python2)
|
||||
endif()
|
||||
|
||||
@@ -8,6 +8,10 @@ set(OPENCV_PYTHON_BINDINGS_DIR "${CMAKE_CURRENT_BINARY_DIR}" CACHE INTERNAL "")
|
||||
# This file is included from a subdirectory
|
||||
set(PYTHON_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../")
|
||||
|
||||
if(NOT OPENCV_SKIP_PYTHON_LOADER)
|
||||
include("${PYTHON_SOURCE_DIR}/python_loader.cmake")
|
||||
endif()
|
||||
|
||||
# get list of modules to wrap
|
||||
set(OPENCV_PYTHON_MODULES)
|
||||
foreach(m ${OPENCV_MODULES_BUILD})
|
||||
|
||||
@@ -86,7 +86,7 @@ set_target_properties(${the_module} PROPERTIES
|
||||
ARCHIVE_OUTPUT_NAME ${the_module} # prevent name conflict for python2/3 outputs
|
||||
PREFIX ""
|
||||
OUTPUT_NAME cv2
|
||||
SUFFIX ${CVPY_SUFFIX})
|
||||
SUFFIX "${CVPY_SUFFIX}")
|
||||
|
||||
if(ENABLE_SOLUTION_FOLDERS)
|
||||
set_target_properties(${the_module} PROPERTIES FOLDER "bindings")
|
||||
@@ -218,29 +218,6 @@ if(NOT OPENCV_SKIP_PYTHON_LOADER)
|
||||
endif()
|
||||
configure_file("${PYTHON_SOURCE_DIR}/package/template/config-x.y.py.in" "${__python_loader_install_tmp_path}/cv2/${__target_config}" @ONLY)
|
||||
install(FILES "${__python_loader_install_tmp_path}/cv2/${__target_config}" DESTINATION "${OPENCV_PYTHON_INSTALL_PATH}/cv2/" COMPONENT python)
|
||||
|
||||
# handle Python extra code
|
||||
foreach(m ${OPENCV_MODULES_BUILD})
|
||||
if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";python;" AND HAVE_${m}
|
||||
AND EXISTS "${OPENCV_MODULE_${m}_LOCATION}/misc/python/package"
|
||||
)
|
||||
set(__base "${OPENCV_MODULE_${m}_LOCATION}/misc/python/package")
|
||||
file(GLOB_RECURSE extra_py_files
|
||||
RELATIVE "${__base}"
|
||||
"${__base}/**/*.py"
|
||||
)
|
||||
if(extra_py_files)
|
||||
list(SORT extra_py_files)
|
||||
foreach(f ${extra_py_files})
|
||||
get_filename_component(__dir "${f}" DIRECTORY)
|
||||
configure_file("${__base}/${f}" "${__loader_path}/cv2/_extra_py_code/${f}" COPYONLY)
|
||||
install(FILES "${__base}/${f}" DESTINATION "${OPENCV_PYTHON_INSTALL_PATH}/cv2/_extra_py_code/${__dir}/" COMPONENT python)
|
||||
endforeach()
|
||||
else()
|
||||
message(WARNING "Module ${m} has no .py files in misc/python/package")
|
||||
endif()
|
||||
endif()
|
||||
endforeach(m)
|
||||
endif() # NOT OPENCV_SKIP_PYTHON_LOADER
|
||||
|
||||
unset(PYTHON_SRC_DIR)
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
OpenCV Python binary extension loader
|
||||
'''
|
||||
import os
|
||||
import importlib
|
||||
import sys
|
||||
|
||||
__all__ = []
|
||||
@@ -15,17 +16,55 @@ except ImportError:
|
||||
print(' pip install numpy')
|
||||
raise
|
||||
|
||||
|
||||
py_code_loader = None
|
||||
if sys.version_info[:2] >= (3, 0):
|
||||
try:
|
||||
from . import _extra_py_code as py_code_loader
|
||||
except:
|
||||
pass
|
||||
|
||||
# TODO
|
||||
# is_x64 = sys.maxsize > 2**32
|
||||
|
||||
|
||||
def __load_extra_py_code_for_module(base, name, enable_debug_print=False):
|
||||
module_name = "{}.{}".format(__name__, name)
|
||||
export_module_name = "{}.{}".format(base, name)
|
||||
native_module = sys.modules.pop(module_name, None)
|
||||
try:
|
||||
py_module = importlib.import_module(module_name)
|
||||
except ImportError as err:
|
||||
if enable_debug_print:
|
||||
print("Can't load Python code for module:", module_name,
|
||||
". Reason:", err)
|
||||
# Extension doesn't contain extra py code
|
||||
return False
|
||||
|
||||
if not hasattr(base, name):
|
||||
setattr(sys.modules[base], name, py_module)
|
||||
sys.modules[export_module_name] = py_module
|
||||
# If it is C extension module it is already loaded by cv2 package
|
||||
if native_module:
|
||||
setattr(py_module, "_native", native_module)
|
||||
for k, v in filter(lambda kv: not hasattr(py_module, kv[0]),
|
||||
native_module.__dict__.items()):
|
||||
if enable_debug_print: print(' symbol({}): {} = {}'.format(name, k, v))
|
||||
setattr(py_module, k, v)
|
||||
return True
|
||||
|
||||
|
||||
def __collect_extra_submodules(enable_debug_print=False):
|
||||
def modules_filter(module):
|
||||
return all((
|
||||
# module is not internal
|
||||
not module.startswith("_"),
|
||||
not module.startswith("python-"),
|
||||
# it is not a file
|
||||
os.path.isdir(os.path.join(_extra_submodules_init_path, module))
|
||||
))
|
||||
if sys.version_info[0] < 3:
|
||||
if enable_debug_print:
|
||||
print("Extra submodules is loaded only for Python 3")
|
||||
return []
|
||||
|
||||
__INIT_FILE_PATH = os.path.abspath(__file__)
|
||||
_extra_submodules_init_path = os.path.dirname(__INIT_FILE_PATH)
|
||||
return filter(modules_filter, os.listdir(_extra_submodules_init_path))
|
||||
|
||||
|
||||
def bootstrap():
|
||||
import sys
|
||||
|
||||
@@ -107,23 +146,36 @@ def bootstrap():
|
||||
# amending of LD_LIBRARY_PATH works for sub-processes only
|
||||
os.environ['LD_LIBRARY_PATH'] = ':'.join(l_vars['BINARIES_PATHS']) + ':' + os.environ.get('LD_LIBRARY_PATH', '')
|
||||
|
||||
if DEBUG: print('OpenCV loader: replacing cv2 module')
|
||||
del sys.modules['cv2']
|
||||
import cv2
|
||||
if DEBUG: print("Relink everything from native cv2 module to cv2 package")
|
||||
|
||||
py_module = sys.modules.pop("cv2")
|
||||
|
||||
native_module = importlib.import_module("cv2")
|
||||
|
||||
sys.modules["cv2"] = py_module
|
||||
setattr(py_module, "_native", native_module)
|
||||
|
||||
for item_name, item in filter(lambda kv: kv[0] not in ("__file__", "__loader__", "__spec__",
|
||||
"__name__", "__package__"),
|
||||
native_module.__dict__.items()):
|
||||
if item_name not in g_vars:
|
||||
g_vars[item_name] = item
|
||||
|
||||
sys.path = save_sys_path # multiprocessing should start from bootstrap code (https://github.com/opencv/opencv/issues/18502)
|
||||
|
||||
try:
|
||||
import sys
|
||||
del sys.OpenCV_LOADER
|
||||
except:
|
||||
pass
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print("Exception during delete OpenCV_LOADER:", e)
|
||||
|
||||
if DEBUG: print('OpenCV loader: binary extension... OK')
|
||||
|
||||
if py_code_loader:
|
||||
py_code_loader.init('cv2')
|
||||
for submodule in __collect_extra_submodules(DEBUG):
|
||||
if __load_extra_py_code_for_module("cv2", submodule, DEBUG):
|
||||
if DEBUG: print("Extra Python code for", submodule, "is loaded")
|
||||
|
||||
if DEBUG: print('OpenCV loader: DONE')
|
||||
|
||||
|
||||
bootstrap()
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
import sys
|
||||
import importlib
|
||||
|
||||
__all__ = ['init']
|
||||
|
||||
|
||||
DEBUG = False
|
||||
if hasattr(sys, 'OpenCV_LOADER_DEBUG'):
|
||||
DEBUG = True
|
||||
|
||||
|
||||
def _load_py_code(base, name):
|
||||
try:
|
||||
m = importlib.import_module(__name__ + name)
|
||||
except ImportError:
|
||||
return # extension doesn't exist?
|
||||
|
||||
if DEBUG: print('OpenCV loader: added python code extension for: ' + name)
|
||||
|
||||
if hasattr(m, '__all__'):
|
||||
export_members = { k : getattr(m, k) for k in m.__all__ }
|
||||
else:
|
||||
export_members = m.__dict__
|
||||
|
||||
for k, v in export_members.items():
|
||||
if k.startswith('_'): # skip internals
|
||||
continue
|
||||
if isinstance(v, type(sys)): # don't bring modules
|
||||
continue
|
||||
if DEBUG: print(' symbol: {} = {}'.format(k, v))
|
||||
setattr(sys.modules[base + name ], k, v)
|
||||
|
||||
del sys.modules[__name__ + name]
|
||||
|
||||
|
||||
# TODO: listdir
|
||||
def init(base):
|
||||
_load_py_code(base, '.cv2') # special case
|
||||
prefix = base
|
||||
prefix_len = len(prefix)
|
||||
|
||||
modules = [ m for m in sys.modules.keys() if m.startswith(prefix) ]
|
||||
for m in modules:
|
||||
m2 = m[prefix_len:] # strip prefix
|
||||
if len(m2) == 0:
|
||||
continue
|
||||
if m2.startswith('._'): # skip internals
|
||||
continue
|
||||
if m2.startswith('.load_config_'): # skip helper files
|
||||
continue
|
||||
_load_py_code(base, m2)
|
||||
|
||||
del sys.modules[__name__]
|
||||
@@ -0,0 +1 @@
|
||||
from .version import get_ocv_version
|
||||
@@ -0,0 +1,5 @@
|
||||
import cv2
|
||||
|
||||
|
||||
def get_ocv_version():
|
||||
return getattr(cv2, "__version__", "unavailable")
|
||||
@@ -22,10 +22,15 @@ else()
|
||||
set(CMAKE_PYTHON_EXTENSION_INSTALL_PATH_BASE "os.path.join(LOADER_DIR, 'not_installed')")
|
||||
endif()
|
||||
|
||||
if(OpenCV_FOUND)
|
||||
return() # Ignore "standalone" builds of Python bindings
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
set(PYTHON_LOADER_FILES
|
||||
"setup.py" "cv2/__init__.py"
|
||||
"cv2/load_config_py2.py" "cv2/load_config_py3.py"
|
||||
"cv2/_extra_py_code/__init__.py"
|
||||
)
|
||||
foreach(fname ${PYTHON_LOADER_FILES})
|
||||
get_filename_component(__dir "${fname}" DIRECTORY)
|
||||
@@ -40,34 +45,84 @@ foreach(fname ${PYTHON_LOADER_FILES})
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(NOT OpenCV_FOUND) # Ignore "standalone" builds of Python bindings
|
||||
if(WIN32)
|
||||
if(CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_PATH "'${EXECUTABLE_OUTPUT_PATH}/Release'") # TODO: CMAKE_BUILD_TYPE is not defined
|
||||
else()
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_PATH "'${EXECUTABLE_OUTPUT_PATH}'")
|
||||
endif()
|
||||
|
||||
|
||||
if(WIN32)
|
||||
if(CMAKE_GENERATOR MATCHES "Visual Studio")
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_PATH "'${EXECUTABLE_OUTPUT_PATH}/Release'") # TODO: CMAKE_BUILD_TYPE is not defined
|
||||
else()
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_PATH "'${LIBRARY_OUTPUT_PATH}'")
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_PATH "'${EXECUTABLE_OUTPUT_PATH}'")
|
||||
endif()
|
||||
else()
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_PATH "'${LIBRARY_OUTPUT_PATH}'")
|
||||
endif()
|
||||
string(REPLACE ";" ",\n " CMAKE_PYTHON_BINARIES_PATH "${CMAKE_PYTHON_BINARIES_PATH}")
|
||||
configure_file("${PYTHON_SOURCE_DIR}/package/template/config.py.in" "${__loader_path}/cv2/config.py" @ONLY)
|
||||
|
||||
|
||||
|
||||
# install
|
||||
if(DEFINED OPENCV_PYTHON_INSTALL_PATH)
|
||||
if(WIN32)
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_INSTALL_PATH "os.path.join(${CMAKE_PYTHON_EXTENSION_INSTALL_PATH_BASE}, '${OPENCV_BIN_INSTALL_PATH}')")
|
||||
else()
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_INSTALL_PATH "os.path.join(${CMAKE_PYTHON_EXTENSION_INSTALL_PATH_BASE}, '${OPENCV_LIB_INSTALL_PATH}')")
|
||||
endif()
|
||||
set(CMAKE_PYTHON_BINARIES_PATH "${CMAKE_PYTHON_BINARIES_INSTALL_PATH}")
|
||||
if (WIN32 AND HAVE_CUDA)
|
||||
if (DEFINED CUDA_TOOLKIT_ROOT_DIR)
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_PATH "os.path.join(os.getenv('CUDA_PATH', '${CUDA_TOOLKIT_ROOT_DIR}'), 'bin')")
|
||||
endif()
|
||||
endif()
|
||||
string(REPLACE ";" ",\n " CMAKE_PYTHON_BINARIES_PATH "${CMAKE_PYTHON_BINARIES_PATH}")
|
||||
configure_file("${PYTHON_SOURCE_DIR}/package/template/config.py.in" "${__loader_path}/cv2/config.py" @ONLY)
|
||||
|
||||
# install
|
||||
if(DEFINED OPENCV_PYTHON_INSTALL_PATH)
|
||||
if(WIN32)
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_INSTALL_PATH "os.path.join(${CMAKE_PYTHON_EXTENSION_INSTALL_PATH_BASE}, '${OPENCV_BIN_INSTALL_PATH}')")
|
||||
else()
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_INSTALL_PATH "os.path.join(${CMAKE_PYTHON_EXTENSION_INSTALL_PATH_BASE}, '${OPENCV_LIB_INSTALL_PATH}')")
|
||||
endif()
|
||||
set(CMAKE_PYTHON_BINARIES_PATH "${CMAKE_PYTHON_BINARIES_INSTALL_PATH}")
|
||||
if (WIN32 AND HAVE_CUDA)
|
||||
if (DEFINED CUDA_TOOLKIT_ROOT_DIR)
|
||||
list(APPEND CMAKE_PYTHON_BINARIES_PATH "os.path.join(os.getenv('CUDA_PATH', '${CUDA_TOOLKIT_ROOT_DIR}'), 'bin')")
|
||||
endif()
|
||||
endif()
|
||||
string(REPLACE ";" ",\n " CMAKE_PYTHON_BINARIES_PATH "${CMAKE_PYTHON_BINARIES_PATH}")
|
||||
configure_file("${PYTHON_SOURCE_DIR}/package/template/config.py.in" "${__python_loader_install_tmp_path}/cv2/config.py" @ONLY)
|
||||
install(FILES "${__python_loader_install_tmp_path}/cv2/config.py" DESTINATION "${OPENCV_PYTHON_INSTALL_PATH}/cv2/" COMPONENT python)
|
||||
endif()
|
||||
configure_file("${PYTHON_SOURCE_DIR}/package/template/config.py.in" "${__python_loader_install_tmp_path}/cv2/config.py" @ONLY)
|
||||
install(FILES "${__python_loader_install_tmp_path}/cv2/config.py" DESTINATION "${OPENCV_PYTHON_INSTALL_PATH}/cv2/" COMPONENT python)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
#
|
||||
# Handle Python extra code (submodules)
|
||||
#
|
||||
function(ocv_add_python_files_from_path search_path)
|
||||
file(GLOB_RECURSE extra_py_files
|
||||
RELATIVE "${search_path}"
|
||||
# Plain Python code
|
||||
"${search_path}/*.py"
|
||||
# Type annotations
|
||||
"${search_path}/*.pyi"
|
||||
)
|
||||
ocv_debug_message("Extra Py files for ${search_path}: ${extra_py_files}")
|
||||
if(extra_py_files)
|
||||
list(SORT extra_py_files)
|
||||
foreach(filename ${extra_py_files})
|
||||
get_filename_component(module "${filename}" DIRECTORY)
|
||||
if(NOT ${module} IN_LIST extra_modules)
|
||||
list(APPEND extra_modules ${module})
|
||||
endif()
|
||||
configure_file("${search_path}/${filename}" "${__loader_path}/cv2/${filename}" COPYONLY)
|
||||
if(DEFINED OPENCV_PYTHON_INSTALL_PATH)
|
||||
install(FILES "${search_path}/${filename}" DESTINATION "${OPENCV_PYTHON_INSTALL_PATH}/cv2/${module}/" COMPONENT python)
|
||||
endif()
|
||||
endforeach()
|
||||
message(STATUS "Found '${extra_modules}' Python modules from ${search_path}")
|
||||
else()
|
||||
message(WARNING "Can't add Python files and modules from '${module_path}'. There is no .py or .pyi files")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
ocv_add_python_files_from_path("${PYTHON_SOURCE_DIR}/package/extra_modules")
|
||||
|
||||
foreach(m ${OPENCV_MODULES_BUILD})
|
||||
if (";${OPENCV_MODULE_${m}_WRAPPERS};" MATCHES ";python;" AND HAVE_${m}
|
||||
AND EXISTS "${OPENCV_MODULE_${m}_LOCATION}/misc/python/package"
|
||||
)
|
||||
ocv_add_python_files_from_path("${OPENCV_MODULE_${m}_LOCATION}/misc/python/package")
|
||||
endif()
|
||||
endforeach(m)
|
||||
|
||||
if(NOT "${OPENCV_PYTHON_EXTRA_MODULES_PATH}" STREQUAL "")
|
||||
foreach(extra_ocv_py_modules_path ${OPENCV_PYTHON_EXTRA_MODULES_PATH})
|
||||
ocv_add_python_files_from_path(${extra_ocv_py_modules_path})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
+393
-371
@@ -49,6 +49,8 @@
|
||||
|
||||
static PyObject* opencv_error = NULL;
|
||||
|
||||
static PyTypeObject* pyopencv_Mat_TypePtr = nullptr;
|
||||
|
||||
class ArgInfo
|
||||
{
|
||||
public:
|
||||
@@ -496,6 +498,33 @@ bool parseSequence(PyObject* obj, RefWrapper<T> (&value)[N], const ArgInfo& info
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace traits {
|
||||
template <bool Value>
|
||||
struct BooleanConstant
|
||||
{
|
||||
static const bool value = Value;
|
||||
typedef BooleanConstant<Value> type;
|
||||
};
|
||||
|
||||
typedef BooleanConstant<true> TrueType;
|
||||
typedef BooleanConstant<false> FalseType;
|
||||
|
||||
template <class T>
|
||||
struct VoidType {
|
||||
typedef void type;
|
||||
};
|
||||
|
||||
template <class T, class DType = void>
|
||||
struct IsRepresentableAsMatDataType : FalseType
|
||||
{
|
||||
};
|
||||
|
||||
template <class T>
|
||||
struct IsRepresentableAsMatDataType<T, typename VoidType<typename DataType<T>::channel_type>::type> : TrueType
|
||||
{
|
||||
};
|
||||
} // namespace traits
|
||||
|
||||
typedef std::vector<uchar> vector_uchar;
|
||||
typedef std::vector<char> vector_char;
|
||||
typedef std::vector<int> vector_int;
|
||||
@@ -611,10 +640,20 @@ static bool isBool(PyObject* obj) CV_NOEXCEPT
|
||||
return PyArray_IsScalar(obj, Bool) || PyBool_Check(obj);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static std::string pycv_dumpArray(const T* arr, int n)
|
||||
{
|
||||
std::ostringstream out;
|
||||
out << "[";
|
||||
for (int i = 0; i < n; ++i)
|
||||
out << " " << arr[i];
|
||||
out << " ]";
|
||||
return out.str();
|
||||
}
|
||||
|
||||
// special case, when the converter needs full ArgInfo structure
|
||||
static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo& info)
|
||||
{
|
||||
bool allowND = true;
|
||||
if(!o || o == Py_None)
|
||||
{
|
||||
if( !m.data )
|
||||
@@ -700,12 +739,29 @@ static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo& info)
|
||||
return false;
|
||||
}
|
||||
|
||||
int size[CV_MAX_DIM+1];
|
||||
size_t step[CV_MAX_DIM+1];
|
||||
size_t elemsize = CV_ELEM_SIZE1(type);
|
||||
const npy_intp* _sizes = PyArray_DIMS(oarr);
|
||||
const npy_intp* _strides = PyArray_STRIDES(oarr);
|
||||
|
||||
CV_LOG_DEBUG(NULL, "Incoming ndarray '" << info.name << "': ndims=" << ndims << " _sizes=" << pycv_dumpArray(_sizes, ndims) << " _strides=" << pycv_dumpArray(_strides, ndims));
|
||||
|
||||
bool ismultichannel = ndims == 3 && _sizes[2] <= CV_CN_MAX;
|
||||
if (pyopencv_Mat_TypePtr && PyObject_TypeCheck(o, pyopencv_Mat_TypePtr))
|
||||
{
|
||||
bool wrapChannels = false;
|
||||
PyObject* pyobj_wrap_channels = PyObject_GetAttrString(o, "wrap_channels");
|
||||
if (pyobj_wrap_channels)
|
||||
{
|
||||
if (!pyopencv_to_safe(pyobj_wrap_channels, wrapChannels, ArgInfo("cv.Mat.wrap_channels", 0)))
|
||||
{
|
||||
// TODO extra message
|
||||
Py_DECREF(pyobj_wrap_channels);
|
||||
return false;
|
||||
}
|
||||
Py_DECREF(pyobj_wrap_channels);
|
||||
}
|
||||
ismultichannel = wrapChannels && ndims >= 1;
|
||||
}
|
||||
|
||||
for( int i = ndims-1; i >= 0 && !needcopy; i-- )
|
||||
{
|
||||
@@ -719,14 +775,26 @@ static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo& info)
|
||||
needcopy = true;
|
||||
}
|
||||
|
||||
if( ismultichannel && _strides[1] != (npy_intp)elemsize*_sizes[2] )
|
||||
needcopy = true;
|
||||
if (ismultichannel)
|
||||
{
|
||||
int channels = ndims >= 1 ? (int)_sizes[ndims - 1] : 1;
|
||||
if (channels > CV_CN_MAX)
|
||||
{
|
||||
failmsg("%s unable to wrap channels, too high (%d > CV_CN_MAX=%d)", info.name, (int)channels, (int)CV_CN_MAX);
|
||||
return false;
|
||||
}
|
||||
ndims--;
|
||||
type |= CV_MAKETYPE(0, channels);
|
||||
|
||||
if (ndims >= 1 && _strides[ndims - 1] != (npy_intp)elemsize*_sizes[ndims])
|
||||
needcopy = true;
|
||||
}
|
||||
|
||||
if (needcopy)
|
||||
{
|
||||
if (info.outputarg)
|
||||
{
|
||||
failmsg("Layout of the output array %s is incompatible with cv::Mat (step[ndims-1] != elemsize or step[1] != elemsize*nchannels)", info.name);
|
||||
failmsg("Layout of the output array %s is incompatible with cv::Mat", info.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -742,6 +810,9 @@ static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo& info)
|
||||
_strides = PyArray_STRIDES(oarr);
|
||||
}
|
||||
|
||||
int size[CV_MAX_DIM+1] = {};
|
||||
size_t step[CV_MAX_DIM+1] = {};
|
||||
|
||||
// Normalize strides in case NPY_RELAXED_STRIDES is set
|
||||
size_t default_step = elemsize;
|
||||
for ( int i = ndims - 1; i >= 0; --i )
|
||||
@@ -760,23 +831,16 @@ static bool pyopencv_to(PyObject* o, Mat& m, const ArgInfo& info)
|
||||
}
|
||||
|
||||
// handle degenerate case
|
||||
// FIXIT: Don't force 1D for Scalars
|
||||
if( ndims == 0) {
|
||||
size[ndims] = 1;
|
||||
step[ndims] = elemsize;
|
||||
ndims++;
|
||||
}
|
||||
|
||||
if( ismultichannel )
|
||||
{
|
||||
ndims--;
|
||||
type |= CV_MAKETYPE(0, size[2]);
|
||||
}
|
||||
|
||||
if( ndims > 2 && !allowND )
|
||||
{
|
||||
failmsg("%s has more than 2 dimensions", info.name);
|
||||
return false;
|
||||
}
|
||||
#if 1
|
||||
CV_LOG_DEBUG(NULL, "Construct Mat: ndims=" << ndims << " size=" << pycv_dumpArray(size, ndims) << " step=" << pycv_dumpArray(step, ndims) << " type=" << cv::typeToString(type));
|
||||
#endif
|
||||
|
||||
m = Mat(ndims, size, type, PyArray_DATA(oarr), step);
|
||||
m.u = g_numpyAllocator.allocate(o, ndims, size, type, step);
|
||||
@@ -1072,6 +1136,30 @@ bool pyopencv_to(PyObject* obj, uchar& value, const ArgInfo& info)
|
||||
return ivalue != -1 || !PyErr_Occurred();
|
||||
}
|
||||
|
||||
template<>
|
||||
bool pyopencv_to(PyObject* obj, char& value, const ArgInfo& info)
|
||||
{
|
||||
if (!obj || obj == Py_None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (isBool(obj))
|
||||
{
|
||||
failmsg("Argument '%s' must be an integer, not bool", info.name);
|
||||
return false;
|
||||
}
|
||||
if (PyArray_IsIntegerScalar(obj))
|
||||
{
|
||||
value = saturate_cast<char>(PyArray_PyIntAsInt(obj));
|
||||
}
|
||||
else
|
||||
{
|
||||
failmsg("Argument '%s' is required to be an integer", info.name);
|
||||
return false;
|
||||
}
|
||||
return !CV_HAS_CONVERSION_ERROR(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const double& value)
|
||||
{
|
||||
@@ -1484,357 +1572,12 @@ PyObject* pyopencv_from(const Point3d& p)
|
||||
return Py_BuildValue("(ddd)", p.x, p.y, p.z);
|
||||
}
|
||||
|
||||
template<typename _Tp> struct pyopencvVecConverter
|
||||
{
|
||||
typedef typename DataType<_Tp>::channel_type _Cp;
|
||||
static inline bool copyOneItem(PyObject *obj, size_t start, int channels, _Cp * data)
|
||||
{
|
||||
for(size_t j = 0; (int)j < channels; j++ )
|
||||
{
|
||||
SafeSeqItem sub_item_wrap(obj, start + j);
|
||||
PyObject* item_ij = sub_item_wrap.item;
|
||||
if( PyInt_Check(item_ij))
|
||||
{
|
||||
int v = (int)PyInt_AsLong(item_ij);
|
||||
if( v == -1 && PyErr_Occurred() )
|
||||
return false;
|
||||
data[j] = saturate_cast<_Cp>(v);
|
||||
}
|
||||
else if( PyLong_Check(item_ij))
|
||||
{
|
||||
int v = (int)PyLong_AsLong(item_ij);
|
||||
if( v == -1 && PyErr_Occurred() )
|
||||
return false;
|
||||
data[j] = saturate_cast<_Cp>(v);
|
||||
}
|
||||
else if( PyFloat_Check(item_ij))
|
||||
{
|
||||
double v = PyFloat_AsDouble(item_ij);
|
||||
if( PyErr_Occurred() )
|
||||
return false;
|
||||
data[j] = saturate_cast<_Cp>(v);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
static bool to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info)
|
||||
{
|
||||
if(!obj || obj == Py_None)
|
||||
return true;
|
||||
if (PyArray_Check(obj))
|
||||
{
|
||||
Mat m;
|
||||
pyopencv_to(obj, m, info);
|
||||
m.copyTo(value);
|
||||
return true;
|
||||
}
|
||||
else if (PySequence_Check(obj))
|
||||
{
|
||||
const int type = traits::Type<_Tp>::value;
|
||||
const int depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type);
|
||||
size_t i, n = PySequence_Size(obj);
|
||||
value.resize(n);
|
||||
for (i = 0; i < n; i++ )
|
||||
{
|
||||
SafeSeqItem item_wrap(obj, i);
|
||||
PyObject* item = item_wrap.item;
|
||||
_Cp* data = (_Cp*)&value[i];
|
||||
|
||||
if( channels == 2 && PyComplex_Check(item) )
|
||||
{
|
||||
data[0] = saturate_cast<_Cp>(PyComplex_RealAsDouble(item));
|
||||
data[1] = saturate_cast<_Cp>(PyComplex_ImagAsDouble(item));
|
||||
}
|
||||
else if( channels > 1 )
|
||||
{
|
||||
if( PyArray_Check(item))
|
||||
{
|
||||
Mat src;
|
||||
pyopencv_to(item, src, info);
|
||||
if( src.dims != 2 || src.channels() != 1 ||
|
||||
((src.cols != 1 || src.rows != channels) &&
|
||||
(src.cols != channels || src.rows != 1)))
|
||||
break;
|
||||
Mat dst(src.rows, src.cols, depth, data);
|
||||
src.convertTo(dst, type);
|
||||
if( dst.data != (uchar*)data )
|
||||
break;
|
||||
}
|
||||
else if (PySequence_Check(item))
|
||||
{
|
||||
if (!copyOneItem(item, 0, channels, data))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (channels == 1)
|
||||
{
|
||||
if (!copyOneItem(obj, i, channels, data))
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != n)
|
||||
{
|
||||
failmsg("Can't convert vector element for '%s', index=%d", info.name, i);
|
||||
}
|
||||
return i == n;
|
||||
}
|
||||
failmsg("Can't convert object to vector for '%s', unsupported type", info.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<_Tp>& value)
|
||||
{
|
||||
if(value.empty())
|
||||
return PyTuple_New(0);
|
||||
int type = traits::Type<_Tp>::value;
|
||||
int depth = CV_MAT_DEPTH(type), channels = CV_MAT_CN(type);
|
||||
Mat src((int)value.size(), channels, depth, (uchar*)&value[0]);
|
||||
return pyopencv_from(src);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
bool pyopencv_to(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencvVecConverter<_Tp>::to(obj, value, info);
|
||||
}
|
||||
|
||||
template<typename _Tp>
|
||||
PyObject* pyopencv_from(const std::vector<_Tp>& value)
|
||||
{
|
||||
return pyopencvVecConverter<_Tp>::from(value);
|
||||
}
|
||||
|
||||
template<typename _Tp> static inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector<_Tp>& value, const ArgInfo& info)
|
||||
{
|
||||
if(!obj || obj == Py_None)
|
||||
return true;
|
||||
if (!PySequence_Check(obj))
|
||||
return false;
|
||||
size_t n = PySequence_Size(obj);
|
||||
value.resize(n);
|
||||
for(size_t i = 0; i < n; i++ )
|
||||
{
|
||||
SafeSeqItem item_wrap(obj, i);
|
||||
if(!pyopencv_to(item_wrap.item, value[i], info))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector<bool>& value, const ArgInfo& info)
|
||||
{
|
||||
if(!obj || obj == Py_None)
|
||||
return true;
|
||||
if (!PySequence_Check(obj))
|
||||
return false;
|
||||
size_t n = PySequence_Size(obj);
|
||||
value.resize(n);
|
||||
for(size_t i = 0; i < n; i++ )
|
||||
{
|
||||
SafeSeqItem item_wrap(obj, i);
|
||||
bool elem{};
|
||||
if(!pyopencv_to(item_wrap.item, elem, info))
|
||||
return false;
|
||||
value[i] = elem;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename _Tp> static inline PyObject* pyopencv_from_generic_vec(const std::vector<_Tp>& value)
|
||||
{
|
||||
int i, n = (int)value.size();
|
||||
PyObject* seq = PyList_New(n);
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
_Tp elem = value[i];
|
||||
PyObject* item = pyopencv_from(elem);
|
||||
if(!item)
|
||||
break;
|
||||
PyList_SetItem(seq, i, item);
|
||||
}
|
||||
if( i < n )
|
||||
{
|
||||
Py_DECREF(seq);
|
||||
return 0;
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
template<> inline PyObject* pyopencv_from_generic_vec(const std::vector<bool>& value)
|
||||
{
|
||||
int i, n = (int)value.size();
|
||||
PyObject* seq = PyList_New(n);
|
||||
for( i = 0; i < n; i++ )
|
||||
{
|
||||
bool elem = value[i];
|
||||
PyObject* item = pyopencv_from(elem);
|
||||
if(!item)
|
||||
break;
|
||||
PyList_SetItem(seq, i, item);
|
||||
}
|
||||
if( i < n )
|
||||
{
|
||||
Py_DECREF(seq);
|
||||
return 0;
|
||||
}
|
||||
return seq;
|
||||
}
|
||||
|
||||
template<std::size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
convert_to_python_tuple(const std::tuple<Tp...>&, PyObject*) { }
|
||||
|
||||
template<std::size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if<I < sizeof...(Tp), void>::type
|
||||
convert_to_python_tuple(const std::tuple<Tp...>& cpp_tuple, PyObject* py_tuple)
|
||||
{
|
||||
PyObject* item = pyopencv_from(std::get<I>(cpp_tuple));
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
PyTuple_SetItem(py_tuple, I, item);
|
||||
convert_to_python_tuple<I + 1, Tp...>(cpp_tuple, py_tuple);
|
||||
}
|
||||
|
||||
|
||||
template<typename... Ts>
|
||||
PyObject* pyopencv_from(const std::tuple<Ts...>& cpp_tuple)
|
||||
{
|
||||
size_t size = sizeof...(Ts);
|
||||
PyObject* py_tuple = PyTuple_New(size);
|
||||
convert_to_python_tuple(cpp_tuple, py_tuple);
|
||||
size_t actual_size = PyTuple_Size(py_tuple);
|
||||
|
||||
if (actual_size < size)
|
||||
{
|
||||
Py_DECREF(py_tuple);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return py_tuple;
|
||||
}
|
||||
|
||||
template<>
|
||||
PyObject* pyopencv_from(const std::pair<int, double>& src)
|
||||
{
|
||||
return Py_BuildValue("(id)", src.first, src.second);
|
||||
}
|
||||
|
||||
template<typename _Tp, typename _Tr> struct pyopencvVecConverter<std::pair<_Tp, _Tr> >
|
||||
{
|
||||
static bool to(PyObject* obj, std::vector<std::pair<_Tp, _Tr> >& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<std::pair<_Tp, _Tr> >& value)
|
||||
{
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename _Tp> struct pyopencvVecConverter<std::vector<_Tp> >
|
||||
{
|
||||
static bool to(PyObject* obj, std::vector<std::vector<_Tp> >& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<std::vector<_Tp> >& value)
|
||||
{
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct pyopencvVecConverter<Mat>
|
||||
{
|
||||
static bool to(PyObject* obj, std::vector<Mat>& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<Mat>& value)
|
||||
{
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct pyopencvVecConverter<UMat>
|
||||
{
|
||||
static bool to(PyObject* obj, std::vector<UMat>& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<UMat>& value)
|
||||
{
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct pyopencvVecConverter<KeyPoint>
|
||||
{
|
||||
static bool to(PyObject* obj, std::vector<KeyPoint>& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<KeyPoint>& value)
|
||||
{
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct pyopencvVecConverter<DMatch>
|
||||
{
|
||||
static bool to(PyObject* obj, std::vector<DMatch>& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<DMatch>& value)
|
||||
{
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct pyopencvVecConverter<String>
|
||||
{
|
||||
static bool to(PyObject* obj, std::vector<String>& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<String>& value)
|
||||
{
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct pyopencvVecConverter<RotatedRect>
|
||||
{
|
||||
static bool to(PyObject* obj, std::vector<RotatedRect>& value, const ArgInfo& info)
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
static PyObject* from(const std::vector<RotatedRect>& value)
|
||||
{
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
bool pyopencv_to(PyObject* obj, TermCriteria& dst, const ArgInfo& info)
|
||||
{
|
||||
@@ -1962,6 +1705,266 @@ PyObject* pyopencv_from(const Moments& m)
|
||||
"nu30", m.nu30, "nu21", m.nu21, "nu12", m.nu12, "nu03", m.nu03);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
struct pyopencvVecConverter;
|
||||
|
||||
template <typename Tp>
|
||||
bool pyopencv_to(PyObject* obj, std::vector<Tp>& value, const ArgInfo& info)
|
||||
{
|
||||
if (!obj || obj == Py_None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return pyopencvVecConverter<Tp>::to(obj, value, info);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
PyObject* pyopencv_from(const std::vector<Tp>& value)
|
||||
{
|
||||
return pyopencvVecConverter<Tp>::from(value);
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
static bool pyopencv_to_generic_vec(PyObject* obj, std::vector<Tp>& value, const ArgInfo& info)
|
||||
{
|
||||
if (!obj || obj == Py_None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!PySequence_Check(obj))
|
||||
{
|
||||
failmsg("Can't parse '%s'. Input argument doesn't provide sequence protocol", info.name);
|
||||
return false;
|
||||
}
|
||||
const size_t n = static_cast<size_t>(PySequence_Size(obj));
|
||||
value.resize(n);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
SafeSeqItem item_wrap(obj, i);
|
||||
if (!pyopencv_to(item_wrap.item, value[i], info))
|
||||
{
|
||||
failmsg("Can't parse '%s'. Sequence item with index %lu has a wrong type", info.name, i);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> inline bool pyopencv_to_generic_vec(PyObject* obj, std::vector<bool>& value, const ArgInfo& info)
|
||||
{
|
||||
if (!obj || obj == Py_None)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (!PySequence_Check(obj))
|
||||
{
|
||||
failmsg("Can't parse '%s'. Input argument doesn't provide sequence protocol", info.name);
|
||||
return false;
|
||||
}
|
||||
const size_t n = static_cast<size_t>(PySequence_Size(obj));
|
||||
value.resize(n);
|
||||
for (size_t i = 0; i < n; i++)
|
||||
{
|
||||
SafeSeqItem item_wrap(obj, i);
|
||||
bool elem{};
|
||||
if (!pyopencv_to(item_wrap.item, elem, info))
|
||||
{
|
||||
failmsg("Can't parse '%s'. Sequence item with index %lu has a wrong type", info.name, i);
|
||||
return false;
|
||||
}
|
||||
value[i] = elem;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <typename Tp>
|
||||
static PyObject* pyopencv_from_generic_vec(const std::vector<Tp>& value)
|
||||
{
|
||||
Py_ssize_t n = static_cast<Py_ssize_t>(value.size());
|
||||
PySafeObject seq(PyTuple_New(n));
|
||||
for (Py_ssize_t i = 0; i < n; i++)
|
||||
{
|
||||
PyObject* item = pyopencv_from(value[i]);
|
||||
// If item can't be assigned - PyTuple_SetItem raises exception and returns -1.
|
||||
if (!item || PyTuple_SetItem(seq, i, item) == -1)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return seq.release();
|
||||
}
|
||||
|
||||
template<> inline PyObject* pyopencv_from_generic_vec(const std::vector<bool>& value)
|
||||
{
|
||||
Py_ssize_t n = static_cast<Py_ssize_t>(value.size());
|
||||
PySafeObject seq(PyTuple_New(n));
|
||||
for (Py_ssize_t i = 0; i < n; i++)
|
||||
{
|
||||
bool elem = value[i];
|
||||
PyObject* item = pyopencv_from(elem);
|
||||
// If item can't be assigned - PyTuple_SetItem raises exception and returns -1.
|
||||
if (!item || PyTuple_SetItem(seq, i, item) == -1)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return seq.release();
|
||||
}
|
||||
|
||||
|
||||
template<std::size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if<I == sizeof...(Tp), void>::type
|
||||
convert_to_python_tuple(const std::tuple<Tp...>&, PyObject*) { }
|
||||
|
||||
template<std::size_t I = 0, typename... Tp>
|
||||
inline typename std::enable_if<I < sizeof...(Tp), void>::type
|
||||
convert_to_python_tuple(const std::tuple<Tp...>& cpp_tuple, PyObject* py_tuple)
|
||||
{
|
||||
PyObject* item = pyopencv_from(std::get<I>(cpp_tuple));
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
PyTuple_SetItem(py_tuple, I, item);
|
||||
convert_to_python_tuple<I + 1, Tp...>(cpp_tuple, py_tuple);
|
||||
}
|
||||
|
||||
|
||||
template<typename... Ts>
|
||||
PyObject* pyopencv_from(const std::tuple<Ts...>& cpp_tuple)
|
||||
{
|
||||
size_t size = sizeof...(Ts);
|
||||
PyObject* py_tuple = PyTuple_New(size);
|
||||
convert_to_python_tuple(cpp_tuple, py_tuple);
|
||||
size_t actual_size = PyTuple_Size(py_tuple);
|
||||
|
||||
if (actual_size < size)
|
||||
{
|
||||
Py_DECREF(py_tuple);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return py_tuple;
|
||||
}
|
||||
|
||||
template <typename Tp>
|
||||
struct pyopencvVecConverter
|
||||
{
|
||||
typedef typename std::vector<Tp>::iterator VecIt;
|
||||
|
||||
static bool to(PyObject* obj, std::vector<Tp>& value, const ArgInfo& info)
|
||||
{
|
||||
if (!PyArray_Check(obj))
|
||||
{
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
// If user passed an array it is possible to make faster conversions in several cases
|
||||
PyArrayObject* array_obj = reinterpret_cast<PyArrayObject*>(obj);
|
||||
const NPY_TYPES target_type = asNumpyType<Tp>();
|
||||
const NPY_TYPES source_type = static_cast<NPY_TYPES>(PyArray_TYPE(array_obj));
|
||||
if (target_type == NPY_OBJECT)
|
||||
{
|
||||
// Non-planar arrays representing objects (e.g. array of N Rect is an array of shape Nx4) have NPY_OBJECT
|
||||
// as their target type.
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
if (PyArray_NDIM(array_obj) > 1)
|
||||
{
|
||||
failmsg("Can't parse %dD array as '%s' vector argument", PyArray_NDIM(array_obj), info.name);
|
||||
return false;
|
||||
}
|
||||
if (target_type != source_type)
|
||||
{
|
||||
// Source type requires conversion
|
||||
// Allowed conversions for target type is handled in the corresponding pyopencv_to function
|
||||
return pyopencv_to_generic_vec(obj, value, info);
|
||||
}
|
||||
// For all other cases, all array data can be directly copied to std::vector data
|
||||
// Simple `memcpy` is not possible because NumPy array can reference a slice of the bigger array:
|
||||
// ```
|
||||
// arr = np.ones((8, 4, 5), dtype=np.int32)
|
||||
// convertible_to_vector_of_int = arr[:, 0, 1]
|
||||
// ```
|
||||
value.resize(static_cast<size_t>(PyArray_SIZE(array_obj)));
|
||||
const npy_intp item_step = PyArray_STRIDE(array_obj, 0) / PyArray_ITEMSIZE(array_obj);
|
||||
const Tp* data_ptr = static_cast<Tp*>(PyArray_DATA(array_obj));
|
||||
for (VecIt it = value.begin(); it != value.end(); ++it, data_ptr += item_step) {
|
||||
*it = *data_ptr;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<Tp>& value)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
return PyTuple_New(0);
|
||||
}
|
||||
return from(value, ::traits::IsRepresentableAsMatDataType<Tp>());
|
||||
}
|
||||
|
||||
private:
|
||||
static PyObject* from(const std::vector<Tp>& value, ::traits::FalseType)
|
||||
{
|
||||
// Underlying type is not representable as Mat Data Type
|
||||
return pyopencv_from_generic_vec(value);
|
||||
}
|
||||
|
||||
static PyObject* from(const std::vector<Tp>& value, ::traits::TrueType)
|
||||
{
|
||||
// Underlying type is representable as Mat Data Type, so faster return type is available
|
||||
typedef DataType<Tp> DType;
|
||||
typedef typename DType::channel_type UnderlyingArrayType;
|
||||
|
||||
// If Mat is always exposed as NumPy array this code path can be reduced to the following snipped:
|
||||
// Mat src(value);
|
||||
// PyObject* array = pyopencv_from(src);
|
||||
// return PyArray_Squeeze(reinterpret_cast<PyArrayObject*>(array));
|
||||
// This puts unnecessary restrictions on Mat object those might be avoided without losing the performance.
|
||||
// Moreover, this version is a bit faster, because it doesn't create temporary objects with reference counting.
|
||||
|
||||
const NPY_TYPES target_type = asNumpyType<UnderlyingArrayType>();
|
||||
const int cols = DType::channels;
|
||||
PyObject* array = NULL;
|
||||
if (cols == 1)
|
||||
{
|
||||
npy_intp dims = static_cast<npy_intp>(value.size());
|
||||
array = PyArray_SimpleNew(1, &dims, target_type);
|
||||
}
|
||||
else
|
||||
{
|
||||
npy_intp dims[2] = {static_cast<npy_intp>(value.size()), cols};
|
||||
array = PyArray_SimpleNew(2, dims, target_type);
|
||||
}
|
||||
if(!array)
|
||||
{
|
||||
// NumPy arrays with shape (N, 1) and (N) are not equal, so correct error message should distinguish
|
||||
// them too.
|
||||
String shape;
|
||||
if (cols > 1)
|
||||
{
|
||||
shape = format("(%d x %d)", static_cast<int>(value.size()), cols);
|
||||
}
|
||||
else
|
||||
{
|
||||
shape = format("(%d)", static_cast<int>(value.size()));
|
||||
}
|
||||
const String error_message = format("Can't allocate NumPy array for vector with dtype=%d and shape=%s",
|
||||
static_cast<int>(target_type), shape.c_str());
|
||||
emit_failmsg(PyExc_MemoryError, error_message.c_str());
|
||||
return array;
|
||||
}
|
||||
// Fill the array
|
||||
PyArrayObject* array_obj = reinterpret_cast<PyArrayObject*>(array);
|
||||
UnderlyingArrayType* array_data = static_cast<UnderlyingArrayType*>(PyArray_DATA(array_obj));
|
||||
// if Tp is representable as Mat DataType, so the following cast is pretty safe...
|
||||
const UnderlyingArrayType* value_data = reinterpret_cast<const UnderlyingArrayType*>(value.data());
|
||||
memcpy(array_data, value_data, sizeof(UnderlyingArrayType) * value.size() * static_cast<size_t>(cols));
|
||||
return array;
|
||||
}
|
||||
};
|
||||
|
||||
static int OnError(int status, const char *func_name, const char *err_msg, const char *file_name, int line, void *userdata)
|
||||
{
|
||||
PyGILState_STATE gstate;
|
||||
@@ -2081,15 +2084,23 @@ static void OnChange(int pos, void *param)
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENCV_HIGHGUI
|
||||
// workaround for #20408, use nullptr, set value later
|
||||
static int _createTrackbar(const String &trackbar_name, const String &window_name, int value, int count,
|
||||
TrackbarCallback onChange, PyObject* py_callback_info)
|
||||
{
|
||||
int n = createTrackbar(trackbar_name, window_name, NULL, count, onChange, py_callback_info);
|
||||
setTrackbarPos(trackbar_name, window_name, value);
|
||||
return n;
|
||||
}
|
||||
static PyObject *pycvCreateTrackbar(PyObject*, PyObject *args)
|
||||
{
|
||||
PyObject *on_change;
|
||||
char* trackbar_name;
|
||||
char* window_name;
|
||||
int *value = new int;
|
||||
int value;
|
||||
int count;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "ssiiO", &trackbar_name, &window_name, value, &count, &on_change))
|
||||
if (!PyArg_ParseTuple(args, "ssiiO", &trackbar_name, &window_name, &value, &count, &on_change))
|
||||
return NULL;
|
||||
if (!PyCallable_Check(on_change)) {
|
||||
PyErr_SetString(PyExc_TypeError, "on_change must be callable");
|
||||
@@ -2108,7 +2119,7 @@ static PyObject *pycvCreateTrackbar(PyObject*, PyObject *args)
|
||||
{
|
||||
registered_callbacks.insert(std::pair<std::string, PyObject*>(name, py_callback_info));
|
||||
}
|
||||
ERRWRAP2(createTrackbar(trackbar_name, window_name, value, count, OnChange, py_callback_info));
|
||||
ERRWRAP2(_createTrackbar(trackbar_name, window_name, value, count, OnChange, py_callback_info));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
@@ -2209,7 +2220,24 @@ static int convert_to_char(PyObject *o, char *dst, const ArgInfo& info)
|
||||
#include "pyopencv_generated_types_content.h"
|
||||
#include "pyopencv_generated_funcs.h"
|
||||
|
||||
static PyObject* pycvRegisterMatType(PyObject *self, PyObject *value)
|
||||
{
|
||||
CV_LOG_DEBUG(NULL, cv::format("pycvRegisterMatType %p %p\n", self, value));
|
||||
|
||||
if (0 == PyType_Check(value))
|
||||
{
|
||||
PyErr_SetString(PyExc_TypeError, "Type argument is expected");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Py_INCREF(value);
|
||||
pyopencv_Mat_TypePtr = (PyTypeObject*)value;
|
||||
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyMethodDef special_methods[] = {
|
||||
{"_registerMatType", (PyCFunction)(pycvRegisterMatType), METH_O, "_registerMatType(cv.Mat) -> None (Internal)"},
|
||||
{"redirectError", CV_PY_FN_WITH_KW(pycvRedirectError), "redirectError(onError) -> None"},
|
||||
#ifdef HAVE_OPENCV_HIGHGUI
|
||||
{"createTrackbar", (PyCFunction)pycvCreateTrackbar, METH_VARARGS, "createTrackbar(trackbarName, windowName, value, count, onChange) -> None"},
|
||||
@@ -2219,12 +2247,6 @@ static PyMethodDef special_methods[] = {
|
||||
#ifdef HAVE_OPENCV_DNN
|
||||
{"dnn_registerLayer", CV_PY_FN_WITH_KW(pyopencv_cv_dnn_registerLayer), "registerLayer(type, class) -> None"},
|
||||
{"dnn_unregisterLayer", CV_PY_FN_WITH_KW(pyopencv_cv_dnn_unregisterLayer), "unregisterLayer(type) -> None"},
|
||||
#endif
|
||||
#ifdef HAVE_OPENCV_GAPI
|
||||
{"GIn", CV_PY_FN_WITH_KW(pyopencv_cv_GIn), "GIn(...) -> GInputProtoArgs"},
|
||||
{"GOut", CV_PY_FN_WITH_KW(pyopencv_cv_GOut), "GOut(...) -> GOutputProtoArgs"},
|
||||
{"gin", CV_PY_FN_WITH_KW(pyopencv_cv_gin), "gin(...) -> ExtractArgsCallback"},
|
||||
{"descr_of", CV_PY_FN_WITH_KW(pyopencv_cv_descr_of), "descr_of(...) -> ExtractMetaCallback"},
|
||||
#endif
|
||||
{NULL, NULL},
|
||||
};
|
||||
|
||||
@@ -214,6 +214,16 @@ simple_argtype_mapping = {
|
||||
"Stream": ArgTypeInfo("Stream", FormatStrings.object, 'Stream::Null()', True),
|
||||
}
|
||||
|
||||
# Set of reserved keywords for Python. Can be acquired via the following call
|
||||
# $ python -c "help('keywords')"
|
||||
# Keywords that are reserved in C/C++ are excluded because they can not be
|
||||
# used as variables identifiers
|
||||
python_reserved_keywords = {
|
||||
"True", "None", "False", "as", "assert", "def", "del", "elif", "except", "exec",
|
||||
"finally", "from", "global", "import", "in", "is", "lambda", "nonlocal",
|
||||
"pass", "print", "raise", "with", "yield"
|
||||
}
|
||||
|
||||
|
||||
def normalize_class_name(name):
|
||||
return re.sub(r"^cv\.", "", name).replace(".", "_")
|
||||
@@ -387,6 +397,8 @@ class ArgInfo(object):
|
||||
def __init__(self, arg_tuple):
|
||||
self.tp = handle_ptr(arg_tuple[0])
|
||||
self.name = arg_tuple[1]
|
||||
if self.name in python_reserved_keywords:
|
||||
self.name += "_"
|
||||
self.defval = arg_tuple[2]
|
||||
self.isarray = False
|
||||
self.arraylen = 0
|
||||
|
||||
@@ -55,13 +55,13 @@ class CppHeaderParser(object):
|
||||
def get_macro_arg(self, arg_str, npos):
|
||||
npos2 = npos3 = arg_str.find("(", npos)
|
||||
if npos2 < 0:
|
||||
print("Error: no arguments for the macro at %d" % (self.lineno,))
|
||||
print("Error: no arguments for the macro at %s:%d" % (self.hname, self.lineno))
|
||||
sys.exit(-1)
|
||||
balance = 1
|
||||
while 1:
|
||||
t, npos3 = self.find_next_token(arg_str, ['(', ')'], npos3+1)
|
||||
if npos3 < 0:
|
||||
print("Error: no matching ')' in the macro call at %d" % (self.lineno,))
|
||||
print("Error: no matching ')' in the macro call at %s:%d" % (self.hname, self.lineno))
|
||||
sys.exit(-1)
|
||||
if t == '(':
|
||||
balance += 1
|
||||
@@ -168,7 +168,7 @@ class CppHeaderParser(object):
|
||||
angle_stack.append(0)
|
||||
elif w == "," or w == '>':
|
||||
if not angle_stack:
|
||||
print("Error at %d: argument contains ',' or '>' not within template arguments" % (self.lineno,))
|
||||
print("Error at %s:%d: argument contains ',' or '>' not within template arguments" % (self.hname, self.lineno))
|
||||
sys.exit(-1)
|
||||
if w == ",":
|
||||
arg_type += "_and_"
|
||||
@@ -198,7 +198,7 @@ class CppHeaderParser(object):
|
||||
p1 = arg_name.find("[")
|
||||
p2 = arg_name.find("]",p1+1)
|
||||
if p2 < 0:
|
||||
print("Error at %d: no closing ]" % (self.lineno,))
|
||||
print("Error at %s:%d: no closing ]" % (self.hname, self.lineno))
|
||||
sys.exit(-1)
|
||||
counter_str = arg_name[p1+1:p2].strip()
|
||||
if counter_str == "":
|
||||
@@ -443,11 +443,18 @@ class CppHeaderParser(object):
|
||||
# filter off some common prefixes, which are meaningless for Python wrappers.
|
||||
# note that we do not strip "static" prefix, which does matter;
|
||||
# it means class methods, not instance methods
|
||||
decl_str = self.batch_replace(decl_str, [("static inline", ""), ("inline", ""), ("explicit ", ""),
|
||||
("CV_EXPORTS_W", ""), ("CV_EXPORTS", ""), ("CV_CDECL", ""),
|
||||
("CV_WRAP ", " "), ("CV_INLINE", ""),
|
||||
("CV_DEPRECATED", ""), ("CV_DEPRECATED_EXTERNAL", "")]).strip()
|
||||
|
||||
decl_str = self.batch_replace(decl_str, [("static inline", ""),
|
||||
("inline", ""),
|
||||
("explicit ", ""),
|
||||
("CV_EXPORTS_W", ""),
|
||||
("CV_EXPORTS", ""),
|
||||
("CV_CDECL", ""),
|
||||
("CV_WRAP ", " "),
|
||||
("CV_INLINE", ""),
|
||||
("CV_DEPRECATED", ""),
|
||||
("CV_DEPRECATED_EXTERNAL", ""),
|
||||
("CV_NODISCARD_STD", ""),
|
||||
("CV_NODISCARD", "")]).strip()
|
||||
|
||||
if decl_str.strip().startswith('virtual'):
|
||||
virtual_method = True
|
||||
@@ -843,6 +850,7 @@ class CppHeaderParser(object):
|
||||
("GAPI_EXPORTS_W_SIMPLE","CV_EXPORTS_W_SIMPLE"),
|
||||
("GAPI_WRAP", "CV_WRAP"),
|
||||
("GAPI_PROP", "CV_PROP"),
|
||||
("GAPI_PROP_RW", "CV_PROP_RW"),
|
||||
('defined(GAPI_STANDALONE)', '0'),
|
||||
])
|
||||
|
||||
@@ -989,7 +997,8 @@ class CppHeaderParser(object):
|
||||
has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
|
||||
if has_mat:
|
||||
_, _, _, gpumat_decl = self.parse_stmt(stmt, token, mat="cuda::GpuMat", docstring=docstring)
|
||||
decls.append(gpumat_decl)
|
||||
if gpumat_decl != decl:
|
||||
decls.append(gpumat_decl)
|
||||
|
||||
if self._generate_umat_decls:
|
||||
# If function takes as one of arguments Mat or vector<Mat> - we want to create the
|
||||
@@ -998,7 +1007,8 @@ class CppHeaderParser(object):
|
||||
has_mat = len(list(filter(lambda x: x[0] in {"Mat", "vector_Mat"}, args))) > 0
|
||||
if has_mat:
|
||||
_, _, _, umat_decl = self.parse_stmt(stmt, token, mat="UMat", docstring=docstring)
|
||||
decls.append(umat_decl)
|
||||
if umat_decl != decl:
|
||||
decls.append(umat_decl)
|
||||
|
||||
docstring = ""
|
||||
if stmt_type == "namespace":
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
"""Algorithm serialization test."""
|
||||
from __future__ import print_function
|
||||
import base64
|
||||
import json
|
||||
import tempfile
|
||||
import os
|
||||
import cv2 as cv
|
||||
@@ -109,5 +111,96 @@ class filestorage_io_test(NewOpenCVTests):
|
||||
def test_json(self):
|
||||
self.run_fs_test(".json")
|
||||
|
||||
def test_base64(self):
|
||||
fd, fname = tempfile.mkstemp(prefix="opencv_python_sample_filestorage_base64", suffix=".json")
|
||||
os.close(fd)
|
||||
np.random.seed(42)
|
||||
self.write_base64_json(fname)
|
||||
os.remove(fname)
|
||||
|
||||
@staticmethod
|
||||
def get_normal_2d_mat():
|
||||
rows = 10
|
||||
cols = 20
|
||||
cn = 3
|
||||
|
||||
image = np.zeros((rows, cols, cn), np.uint8)
|
||||
image[:] = (1, 2, 127)
|
||||
|
||||
for i in range(rows):
|
||||
for j in range(cols):
|
||||
image[i, j, 1] = (i + j) % 256
|
||||
|
||||
return image
|
||||
|
||||
@staticmethod
|
||||
def get_normal_nd_mat():
|
||||
shape = (2, 2, 1, 2)
|
||||
cn = 4
|
||||
|
||||
image = np.zeros(shape + (cn,), np.float64)
|
||||
image[:] = (0.888, 0.111, 0.666, 0.444)
|
||||
|
||||
return image
|
||||
|
||||
@staticmethod
|
||||
def get_empty_2d_mat():
|
||||
shape = (0, 0)
|
||||
cn = 1
|
||||
|
||||
image = np.zeros(shape + (cn,), np.uint8)
|
||||
|
||||
return image
|
||||
|
||||
@staticmethod
|
||||
def get_random_mat():
|
||||
rows = 8
|
||||
cols = 16
|
||||
cn = 1
|
||||
|
||||
image = np.random.rand(rows, cols, cn)
|
||||
|
||||
return image
|
||||
|
||||
@staticmethod
|
||||
def decode(data):
|
||||
# strip $base64$
|
||||
encoded = data[8:]
|
||||
|
||||
if len(encoded) == 0:
|
||||
return b''
|
||||
|
||||
# strip info about datatype and padding
|
||||
return base64.b64decode(encoded)[24:]
|
||||
|
||||
def write_base64_json(self, fname):
|
||||
fs = cv.FileStorage(fname, cv.FileStorage_WRITE_BASE64)
|
||||
|
||||
mats = {'normal_2d_mat': self.get_normal_2d_mat(),
|
||||
'normal_nd_mat': self.get_normal_nd_mat(),
|
||||
'empty_2d_mat': self.get_empty_2d_mat(),
|
||||
'random_mat': self.get_random_mat()}
|
||||
|
||||
for name, mat in mats.items():
|
||||
fs.write(name, mat)
|
||||
|
||||
fs.release()
|
||||
|
||||
data = {}
|
||||
with open(fname) as file:
|
||||
data = json.load(file)
|
||||
|
||||
for name, mat in mats.items():
|
||||
buffer = b''
|
||||
|
||||
if mat.size != 0:
|
||||
if hasattr(mat, 'tobytes'):
|
||||
buffer = mat.tobytes()
|
||||
else:
|
||||
buffer = mat.tostring()
|
||||
|
||||
self.assertEqual(buffer, self.decode(data[name]['data']))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
|
||||
@@ -80,5 +80,52 @@ class houghcircles_test(NewOpenCVTests):
|
||||
self.assertLess(float(len(circles) - matches_counter) / len(circles), .75)
|
||||
|
||||
|
||||
def test_houghcircles_alt(self):
|
||||
|
||||
fn = "samples/data/board.jpg"
|
||||
|
||||
src = self.get_sample(fn, 1)
|
||||
img = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
|
||||
img = cv.medianBlur(img, 5)
|
||||
|
||||
circles = cv.HoughCircles(img, cv.HOUGH_GRADIENT_ALT, 1, 10, np.array([]), 300, 0.9, 1, 30)
|
||||
|
||||
self.assertEqual(circles.shape, (1, 18, 3))
|
||||
|
||||
circles = circles[0]
|
||||
|
||||
testCircles = [[38, 181, 17.6],
|
||||
[99.7, 166, 13.12],
|
||||
[142.7, 160, 13.52],
|
||||
[223.6, 110, 8.62],
|
||||
[79.1, 206.7, 8.62],
|
||||
[47.5, 351.6, 11.64],
|
||||
[189.5, 354.4, 11.64],
|
||||
[189.8, 298.9, 10.64],
|
||||
[189.5, 252.4, 14.62],
|
||||
[252.5, 393.4, 15.62],
|
||||
[602.9, 467.5, 11.42],
|
||||
[222, 210.4, 9.12],
|
||||
[263.1, 216.7, 9.12],
|
||||
[359.8, 222.6, 9.12],
|
||||
[518.9, 120.9, 9.12],
|
||||
[413.8, 113.4, 9.12],
|
||||
[489, 127.2, 9.12],
|
||||
[448.4, 121.3, 9.12],
|
||||
[384.6, 128.9, 8.62]]
|
||||
|
||||
matches_counter = 0
|
||||
|
||||
for i in range(len(testCircles)):
|
||||
for j in range(len(circles)):
|
||||
|
||||
tstCircle = circleApproximation(testCircles[i])
|
||||
circle = circleApproximation(circles[j])
|
||||
if convContoursIntersectiponRate(tstCircle, circle) > 0.6:
|
||||
matches_counter += 1
|
||||
|
||||
self.assertGreater(float(matches_counter) / len(testCircles), .5)
|
||||
self.assertLess(float(len(circles) - matches_counter) / len(circles), .75)
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
|
||||
@@ -20,8 +20,13 @@ class Hackathon244Tests(NewOpenCVTests):
|
||||
flag, ajpg = cv.imencode("img_q90.jpg", a, [cv.IMWRITE_JPEG_QUALITY, 90])
|
||||
self.assertEqual(flag, True)
|
||||
self.assertEqual(ajpg.dtype, np.uint8)
|
||||
self.assertGreater(ajpg.shape[0], 1)
|
||||
self.assertEqual(ajpg.shape[1], 1)
|
||||
self.assertTrue(isinstance(ajpg, np.ndarray), "imencode returned buffer of wrong type: {}".format(type(ajpg)))
|
||||
self.assertEqual(len(ajpg.shape), 1, "imencode returned buffer with wrong shape: {}".format(ajpg.shape))
|
||||
self.assertGreaterEqual(len(ajpg), 1, "imencode length of the returned buffer should be at least 1")
|
||||
self.assertLessEqual(
|
||||
len(ajpg), a.size,
|
||||
"imencode length of the returned buffer shouldn't exceed number of elements in original image"
|
||||
)
|
||||
|
||||
def test_projectPoints(self):
|
||||
objpt = np.float64([[1,2,3]])
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
#!/usr/bin/env python
|
||||
from __future__ import print_function
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
from tests_common import NewOpenCVTests
|
||||
|
||||
try:
|
||||
if sys.version_info[:2] < (3, 0):
|
||||
raise unittest.SkipTest('Python 2.x is not supported')
|
||||
|
||||
|
||||
class MatTest(NewOpenCVTests):
|
||||
|
||||
def test_mat_construct(self):
|
||||
data = np.random.random([10, 10, 3])
|
||||
|
||||
#print(np.ndarray.__dictoffset__) # 0
|
||||
#print(cv.Mat.__dictoffset__) # 88 (> 0)
|
||||
#print(cv.Mat) # <class cv2.Mat>
|
||||
#print(cv.Mat.__base__) # <class 'numpy.ndarray'>
|
||||
|
||||
mat_data0 = cv.Mat(data)
|
||||
assert isinstance(mat_data0, cv.Mat)
|
||||
assert isinstance(mat_data0, np.ndarray)
|
||||
self.assertEqual(mat_data0.wrap_channels, False)
|
||||
res0 = cv.utils.dumpInputArray(mat_data0)
|
||||
self.assertEqual(res0, "InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=300 dims(-1)=3 size(-1)=[10 10 3] type(-1)=CV_64FC1")
|
||||
|
||||
mat_data1 = cv.Mat(data, wrap_channels=True)
|
||||
assert isinstance(mat_data1, cv.Mat)
|
||||
assert isinstance(mat_data1, np.ndarray)
|
||||
self.assertEqual(mat_data1.wrap_channels, True)
|
||||
res1 = cv.utils.dumpInputArray(mat_data1)
|
||||
self.assertEqual(res1, "InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=100 dims(-1)=2 size(-1)=10x10 type(-1)=CV_64FC3")
|
||||
|
||||
mat_data2 = cv.Mat(mat_data1)
|
||||
assert isinstance(mat_data2, cv.Mat)
|
||||
assert isinstance(mat_data2, np.ndarray)
|
||||
self.assertEqual(mat_data2.wrap_channels, True) # fail if __array_finalize__ doesn't work
|
||||
res2 = cv.utils.dumpInputArray(mat_data2)
|
||||
self.assertEqual(res2, "InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=100 dims(-1)=2 size(-1)=10x10 type(-1)=CV_64FC3")
|
||||
|
||||
|
||||
def test_mat_construct_4d(self):
|
||||
data = np.random.random([5, 10, 10, 3])
|
||||
|
||||
mat_data0 = cv.Mat(data)
|
||||
assert isinstance(mat_data0, cv.Mat)
|
||||
assert isinstance(mat_data0, np.ndarray)
|
||||
self.assertEqual(mat_data0.wrap_channels, False)
|
||||
res0 = cv.utils.dumpInputArray(mat_data0)
|
||||
self.assertEqual(res0, "InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=1500 dims(-1)=4 size(-1)=[5 10 10 3] type(-1)=CV_64FC1")
|
||||
|
||||
mat_data1 = cv.Mat(data, wrap_channels=True)
|
||||
assert isinstance(mat_data1, cv.Mat)
|
||||
assert isinstance(mat_data1, np.ndarray)
|
||||
self.assertEqual(mat_data1.wrap_channels, True)
|
||||
res1 = cv.utils.dumpInputArray(mat_data1)
|
||||
self.assertEqual(res1, "InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=500 dims(-1)=3 size(-1)=[5 10 10] type(-1)=CV_64FC3")
|
||||
|
||||
mat_data2 = cv.Mat(mat_data1)
|
||||
assert isinstance(mat_data2, cv.Mat)
|
||||
assert isinstance(mat_data2, np.ndarray)
|
||||
self.assertEqual(mat_data2.wrap_channels, True) # __array_finalize__ doesn't work
|
||||
res2 = cv.utils.dumpInputArray(mat_data2)
|
||||
self.assertEqual(res2, "InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=500 dims(-1)=3 size(-1)=[5 10 10] type(-1)=CV_64FC3")
|
||||
|
||||
|
||||
def test_mat_wrap_channels_fail(self):
|
||||
data = np.random.random([2, 3, 4, 520])
|
||||
|
||||
mat_data0 = cv.Mat(data)
|
||||
assert isinstance(mat_data0, cv.Mat)
|
||||
assert isinstance(mat_data0, np.ndarray)
|
||||
self.assertEqual(mat_data0.wrap_channels, False)
|
||||
res0 = cv.utils.dumpInputArray(mat_data0)
|
||||
self.assertEqual(res0, "InputArray: empty()=false kind=0x00010000 flags=0x01010000 total(-1)=12480 dims(-1)=4 size(-1)=[2 3 4 520] type(-1)=CV_64FC1")
|
||||
|
||||
with self.assertRaises(cv.error):
|
||||
mat_data1 = cv.Mat(data, wrap_channels=True) # argument unable to wrap channels, too high (520 > CV_CN_MAX=512)
|
||||
res1 = cv.utils.dumpInputArray(mat_data1)
|
||||
print(mat_data1.__dict__)
|
||||
print(res1)
|
||||
|
||||
|
||||
def test_ufuncs(self):
|
||||
data = np.arange(10)
|
||||
mat_data = cv.Mat(data)
|
||||
mat_data2 = 2 * mat_data
|
||||
self.assertEqual(type(mat_data2), cv.Mat)
|
||||
np.testing.assert_equal(2 * data, 2 * mat_data)
|
||||
|
||||
|
||||
def test_comparison(self):
|
||||
# Undefined behavior, do NOT use that.
|
||||
# Behavior may be changed in the future
|
||||
|
||||
data = np.ones((10, 10, 3))
|
||||
mat_wrapped = cv.Mat(data, wrap_channels=True)
|
||||
mat_simple = cv.Mat(data)
|
||||
np.testing.assert_equal(mat_wrapped, mat_simple) # ???: wrap_channels is not checked for now
|
||||
np.testing.assert_equal(data, mat_simple)
|
||||
np.testing.assert_equal(data, mat_wrapped)
|
||||
|
||||
#self.assertEqual(mat_wrapped, mat_simple) # ???
|
||||
#self.assertTrue(mat_wrapped == mat_simple) # ???
|
||||
#self.assertTrue((mat_wrapped == mat_simple).all())
|
||||
|
||||
|
||||
except unittest.SkipTest as e:
|
||||
|
||||
message = str(e)
|
||||
|
||||
class TestSkip(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.skipTest('Skip tests: ' + message)
|
||||
|
||||
def test_skip():
|
||||
pass
|
||||
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
NewOpenCVTests.bootstrap()
|
||||
@@ -4,6 +4,12 @@ from __future__ import print_function
|
||||
import ctypes
|
||||
from functools import partial
|
||||
from collections import namedtuple
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
from collections import Sequence
|
||||
else:
|
||||
from collections.abc import Sequence
|
||||
|
||||
import numpy as np
|
||||
import cv2 as cv
|
||||
@@ -464,6 +470,151 @@ class Arguments(NewOpenCVTests):
|
||||
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
|
||||
_ = cv.utils.dumpRange(not_convertible)
|
||||
|
||||
def test_reserved_keywords_are_transformed(self):
|
||||
default_lambda_value = 2
|
||||
default_from_value = 3
|
||||
format_str = "arg={}, lambda={}, from={}"
|
||||
self.assertEqual(
|
||||
cv.utils.testReservedKeywordConversion(20), format_str.format(20, default_lambda_value, default_from_value)
|
||||
)
|
||||
self.assertEqual(
|
||||
cv.utils.testReservedKeywordConversion(10, lambda_=10), format_str.format(10, 10, default_from_value)
|
||||
)
|
||||
self.assertEqual(
|
||||
cv.utils.testReservedKeywordConversion(10, from_=10), format_str.format(10, default_lambda_value, 10)
|
||||
)
|
||||
self.assertEqual(
|
||||
cv.utils.testReservedKeywordConversion(20, lambda_=-4, from_=12), format_str.format(20, -4, 12)
|
||||
)
|
||||
|
||||
def test_parse_vector_int_convertible(self):
|
||||
np.random.seed(123098765)
|
||||
try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfInt)
|
||||
arr = np.random.randint(-20, 20, 40).astype(np.int32).reshape(10, 2, 2)
|
||||
int_min, int_max = get_limits(ctypes.c_int)
|
||||
for convertible in ((int_min, 1, 2, 3, int_max), [40, 50], tuple(),
|
||||
np.array([int_min, -10, 24, int_max], dtype=np.int32),
|
||||
np.array([10, 230, 12], dtype=np.uint8), arr[:, 0, 1],):
|
||||
expected = "[" + ", ".join(map(str, convertible)) + "]"
|
||||
actual = try_to_convert(convertible)
|
||||
self.assertEqual(expected, actual,
|
||||
msg=get_conversion_error_msg(convertible, expected, actual))
|
||||
|
||||
def test_parse_vector_int_not_convertible(self):
|
||||
np.random.seed(123098765)
|
||||
arr = np.random.randint(-20, 20, 40).astype(np.float).reshape(10, 2, 2)
|
||||
int_min, int_max = get_limits(ctypes.c_int)
|
||||
test_dict = {1: 2, 3: 10, 10: 20}
|
||||
for not_convertible in ((int_min, 1, 2.5, 3, int_max), [True, 50], 'test', test_dict,
|
||||
reversed([1, 2, 3]),
|
||||
np.array([int_min, -10, 24, [1, 2]], dtype=np.object),
|
||||
np.array([[1, 2], [3, 4]]), arr[:, 0, 1],):
|
||||
with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)):
|
||||
_ = cv.utils.dumpVectorOfInt(not_convertible)
|
||||
|
||||
def test_parse_vector_double_convertible(self):
|
||||
np.random.seed(1230965)
|
||||
try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfDouble)
|
||||
arr = np.random.randint(-20, 20, 40).astype(np.int32).reshape(10, 2, 2)
|
||||
for convertible in ((1, 2.12, 3.5), [40, 50], tuple(),
|
||||
np.array([-10, 24], dtype=np.int32),
|
||||
np.array([-12.5, 1.4], dtype=np.double),
|
||||
np.array([10, 230, 12], dtype=np.float), arr[:, 0, 1], ):
|
||||
expected = "[" + ", ".join(map(lambda v: "{:.2f}".format(v), convertible)) + "]"
|
||||
actual = try_to_convert(convertible)
|
||||
self.assertEqual(expected, actual,
|
||||
msg=get_conversion_error_msg(convertible, expected, actual))
|
||||
|
||||
def test_parse_vector_double_not_convertible(self):
|
||||
test_dict = {1: 2, 3: 10, 10: 20}
|
||||
for not_convertible in (('t', 'e', 's', 't'), [True, 50.55], 'test', test_dict,
|
||||
np.array([-10.1, 24.5, [1, 2]], dtype=np.object),
|
||||
np.array([[1, 2], [3, 4]]),):
|
||||
with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)):
|
||||
_ = cv.utils.dumpVectorOfDouble(not_convertible)
|
||||
|
||||
def test_parse_vector_rect_convertible(self):
|
||||
np.random.seed(1238765)
|
||||
try_to_convert = partial(self._try_to_convert, cv.utils.dumpVectorOfRect)
|
||||
arr_of_rect_int32 = np.random.randint(5, 20, 4 * 3).astype(np.int32).reshape(3, 4)
|
||||
arr_of_rect_cast = np.random.randint(10, 40, 4 * 5).astype(np.uint8).reshape(5, 4)
|
||||
for convertible in (((1, 2, 3, 4), (10, -20, 30, 10)), arr_of_rect_int32, arr_of_rect_cast,
|
||||
arr_of_rect_int32.astype(np.int8), [[5, 3, 1, 4]],
|
||||
((np.int8(4), np.uint8(10), np.int(32), np.int16(55)),)):
|
||||
expected = "[" + ", ".join(map(lambda v: "[x={}, y={}, w={}, h={}]".format(*v), convertible)) + "]"
|
||||
actual = try_to_convert(convertible)
|
||||
self.assertEqual(expected, actual,
|
||||
msg=get_conversion_error_msg(convertible, expected, actual))
|
||||
|
||||
def test_parse_vector_rect_not_convertible(self):
|
||||
np.random.seed(1238765)
|
||||
arr = np.random.randint(5, 20, 4 * 3).astype(np.float).reshape(3, 4)
|
||||
for not_convertible in (((1, 2, 3, 4), (10.5, -20, 30.1, 10)), arr,
|
||||
[[5, 3, 1, 4], []],
|
||||
((np.float(4), np.uint8(10), np.int(32), np.int16(55)),)):
|
||||
with self.assertRaises(TypeError, msg=get_no_exception_msg(not_convertible)):
|
||||
_ = cv.utils.dumpVectorOfRect(not_convertible)
|
||||
|
||||
def test_vector_general_return(self):
|
||||
expected_number_of_mats = 5
|
||||
expected_shape = (10, 10, 3)
|
||||
expected_type = np.uint8
|
||||
mats = cv.utils.generateVectorOfMat(5, 10, 10, cv.CV_8UC3)
|
||||
self.assertTrue(isinstance(mats, tuple),
|
||||
"Vector of Mats objects should be returned as tuple. Got: {}".format(type(mats)))
|
||||
self.assertEqual(len(mats), expected_number_of_mats, "Returned array has wrong length")
|
||||
for mat in mats:
|
||||
self.assertEqual(mat.shape, expected_shape, "Returned Mat has wrong shape")
|
||||
self.assertEqual(mat.dtype, expected_type, "Returned Mat has wrong elements type")
|
||||
empty_mats = cv.utils.generateVectorOfMat(0, 10, 10, cv.CV_32FC1)
|
||||
self.assertTrue(isinstance(empty_mats, tuple),
|
||||
"Empty vector should be returned as empty tuple. Got: {}".format(type(mats)))
|
||||
self.assertEqual(len(empty_mats), 0, "Vector of size 0 should be returned as tuple of length 0")
|
||||
|
||||
def test_vector_fast_return(self):
|
||||
expected_shape = (5, 4)
|
||||
rects = cv.utils.generateVectorOfRect(expected_shape[0])
|
||||
self.assertTrue(isinstance(rects, np.ndarray),
|
||||
"Vector of rectangles should be returned as numpy array. Got: {}".format(type(rects)))
|
||||
self.assertEqual(rects.dtype, np.int32, "Vector of rectangles has wrong elements type")
|
||||
self.assertEqual(rects.shape, expected_shape, "Vector of rectangles has wrong shape")
|
||||
empty_rects = cv.utils.generateVectorOfRect(0)
|
||||
self.assertTrue(isinstance(empty_rects, tuple),
|
||||
"Empty vector should be returned as empty tuple. Got: {}".format(type(empty_rects)))
|
||||
self.assertEqual(len(empty_rects), 0, "Vector of size 0 should be returned as tuple of length 0")
|
||||
|
||||
expected_shape = (10,)
|
||||
ints = cv.utils.generateVectorOfInt(expected_shape[0])
|
||||
self.assertTrue(isinstance(ints, np.ndarray),
|
||||
"Vector of integers should be returned as numpy array. Got: {}".format(type(ints)))
|
||||
self.assertEqual(ints.dtype, np.int32, "Vector of integers has wrong elements type")
|
||||
self.assertEqual(ints.shape, expected_shape, "Vector of integers has wrong shape.")
|
||||
|
||||
|
||||
class CanUsePurePythonModuleFunction(NewOpenCVTests):
|
||||
def test_can_get_ocv_version(self):
|
||||
import sys
|
||||
if sys.version_info[0] < 3:
|
||||
raise unittest.SkipTest('Python 2.x is not supported')
|
||||
|
||||
self.assertEqual(cv.misc.get_ocv_version(), cv.__version__,
|
||||
"Can't get package version using Python misc module")
|
||||
|
||||
def test_native_method_can_be_patched(self):
|
||||
import sys
|
||||
|
||||
if sys.version_info[0] < 3:
|
||||
raise unittest.SkipTest('Python 2.x is not supported')
|
||||
|
||||
res = cv.utils.testOverwriteNativeMethod(10)
|
||||
self.assertTrue(isinstance(res, Sequence),
|
||||
msg="Overwritten method should return sequence. "
|
||||
"Got: {} of type {}".format(res, type(res)))
|
||||
self.assertSequenceEqual(res, (11, 10),
|
||||
msg="Failed to overwrite native method")
|
||||
res = cv.utils._native.testOverwriteNativeMethod(123)
|
||||
self.assertEqual(res, 123, msg="Failed to call native method implementation")
|
||||
|
||||
|
||||
class SamplesFindFile(NewOpenCVTests):
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import random
|
||||
import argparse
|
||||
|
||||
import numpy as np
|
||||
#sys.OpenCV_LOADER_DEBUG = True
|
||||
import cv2 as cv
|
||||
|
||||
# Python 3 moved urlopen to urllib.requests
|
||||
|
||||
Reference in New Issue
Block a user