Reputation: 7266
I'm trying to understand cv2.bitwise_and
function of opencv-python. So I tried it as:
import cv2
cv2.bitwise_and(1,1)
above code returns
array([[1.],
[0.],
[0.],
[0.]])
I don't understand why it returns this.
Documentation says :
dst(I) = src1(I) ^ src2(I) if mask(I) != 0
according to this output should be single value 1. where am I going wrong?
Upvotes: 4
Views: 2070
Reputation: 19061
The documentation is a bit vague in this aspect, and it will take some digging through both source, as well as docs to properly explain what's happening.
First of all -- scalars. In context of data types, we have a cv::Scalar
, which is actually a specialization of template cv::Scalar_
. It represents a 4-element vector, and derives from cv::Vec
-- a template representing a fixed size vector, which is again a special case of cv::Matx
, a class representing small fixed size matrices.
That's scalar the data type, however in the context of the bitwise_and
(and related functions), the concept what is and isn't a scalar is much looser -- the function in fact is not aware that gave it an instance of cv::Scalar
.
If you look at the signature of the function, you'll notice that the inputs are InputArray
s. So the inputs are always arrays, but it's possible that some of their properties differ (kind, element type, size, dimensionality, etc.).
The specific check in the code verifies that size, type and kind match. If that's the case (and in your scenario it is), the operation dst(I) = src1(I) ^ src2(I) if mask(I) != 0
runs.
Otherwise it will check whether one of the input arrays represents a scalar. It uses function checkScalar
to do that, and the return statement says most of it:
return sz == Size(1, 1)
|| sz == Size(1, cn) || sz == Size(cn, 1)
|| (sz == Size(1, 4) && sc.type() == CV_64F && cn <= 4);
cn
or cn
x 1 (where cn
is the number of channels if the other input array).The last case matches both the default cv::Scalar
(which, as we have seen earlier, is a cv::Matx<double,4,1>
), as well as cv::Mat(4,1,CF_64F)
.
As an intermission, let's test some of what we learned above.
Code:
cv::Scalar foo(1), bar(1);
cv::Mat result;
cv::bitwise_and(foo, bar, result);
std::cout << result << '\n';
std::cout << "size : " << result.size() << '\n';
std::cout << "type==CV_64FC1 : " << (result.type() == CV_64FC1 ? "yes" : "no") << '\n';
Output:
[1;
0;
0;
0]
size : [1 x 4]
type==CV_64FC1 : yes
Having covered the underlying C++ API, let's look at the Python bindings. The generator that creates the wrappers for Python API is fairly complex, so let's skip that, and instead inspect a relevant snippet of what it generates for bitwise_and
:
using namespace cv;
{
PyObject* pyobj_src1 = NULL;
Mat src1;
PyObject* pyobj_src2 = NULL;
Mat src2;
PyObject* pyobj_dst = NULL;
Mat dst;
PyObject* pyobj_mask = NULL;
Mat mask;
const char* keywords[] = { "src1", "src2", "dst", "mask", NULL };
if( PyArg_ParseTupleAndKeywords(args, kw, "OO|OO:bitwise_and", (char**)keywords, &pyobj_src1, &pyobj_src2, &pyobj_dst, &pyobj_mask) &&
pyopencv_to(pyobj_src1, src1, ArgInfo("src1", 0)) &&
pyopencv_to(pyobj_src2, src2, ArgInfo("src2", 0)) &&
pyopencv_to(pyobj_dst, dst, ArgInfo("dst", 1)) &&
pyopencv_to(pyobj_mask, mask, ArgInfo("mask", 0)) )
{
ERRWRAP2(cv::bitwise_and(src1, src2, dst, mask));
return pyopencv_from(dst);
}
}
PyErr_Clear();
We can see that parameters that correspond to InputArray
or OutputArray
are loaded into a cv::Mat
instance. Let's look at the part of pyopencv_to
that corresponds to your scenario:
if( PyInt_Check(o) )
{
double v[] = {static_cast<double>(PyInt_AsLong((PyObject*)o)), 0., 0., 0.};
m = Mat(4, 1, CV_64F, v).clone();
return true;
}
A cv::Mat(4, 1, CV_64F)
(recall from earlier that this fits the test for scalar) containing the input integer cast to double, with the remaining 3 position padded with zeros.
Since no destination is provided, a Mat
will be allocated automatically, of the same size and type as inputs. On return to Python, the Mat
will become a numpy array.
Upvotes: 0
Reputation: 794
The documentation says clearly that the function performs the operations dst(I) = src1(I) ^ src2(I) if mask(I) != 0
if the inputs are two arrays of the same size.
So try:
import numpy as np # Opecv works with numpy arrays
import cv2
a = np.uint8([1])
b = np.uint8([1])
cv2.bitwise_and(a, b)
That code returns:
array([[1]], dtype=uint8)
That is a one dimensional array containing the number 1.
The documentation also mentions that the operation can be done with an array and a scalar, but not with two scalars, so the input cv2.bitwise_and(1,1)
is not correct.
Upvotes: 2