Merge remote-tracking branch 'upstream/3.4' into merge-3.4

This commit is contained in:
Alexander Alekhin 2021-01-25 22:34:41 +00:00
commit e85b41f9be
9 changed files with 419 additions and 189 deletions

View File

@ -78,6 +78,41 @@ String testOverloadResolution(const Rect& rect)
rect.width, rect.height); rect.width, rect.height);
} }
CV_WRAP static inline
String dumpRect(const Rect& argument)
{
return format("rect: (x=%d, y=%d, w=%d, h=%d)", argument.x, argument.y,
argument.width, argument.height);
}
CV_WRAP static inline
String dumpTermCriteria(const TermCriteria& argument)
{
return format("term_criteria: (type=%d, max_count=%d, epsilon=%lf",
argument.type, argument.maxCount, argument.epsilon);
}
CV_WRAP static inline
String dumpRotatedRect(const RotatedRect& argument)
{
return format("rotated_rect: (c_x=%f, c_y=%f, w=%f, h=%f, a=%f)",
argument.center.x, argument.center.y, argument.size.width,
argument.size.height, argument.angle);
}
CV_WRAP static inline
String dumpRange(const Range& argument)
{
if (argument == Range::all())
{
return "range: all";
}
else
{
return format("range: (s=%d, e=%d)", argument.start, argument.end);
}
}
CV_WRAP static inline CV_WRAP static inline
AsyncArray testAsyncArray(InputArray argument) AsyncArray testAsyncArray(InputArray argument)
{ {

View File

@ -1149,14 +1149,14 @@ void OpenCLExecutionContext::release()
} }
// true if we have initialized OpenCL subsystem with available platforms // true if we have initialized OpenCL subsystem with available platforms
static bool g_isOpenCLActivated = false; static bool g_isOpenCLInitialized = false;
static bool g_isOpenCLAvailable = false;
bool haveOpenCL() bool haveOpenCL()
{ {
CV_TRACE_FUNCTION(); CV_TRACE_FUNCTION();
static bool g_isOpenCLInitialized = false;
static bool g_isOpenCLAvailable = false;
if (!g_isOpenCLInitialized) if (!g_isOpenCLInitialized)
{ {
@ -1178,7 +1178,7 @@ bool haveOpenCL()
{ {
cl_uint n = 0; cl_uint n = 0;
g_isOpenCLAvailable = ::clGetPlatformIDs(0, NULL, &n) == CL_SUCCESS; g_isOpenCLAvailable = ::clGetPlatformIDs(0, NULL, &n) == CL_SUCCESS;
g_isOpenCLActivated = n > 0; g_isOpenCLAvailable &= n > 0;
CV_LOG_INFO(NULL, "OpenCL: found " << n << " platforms"); CV_LOG_INFO(NULL, "OpenCL: found " << n << " platforms");
} }
catch (...) catch (...)
@ -1214,7 +1214,7 @@ bool useOpenCL()
bool isOpenCLActivated() bool isOpenCLActivated()
{ {
if (!g_isOpenCLActivated) if (!g_isOpenCLAvailable)
return false; // prevent unnecessary OpenCL activation via useOpenCL()->haveOpenCL() calls return false; // prevent unnecessary OpenCL activation via useOpenCL()->haveOpenCL() calls
return useOpenCL(); return useOpenCL();
} }

View File

@ -177,7 +177,7 @@ class dnn_test(NewOpenCVTests):
cv.rectangle(frame, box, (0, 255, 0)) cv.rectangle(frame, box, (0, 255, 0))
cv.rectangle(frame, np.array(box), (0, 255, 0)) cv.rectangle(frame, np.array(box), (0, 255, 0))
cv.rectangle(frame, tuple(box), (0, 255, 0)) cv.rectangle(frame, tuple(box), (0, 255, 0))
# FIXIT never properly work: cv.rectangle(frame, list(box), (0, 255, 0)) cv.rectangle(frame, list(box), (0, 255, 0))
def test_classification_model(self): def test_classification_model(self):

View File

@ -1162,6 +1162,53 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
layerParams.type = "Scale"; layerParams.type = "Scale";
} }
} }
else if (!haveVariables)
{
Mat inp0 = getBlob(node_proto, 0);
Mat inp1 = getBlob(node_proto, 1);
if (inp0.size != inp1.size && (inp0.total() != 1 || inp1.total() != 1))
CV_Error_(Error::StsNotImplemented, ("Different shapes case is not supported with constant inputs: %s", layer_type.c_str()));
if (inp0.total() == 1 && inp1.total() == 1 && inp0.dims != inp1.dims)
{
if (inp0.dims < inp1.dims)
{
inp0 = inp0.reshape(1, inp1.dims, inp1.size);
inp0.dims = inp1.dims;
}
else
{
inp1 = inp1.reshape(1, inp0.dims, inp0.size);
inp1.dims = inp0.dims;
}
}
Mat out;
if (inp0.total() != inp1.total())
{
if (inp0.total() == 1)
{
float coeff = isDiv ? 1.0 / inp0.at<float>(0) : inp0.at<float>(0);
multiply(inp1, coeff, out);
}
else
{
float coeff = isDiv ? 1.0 / inp1.at<float>(0) : inp1.at<float>(0);
multiply(inp0, coeff, out);
}
}
else
{
out = isDiv ? inp0 / inp1 : inp0.mul(inp1);
}
if (inp0.dims == 1 && inp1.dims == 1)
out.dims = 1; // to workaround dims == 1
addConstant(layerParams.name, out);
return;
}
else if (outShapes[node_proto.input(0)] == outShapes[node_proto.input(1)]) else if (outShapes[node_proto.input(0)] == outShapes[node_proto.input(1)])
{ {
layerParams.type = "Eltwise"; layerParams.type = "Eltwise";
@ -1201,20 +1248,6 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
} }
layerParams.type = "Scale"; layerParams.type = "Scale";
} }
if (!haveVariables)
{
Mat inp0 = getBlob(node_proto, 0);
Mat inp1 = getBlob(node_proto, 1);
if (inp0.size != inp1.size && inp1.total() != 1)
CV_Error(Error::StsNotImplemented, "Constant multiply with different shapes");
Mat out = isDiv ? inp0 / inp1 : inp0.mul(inp1);
out = out.reshape(1, inp0.dims, inp0.size);
out.dims = inp0.dims; // to workaround dims == 1
addConstant(layerParams.name, out);
return;
}
} }
else if (layer_type == "Conv") else if (layer_type == "Conv")
{ {
@ -1733,9 +1766,26 @@ void ONNXImporter::handleNode(const opencv_onnx::NodeProto& node_proto_)
if (!hasVariableInps) if (!hasVariableInps)
{ {
std::vector<Mat> inputs(node_proto.input_size()), concatenated; std::vector<Mat> inputs(node_proto.input_size()), concatenated;
// Due constant folding we can get inputs with different number of dimensions
// Insert the missing dimension to inputs
MatShape inputShape;
for (size_t i = 0; i < inputs.size(); ++i) for (size_t i = 0; i < inputs.size(); ++i)
{ {
inputs[i] = getBlob(node_proto, i); inputs[i] = getBlob(node_proto, i);
if (inputs[i].size.dims() > inputShape.size())
{
inputShape = shape(inputs[i]);
}
}
// Concat-1 has default value for axis is 1: https://github.com/onnx/onnx/blob/master/docs/Changelog.md#Concat-1
int axis = layerParams.get<int>("axis", 1);
for (size_t i = 0; i < inputs.size(); ++i)
{
MatShape targetShape = inputShape;
targetShape[axis] = shape(inputs[i])[axis];
CV_CheckEQ(total(targetShape), total(shape(inputs[i])), "");
inputs[i] = inputs[i].reshape(0, targetShape);
} }
runLayer(layerParams, inputs, concatenated); runLayer(layerParams, inputs, concatenated);

View File

@ -1228,8 +1228,18 @@ void TFImporter::parseNode(const tensorflow::NodeDef& layer_)
int kernel_blob_index = -1; int kernel_blob_index = -1;
const tensorflow::TensorProto& kernelTensor = getConstBlob(layer, value_id, -1, &kernel_blob_index); const tensorflow::TensorProto& kernelTensor = getConstBlob(layer, value_id, -1, &kernel_blob_index);
blobFromTensor(kernelTensor, layerParams.blobs[0]); const String kernelTensorName = layer.input(kernel_blob_index);
releaseTensor(const_cast<tensorflow::TensorProto*>(&kernelTensor)); std::map<String, Mat>::iterator sharedWeightsIt = sharedWeights.find(kernelTensorName);
if (sharedWeightsIt == sharedWeights.end())
{
blobFromTensor(kernelTensor, layerParams.blobs[0]);
releaseTensor(const_cast<tensorflow::TensorProto*>(&kernelTensor));
sharedWeights[kernelTensorName] = layerParams.blobs[0];
}
else
{
layerParams.blobs[0] = sharedWeightsIt->second;
}
if (kernel_blob_index == 1) { // In this case output is computed by x*W formula - W should be transposed if (kernel_blob_index == 1) { // In this case output is computed by x*W formula - W should be transposed
Mat data = layerParams.blobs[0].t(); Mat data = layerParams.blobs[0].t();

View File

@ -706,6 +706,11 @@ TEST_P(Test_ONNX_layers, Mish)
testONNXModels("mish"); testONNXModels("mish");
} }
TEST_P(Test_ONNX_layers, CalculatePads)
{
testONNXModels("calc_pads");
}
TEST_P(Test_ONNX_layers, Conv1d) TEST_P(Test_ONNX_layers, Conv1d)
{ {
testONNXModels("conv1d"); testONNXModels("conv1d");

View File

@ -1118,26 +1118,20 @@ static void icvScreenToClient( HWND hwnd, RECT* rect )
/* Calculatess the window coordinates relative to the upper left corner of the mainhWnd window */ /* Calculatess the window coordinates relative to the upper left corner of the mainhWnd window */
static RECT icvCalcWindowRect( CvWindow* window ) static RECT icvCalcWindowRect( CvWindow* window )
{ {
const int gutter = 1; RECT crect = { 0 }, trect = { 0 }, rect = { 0 };
RECT crect = { 0 }, trect = { 0 } , rect = { 0 };
assert(window); assert(window);
GetClientRect(window->frame, &crect); GetClientRect(window->frame, &crect);
if(window->toolbar.toolbar) if (window->toolbar.toolbar)
{ {
GetWindowRect(window->toolbar.toolbar, &trect); GetWindowRect(window->toolbar.toolbar, &trect);
icvScreenToClient(window->frame, &trect); icvScreenToClient(window->frame, &trect);
SubtractRect( &rect, &crect, &trect); SubtractRect(&rect, &crect, &trect);
} }
else else
rect = crect; rect = crect;
rect.top += gutter;
rect.left += gutter;
rect.bottom -= gutter;
rect.right -= gutter;
return rect; return rect;
} }

View File

@ -407,6 +407,63 @@ void pyPopulateArgumentConversionErrors()
} }
} }
struct SafeSeqItem
{
PyObject * item;
SafeSeqItem(PyObject *obj, size_t idx) { item = PySequence_GetItem(obj, idx); }
~SafeSeqItem() { Py_XDECREF(item); }
private:
SafeSeqItem(const SafeSeqItem&); // = delete
SafeSeqItem& operator=(const SafeSeqItem&); // = delete
};
template <class T>
class RefWrapper
{
public:
RefWrapper(T& item) : item_(item) {}
T& get() CV_NOEXCEPT { return item_; }
private:
T& item_;
};
// In order to support this conversion on 3.x branch - use custom reference_wrapper
// and C-style array instead of std::array<T, N>
template <class T, std::size_t N>
bool parseSequence(PyObject* obj, RefWrapper<T> (&value)[N], 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 std::size_t sequenceSize = PySequence_Size(obj);
if (sequenceSize != N)
{
failmsg("Can't parse '%s'. Expected sequence length %lu, got %lu",
info.name, N, sequenceSize);
return false;
}
for (std::size_t i = 0; i < N; ++i)
{
SafeSeqItem seqItem(obj, i);
if (!pyopencv_to(seqItem.item, value[i].get(), info))
{
failmsg("Can't parse '%s'. Sequence item with index %lu has a "
"wrong type", info.name, i);
return false;
}
}
return true;
}
} // namespace } // namespace
typedef std::vector<uchar> vector_uchar; typedef std::vector<uchar> vector_uchar;
@ -781,13 +838,6 @@ static PyObject* pyopencv_from(void*& ptr)
return PyLong_FromVoidPtr(ptr); return PyLong_FromVoidPtr(ptr);
} }
struct SafeSeqItem
{
PyObject * item;
SafeSeqItem(PyObject *obj, size_t idx) { item = PySequence_GetItem(obj, idx); }
~SafeSeqItem() { Py_XDECREF(item); }
};
static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo& info) static bool pyopencv_to(PyObject *o, Scalar& s, const ArgInfo& info)
{ {
if(!o || o == Py_None) if(!o || o == Py_None)
@ -1138,10 +1188,9 @@ bool pyopencv_to(PyObject* obj, String &value, const ArgInfo& info)
template<> template<>
bool pyopencv_to(PyObject* obj, Size& sz, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Size& sz, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(sz.width),
if(!obj || obj == Py_None) RefWrapper<int>(sz.height)};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "ii", &sz.width, &sz.height) > 0;
} }
template<> template<>
@ -1153,10 +1202,9 @@ PyObject* pyopencv_from(const Size& sz)
template<> template<>
bool pyopencv_to(PyObject* obj, Size_<float>& sz, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Size_<float>& sz, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(sz.width),
if(!obj || obj == Py_None) RefWrapper<float>(sz.height)};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "ff", &sz.width, &sz.height) > 0;
} }
template<> template<>
@ -1165,6 +1213,15 @@ PyObject* pyopencv_from(const Size_<float>& sz)
return Py_BuildValue("(ff)", sz.width, sz.height); return Py_BuildValue("(ff)", sz.width, sz.height);
} }
template<>
bool pyopencv_to(PyObject* obj, Rect& r, const ArgInfo& info)
{
RefWrapper<int> values[] = {RefWrapper<int>(r.x), RefWrapper<int>(r.y),
RefWrapper<int>(r.width),
RefWrapper<int>(r.height)};
return parseSequence(obj, values, info);
}
template<> template<>
PyObject* pyopencv_from(const Rect& r) PyObject* pyopencv_from(const Rect& r)
{ {
@ -1174,10 +1231,10 @@ PyObject* pyopencv_from(const Rect& r)
template<> template<>
bool pyopencv_to(PyObject* obj, Rect2d& r, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Rect2d& r, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {
if(!obj || obj == Py_None) RefWrapper<double>(r.x), RefWrapper<double>(r.y),
return true; RefWrapper<double>(r.width), RefWrapper<double>(r.height)};
return PyArg_ParseTuple(obj, "dddd", &r.x, &r.y, &r.width, &r.height) > 0; return parseSequence(obj, values, info);
} }
template<> template<>
@ -1189,44 +1246,17 @@ PyObject* pyopencv_from(const Rect2d& r)
template<> template<>
bool pyopencv_to(PyObject* obj, Range& r, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Range& r, const ArgInfo& info)
{ {
CV_UNUSED(info); if (!obj || obj == Py_None)
if(!obj || obj == Py_None)
return true;
while (PySequence_Check(obj))
{ {
if (2 != PySequence_Size(obj))
{
failmsg("Range value for argument '%s' is longer than 2", info.name);
return false;
}
{
SafeSeqItem item_wrap(obj, 0);
PyObject *item = item_wrap.item;
if (PyInt_Check(item)) {
r.start = (int)PyInt_AsLong(item);
} else {
failmsg("Range.start value for argument '%s' is not integer", info.name);
break;
}
}
{
SafeSeqItem item_wrap(obj, 1);
PyObject *item = item_wrap.item;
if (PyInt_Check(item)) {
r.end = (int)PyInt_AsLong(item);
} else {
failmsg("Range.end value for argument '%s' is not integer", info.name);
break;
}
}
return true; return true;
} }
if(PyObject_Size(obj) == 0) if (PyObject_Size(obj) == 0)
{ {
r = Range::all(); r = Range::all();
return true; return true;
} }
return PyArg_ParseTuple(obj, "ii", &r.start, &r.end) > 0; RefWrapper<int> values[] = {RefWrapper<int>(r.start), RefWrapper<int>(r.end)};
return parseSequence(obj, values, info);
} }
template<> template<>
@ -1238,64 +1268,42 @@ PyObject* pyopencv_from(const Range& r)
template<> template<>
bool pyopencv_to(PyObject* obj, Point& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(p.x), RefWrapper<int>(p.y)};
if(!obj || obj == Py_None) return parseSequence(obj, values, info);
return true;
if(PyComplex_Check(obj))
{
p.x = saturate_cast<int>(PyComplex_RealAsDouble(obj));
p.y = saturate_cast<int>(PyComplex_ImagAsDouble(obj));
return true;
}
return PyArg_ParseTuple(obj, "ii", &p.x, &p.y) > 0;
} }
template<> template <>
bool pyopencv_to(PyObject* obj, Point2f& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point2f& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(p.x),
if(!obj || obj == Py_None) RefWrapper<float>(p.y)};
return true; return parseSequence(obj, values, info);
if (PyComplex_Check(obj))
{
p.x = saturate_cast<float>(PyComplex_RealAsDouble(obj));
p.y = saturate_cast<float>(PyComplex_ImagAsDouble(obj));
return true;
}
return PyArg_ParseTuple(obj, "ff", &p.x, &p.y) > 0;
} }
template<> template<>
bool pyopencv_to(PyObject* obj, Point2d& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point2d& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(p.x),
if(!obj || obj == Py_None) RefWrapper<double>(p.y)};
return true; return parseSequence(obj, values, info);
if(PyComplex_Check(obj))
{
p.x = PyComplex_RealAsDouble(obj);
p.y = PyComplex_ImagAsDouble(obj);
return true;
}
return PyArg_ParseTuple(obj, "dd", &p.x, &p.y) > 0;
} }
template<> template<>
bool pyopencv_to(PyObject* obj, Point3f& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point3f& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(p.x),
if(!obj || obj == Py_None) RefWrapper<float>(p.y),
return true; RefWrapper<float>(p.z)};
return PyArg_ParseTuple(obj, "fff", &p.x, &p.y, &p.z) > 0; return parseSequence(obj, values, info);
} }
template<> template<>
bool pyopencv_to(PyObject* obj, Point3d& p, const ArgInfo& info) bool pyopencv_to(PyObject* obj, Point3d& p, const ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(p.x),
if(!obj || obj == Py_None) RefWrapper<double>(p.y),
return true; RefWrapper<double>(p.z)};
return PyArg_ParseTuple(obj, "ddd", &p.x, &p.y, &p.z) > 0; return parseSequence(obj, values, info);
} }
template<> template<>
@ -1318,74 +1326,66 @@ PyObject* pyopencv_from(const Point3f& p)
static bool pyopencv_to(PyObject* obj, Vec4d& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec4d& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(v[0]), RefWrapper<double>(v[1]),
if (!obj) RefWrapper<double>(v[2]), RefWrapper<double>(v[3])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "dddd", &v[0], &v[1], &v[2], &v[3]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec4f& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec4f& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(v[0]), RefWrapper<float>(v[1]),
if (!obj) RefWrapper<float>(v[2]), RefWrapper<float>(v[3])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "ffff", &v[0], &v[1], &v[2], &v[3]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec4i& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec4i& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1]),
if (!obj) RefWrapper<int>(v[2]), RefWrapper<int>(v[3])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "iiii", &v[0], &v[1], &v[2], &v[3]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec3d& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec3d& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(v[0]),
if (!obj) RefWrapper<double>(v[1]),
return true; RefWrapper<double>(v[2])};
return PyArg_ParseTuple(obj, "ddd", &v[0], &v[1], &v[2]) > 0; return parseSequence(obj, values, info);
} }
static bool pyopencv_to(PyObject* obj, Vec3f& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec3f& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(v[0]),
if (!obj) RefWrapper<float>(v[1]),
return true; RefWrapper<float>(v[2])};
return PyArg_ParseTuple(obj, "fff", &v[0], &v[1], &v[2]) > 0; return parseSequence(obj, values, info);
} }
static bool pyopencv_to(PyObject* obj, Vec3i& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec3i& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1]),
if (!obj) RefWrapper<int>(v[2])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "iii", &v[0], &v[1], &v[2]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec2d& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec2d& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<double> values[] = {RefWrapper<double>(v[0]),
if (!obj) RefWrapper<double>(v[1])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "dd", &v[0], &v[1]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec2f& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec2f& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<float> values[] = {RefWrapper<float>(v[0]),
if (!obj) RefWrapper<float>(v[1])};
return true; return parseSequence(obj, values, info);
return PyArg_ParseTuple(obj, "ff", &v[0], &v[1]) > 0;
} }
static bool pyopencv_to(PyObject* obj, Vec2i& v, ArgInfo& info) static bool pyopencv_to(PyObject* obj, Vec2i& v, ArgInfo& info)
{ {
CV_UNUSED(info); RefWrapper<int> values[] = {RefWrapper<int>(v[0]), RefWrapper<int>(v[1])};
if (!obj) return parseSequence(obj, values, info);
return true;
return PyArg_ParseTuple(obj, "ii", &v[0], &v[1]) > 0;
} }
template<> template<>
@ -1766,39 +1766,54 @@ template<> struct pyopencvVecConverter<RotatedRect>
}; };
template<> template<>
bool pyopencv_to(PyObject* obj, Rect& r, const ArgInfo& info) bool pyopencv_to(PyObject* obj, TermCriteria& dst, const ArgInfo& info)
{ {
CV_UNUSED(info); if (!obj || obj == Py_None)
if(!obj || obj == Py_None)
return true;
if (PyTuple_Check(obj))
return PyArg_ParseTuple(obj, "iiii", &r.x, &r.y, &r.width, &r.height) > 0;
else
{ {
std::vector<int> value(4);
if (!pyopencvVecConverter<int>::to(obj, value, info))
{
return false;
}
if (value.size() != 4)
{
failmsg("Expected 4 values for '%s', got %d", info.name, (int)value.size());
return false;
}
r = Rect(value[0], value[1], value[2], value[3]);
return true; return true;
} }
if (!PySequence_Check(obj))
} {
failmsg("Can't parse '%s' as TermCriteria."
template<> "Input argument doesn't provide sequence protocol",
bool pyopencv_to(PyObject *obj, TermCriteria& dst, const ArgInfo& info) info.name);
{ return false;
CV_UNUSED(info); }
if(!obj) const std::size_t sequenceSize = PySequence_Size(obj);
return true; if (sequenceSize != 3) {
return PyArg_ParseTuple(obj, "iid", &dst.type, &dst.maxCount, &dst.epsilon) > 0; failmsg("Can't parse '%s' as TermCriteria. Expected sequence length 3, "
"got %lu",
info.name, sequenceSize);
return false;
}
{
const String typeItemName = format("'%s' criteria type", info.name);
const ArgInfo typeItemInfo(typeItemName.c_str(), false);
SafeSeqItem typeItem(obj, 0);
if (!pyopencv_to(typeItem.item, dst.type, typeItemInfo))
{
return false;
}
}
{
const String maxCountItemName = format("'%s' max count", info.name);
const ArgInfo maxCountItemInfo(maxCountItemName.c_str(), false);
SafeSeqItem maxCountItem(obj, 1);
if (!pyopencv_to(maxCountItem.item, dst.maxCount, maxCountItemInfo))
{
return false;
}
}
{
const String epsilonItemName = format("'%s' epsilon", info.name);
const ArgInfo epsilonItemInfo(epsilonItemName.c_str(), false);
SafeSeqItem epsilonItem(obj, 2);
if (!pyopencv_to(epsilonItem.item, dst.epsilon, epsilonItemInfo))
{
return false;
}
}
return true;
} }
template<> template<>
@ -1808,12 +1823,54 @@ PyObject* pyopencv_from(const TermCriteria& src)
} }
template<> template<>
bool pyopencv_to(PyObject *obj, RotatedRect& dst, const ArgInfo& info) bool pyopencv_to(PyObject* obj, RotatedRect& dst, const ArgInfo& info)
{ {
CV_UNUSED(info); if (!obj || obj == Py_None)
if(!obj) {
return true; return true;
return PyArg_ParseTuple(obj, "(ff)(ff)f", &dst.center.x, &dst.center.y, &dst.size.width, &dst.size.height, &dst.angle) > 0; }
if (!PySequence_Check(obj))
{
failmsg("Can't parse '%s' as RotatedRect."
"Input argument doesn't provide sequence protocol",
info.name);
return false;
}
const std::size_t sequenceSize = PySequence_Size(obj);
if (sequenceSize != 3)
{
failmsg("Can't parse '%s' as RotatedRect. Expected sequence length 3, got %lu",
info.name, sequenceSize);
return false;
}
{
const String centerItemName = format("'%s' center point", info.name);
const ArgInfo centerItemInfo(centerItemName.c_str(), false);
SafeSeqItem centerItem(obj, 0);
if (!pyopencv_to(centerItem.item, dst.center, centerItemInfo))
{
return false;
}
}
{
const String sizeItemName = format("'%s' size", info.name);
const ArgInfo sizeItemInfo(sizeItemName.c_str(), false);
SafeSeqItem sizeItem(obj, 1);
if (!pyopencv_to(sizeItem.item, dst.size, sizeItemInfo))
{
return false;
}
}
{
const String angleItemName = format("'%s' angle", info.name);
const ArgInfo angleItemInfo(angleItemName.c_str(), false);
SafeSeqItem angleItem(obj, 2);
if (!pyopencv_to(angleItem.item, dst.angle, angleItemInfo))
{
return false;
}
}
return true;
} }
template<> template<>

View File

@ -3,6 +3,7 @@ from __future__ import print_function
import ctypes import ctypes
from functools import partial from functools import partial
from collections import namedtuple
import numpy as np import numpy as np
import cv2 as cv import cv2 as cv
@ -379,6 +380,84 @@ class Arguments(NewOpenCVTests):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)): with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpString(not_convertible) _ = cv.utils.dumpString(not_convertible)
def test_parse_to_rect_convertible(self):
Rect = namedtuple('Rect', ('x', 'y', 'w', 'h'))
try_to_convert = partial(self._try_to_convert, cv.utils.dumpRect)
for convertible in ((1, 2, 4, 5), [5, 3, 10, 20], np.array([10, 20, 23, 10]),
Rect(10, 30, 40, 55), tuple(np.array([40, 20, 24, 20])),
list(np.array([20, 40, 30, 35]))):
expected = 'rect: (x={}, y={}, w={}, h={})'.format(*convertible)
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_rect_not_convertible(self):
for not_convertible in (np.empty(shape=(4, 1)), (), [], np.array([]), (12, ),
[3, 4, 5, 10, 123], {1: 2, 3:4, 5:10, 6:30},
'1234', np.array([1, 2, 3, 4], dtype=np.float32),
np.array([[1, 2], [3, 4], [5, 6], [6, 8]]), (1, 2, 5, 1.5)):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpRect(not_convertible)
def test_parse_to_rotated_rect_convertible(self):
RotatedRect = namedtuple('RotatedRect', ('center', 'size', 'angle'))
try_to_convert = partial(self._try_to_convert, cv.utils.dumpRotatedRect)
for convertible in (((2.5, 2.5), (10., 20.), 12.5), [[1.5, 10.5], (12.5, 51.5), 10],
RotatedRect((10, 40), np.array([10.5, 20.5]), 5),
np.array([[10, 6], [50, 50], 5.5], dtype=object)):
center, size, angle = convertible
expected = 'rotated_rect: (c_x={:.6f}, c_y={:.6f}, w={:.6f},' \
' h={:.6f}, a={:.6f})'.format(center[0], center[1],
size[0], size[1], angle)
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_rotated_rect_not_convertible(self):
for not_convertible in ([], (), np.array([]), (123, (45, 34), 1), {1: 2, 3: 4}, 123,
np.array([[123, 123, 14], [1, 3], 56], dtype=object), '123'):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpRotatedRect(not_convertible)
def test_parse_to_term_criteria_convertible(self):
TermCriteria = namedtuple('TermCriteria', ('type', 'max_count', 'epsilon'))
try_to_convert = partial(self._try_to_convert, cv.utils.dumpTermCriteria)
for convertible in ((1, 10, 1e-3), [2, 30, 1e-1], np.array([10, 20, 0.5], dtype=object),
TermCriteria(0, 5, 0.1)):
expected = 'term_criteria: (type={}, max_count={}, epsilon={:.6f}'.format(*convertible)
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_term_criteria_not_convertible(self):
for not_convertible in ([], (), np.array([]), [1, 4], (10,), (1.5, 34, 0.1),
{1: 5, 3: 5, 10: 10}, '145'):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpTermCriteria(not_convertible)
def test_parse_to_range_convertible_to_all(self):
try_to_convert = partial(self._try_to_convert, cv.utils.dumpRange)
for convertible in ((), [], np.array([])):
expected = 'range: all'
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_range_convertible(self):
Range = namedtuple('Range', ('start', 'end'))
try_to_convert = partial(self._try_to_convert, cv.utils.dumpRange)
for convertible in ((10, 20), [-1, 3], np.array([10, 24]), Range(-4, 6)):
expected = 'range: (s={}, e={})'.format(*convertible)
actual = try_to_convert(convertible)
self.assertEqual(expected, actual,
msg=get_conversion_error_msg(convertible, expected, actual))
def test_parse_to_range_not_convertible(self):
for not_convertible in ((1, ), [40, ], np.array([1, 4, 6]), {'a': 1, 'b': 40},
(1.5, 13.5), [3, 6.7], np.array([6.3, 2.1]), '14, 4'):
with self.assertRaises((TypeError), msg=get_no_exception_msg(not_convertible)):
_ = cv.utils.dumpRange(not_convertible)
class SamplesFindFile(NewOpenCVTests): class SamplesFindFile(NewOpenCVTests):