ocl: compare with scalar
authorAlexander Alekhin <alexander.alekhin@itseez.com>
Thu, 13 Mar 2014 16:36:23 +0000 (20:36 +0400)
committerAlexander Alekhin <alexander.alekhin@itseez.com>
Fri, 14 Mar 2014 15:51:19 +0000 (19:51 +0400)
modules/core/include/opencv2/core/mat.hpp
modules/core/include/opencv2/core/mat.inl.hpp
modules/core/perf/opencl/perf_arithm.cpp
modules/core/src/arithm.cpp
modules/core/src/opencl/arithm.cl
modules/core/test/ocl/test_arithm.cpp

index eb206fc..33167fa 100644 (file)
@@ -135,7 +135,7 @@ public:
     bool isUMat() const;
     bool isMatVector() const;
     bool isUMatVector() const;
-    bool isMatx();
+    bool isMatx() const;
 
     virtual ~_InputArray();
 
index d289e3a..d463eec 100644 (file)
@@ -112,7 +112,7 @@ inline bool _InputArray::isMat() const { return kind() == _InputArray::MAT; }
 inline bool _InputArray::isUMat() const  { return kind() == _InputArray::UMAT; }
 inline bool _InputArray::isMatVector() const { return kind() == _InputArray::STD_VECTOR_MAT; }
 inline bool _InputArray::isUMatVector() const  { return kind() == _InputArray::STD_VECTOR_UMAT; }
-inline bool _InputArray::isMatx()  { return kind() == _InputArray::MATX; }
+inline bool _InputArray::isMatx() const { return kind() == _InputArray::MATX; }
 
 ////////////////////////////////////////////////////////////////////////////////////////
 
index f4680aa..a2c015b 100644 (file)
@@ -540,7 +540,7 @@ typedef TestBaseWithParam<CompareParams> CompareFixture;
 
 OCL_PERF_TEST_P(CompareFixture, Compare,
             ::testing::Combine(OCL_TEST_SIZES,
-                               OCL_TEST_TYPES, CmpCode::all()))
+                               OCL_TEST_TYPES_134, CmpCode::all()))
 {
     const CompareParams params = GetParam();
     const Size srcSize = get<0>(params);
@@ -549,7 +549,7 @@ OCL_PERF_TEST_P(CompareFixture, Compare,
 
     checkDeviceMaxMemoryAllocSize(srcSize, type);
 
-    UMat src1(srcSize, type), src2(srcSize, type), dst(srcSize, CV_8UC1);
+    UMat src1(srcSize, type), src2(srcSize, type), dst(srcSize, CV_8UC(CV_MAT_CN(type)));
     declare.in(src1, src2, WARMUP_RNG).out(dst);
 
     OCL_TEST_CYCLE() cv::compare(src1, src2, dst, cmpCode);
@@ -557,6 +557,25 @@ OCL_PERF_TEST_P(CompareFixture, Compare,
     SANITY_CHECK(dst);
 }
 
+OCL_PERF_TEST_P(CompareFixture, CompareScalar,
+            ::testing::Combine(OCL_TEST_SIZES,
+                               OCL_TEST_TYPES_134, CmpCode::all()))
+{
+    const CompareParams params = GetParam();
+    const Size srcSize = get<0>(params);
+    const int type = get<1>(params);
+    const int cmpCode = get<2>(params);
+
+    checkDeviceMaxMemoryAllocSize(srcSize, type);
+
+    UMat src1(srcSize, type), dst(srcSize, CV_8UC(CV_MAT_CN(type)));
+    declare.in(src1, WARMUP_RNG).out(dst);
+
+    OCL_TEST_CYCLE() cv::compare(src1, 32, dst, cmpCode);
+
+    SANITY_CHECK(dst);
+}
+
 ///////////// pow ////////////////////////
 
 typedef Size_MatType PowFixture;
index 436239c..5ef6c0e 100644 (file)
@@ -2617,44 +2617,83 @@ static double getMaxVal(int depth)
 
 #ifdef HAVE_OPENCL
 
-static bool ocl_compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op)
+static bool ocl_compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op, bool haveScalar)
 {
-    if ( !((_src1.isMat() || _src1.isUMat()) && (_src2.isMat() || _src2.isUMat())) )
-        return false;
-
     bool doubleSupport = ocl::Device::getDefault().doubleFPConfig() > 0;
-    int type = _src1.type(), depth = CV_MAT_DEPTH(type), cn = CV_MAT_CN(type), type2 = _src2.type();
-    if ( (!doubleSupport && (depth == CV_64F || _src2.depth() == CV_64F)) ||
-        !_src1.sameSize(_src2) || type != type2)
+    int type1 = _src1.type(), depth1 = CV_MAT_DEPTH(type1), cn = CV_MAT_CN(type1);
+    int type2 = _src2.type();
+
+    if (!haveScalar)
+    {
+        if ( (!doubleSupport && (depth1 == CV_64F || _src2.depth() == CV_64F)) ||
+            !_src1.sameSize(_src2) || type1 != type2)
+            return false;
+    }
+    else
+    {
+        if (cn > 1 || depth1 <= CV_32S) // FIXIT: if (cn > 4): Need to clear CPU-based compare behavior
+            return false;
+    }
+
+    if (!doubleSupport && depth1 == CV_64F)
         return false;
 
-    int kercn = ocl::predictOptimalVectorWidth(_src1, _src2, _dst);
+    int kercn = haveScalar ? cn : ocl::predictOptimalVectorWidth(_src1, _src2, _dst);
+    int scalarcn = kercn == 3 ? 4 : kercn;
+
     const char * const operationMap[] = { "==", ">", ">=", "<", "<=", "!=" };
     char cvt[40];
 
-    ocl::Kernel k("KF", ocl::core::arithm_oclsrc,
-                  format("-D BINARY_OP -D srcT1=%s -D dstT=%s -D workT=srcT1 -D cn=%d"
-                         " -D convertToDT=%s -D OP_CMP -D CMP_OPERATOR=%s%s -D srcT1_C1=%s"
-                         " -D srcT2_C1=%s -D dstT_C1=%s",
-                         ocl::typeToStr(CV_MAKE_TYPE(depth, kercn)),
-                         ocl::typeToStr(CV_8UC(kercn)), kercn,
-                         ocl::convertTypeStr(depth, CV_8U, kercn, cvt),
-                         operationMap[op], doubleSupport ? " -D DOUBLE_SUPPORT" : "",
-                         ocl::typeToStr(depth), ocl::typeToStr(depth), ocl::typeToStr(CV_8U)));
+    String buildOptions = format(
+            "-D %s -D srcT1=%s -D dstT=%s -D workT=srcT1 -D cn=%d"
+            " -D convertToDT=%s -D OP_CMP -D CMP_OPERATOR=%s%s -D srcT1_C1=%s"
+            " -D srcT2_C1=%s -D dstT_C1=%s -D workST=%s%s",
+            (haveScalar ? "UNARY_OP" : "BINARY_OP"),
+            ocl::typeToStr(CV_MAKE_TYPE(depth1, kercn)),
+            ocl::typeToStr(CV_8UC(kercn)), kercn,
+            ocl::convertTypeStr(depth1, CV_8U, kercn, cvt),
+            operationMap[op], doubleSupport ? " -D DOUBLE_SUPPORT" : "",
+            ocl::typeToStr(depth1), ocl::typeToStr(depth1), ocl::typeToStr(CV_8U),
+            ocl::typeToStr(CV_MAKE_TYPE(depth1, scalarcn)),
+            doubleSupport ? " -D DOUBLE_SUPPORT" : ""
+            );
+
+    ocl::Kernel k("KF", ocl::core::arithm_oclsrc, buildOptions);
     if (k.empty())
         return false;
 
-    CV_Assert(type == type2);
-    UMat src1 = _src1.getUMat(), src2 = _src2.getUMat();
+    UMat src1 = _src1.getUMat();
     Size size = src1.size();
-    CV_Assert(size == src2.size());
-
     _dst.create(size, CV_8UC(cn));
     UMat dst = _dst.getUMat();
 
-    k.args(ocl::KernelArg::ReadOnlyNoSize(src1),
-           ocl::KernelArg::ReadOnlyNoSize(src2),
-           ocl::KernelArg::WriteOnly(dst, cn, kercn));
+    if (haveScalar)
+    {
+        size_t esz = CV_ELEM_SIZE1(type1)*scalarcn;
+        double buf[4]={0,0,0,0};
+        Mat src2sc = _src2.getMat();
+
+        if (!src2sc.empty())
+            convertAndUnrollScalar(src2sc, type1, (uchar*)buf, 1);
+
+        ocl::KernelArg scalararg = ocl::KernelArg(0, 0, 0, 0, buf, esz);
+
+        k.args(ocl::KernelArg::ReadOnlyNoSize(src1, cn, kercn),
+               ocl::KernelArg::WriteOnly(dst, cn, kercn),
+               scalararg);
+    }
+    else
+    {
+        CV_DbgAssert(type1 == type2);
+        UMat src2 = _src2.getUMat();
+        CV_DbgAssert(size == src2.size());
+
+        _dst.create(size, CV_8UC(cn));
+
+        k.args(ocl::KernelArg::ReadOnlyNoSize(src1),
+               ocl::KernelArg::ReadOnlyNoSize(src2),
+               ocl::KernelArg::WriteOnly(dst, cn, kercn));
+    }
 
     size_t globalsize[2] = { dst.cols * cn / kercn, dst.rows };
     return k.run(2, globalsize, NULL, false);
@@ -2669,8 +2708,29 @@ void cv::compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op)
     CV_Assert( op == CMP_LT || op == CMP_LE || op == CMP_EQ ||
                op == CMP_NE || op == CMP_GE || op == CMP_GT );
 
+    bool haveScalar = false;
+
+    if ((_src1.isMatx() + _src2.isMatx()) == 1
+            || !_src1.sameSize(_src2)
+            || _src1.type() != _src2.type())
+    {
+        if (checkScalar(_src1, _src2.type(), _src1.kind(), _src2.kind()))
+        {
+            op = op == CMP_LT ? CMP_GT : op == CMP_LE ? CMP_GE :
+                op == CMP_GE ? CMP_LE : op == CMP_GT ? CMP_LT : op;
+            // src1 is a scalar; swap it with src2
+            compare(_src2, _src1, _dst, op);
+            return;
+        }
+        else if( !checkScalar(_src2, _src1.type(), _src2.kind(), _src1.kind()) )
+            CV_Error( CV_StsUnmatchedSizes,
+                     "The operation is neither 'array op array' (where arrays have the same size and the same type), "
+                     "nor 'array op scalar', nor 'scalar op array'" );
+        haveScalar = true;
+    }
+
     CV_OCL_RUN(_src1.dims() <= 2 && _src2.dims() <= 2 && _dst.isUMat(),
-               ocl_compare(_src1, _src2, _dst, op))
+               ocl_compare(_src1, _src2, _dst, op, haveScalar))
 
     int kind1 = _src1.kind(), kind2 = _src2.kind();
     Mat src1 = _src1.getMat(), src2 = _src2.getMat();
@@ -2685,26 +2745,6 @@ void cv::compare(InputArray _src1, InputArray _src2, OutputArray _dst, int op)
         return;
     }
 
-    bool haveScalar = false;
-
-    if( (kind1 == _InputArray::MATX) + (kind2 == _InputArray::MATX) == 1 ||
-        src1.size != src2.size || src1.type() != src2.type() )
-    {
-        if( checkScalar(src1, src2.type(), kind1, kind2) )
-        {
-            // src1 is a scalar; swap it with src2
-            swap(src1, src2);
-            op = op == CMP_LT ? CMP_GT : op == CMP_LE ? CMP_GE :
-                op == CMP_GE ? CMP_LE : op == CMP_GT ? CMP_LT : op;
-        }
-        else if( !checkScalar(src2, src1.type(), kind2, kind1) )
-            CV_Error( CV_StsUnmatchedSizes,
-                     "The operation is neither 'array op array' (where arrays have the same size and the same type), "
-                     "nor 'array op scalar', nor 'scalar op array'" );
-        haveScalar = true;
-    }
-
-
     int cn = src1.channels(), depth1 = src1.depth(), depth2 = src2.depth();
 
     _dst.create(src1.dims, src1.size, CV_8UC(cn));
index 865b433..d1ef96d 100644 (file)
 #elif defined OP_CMP
 #define srcT2 srcT1
 #define convertToWT1
-#define PROCESS_ELEM storedst(convertToDT(srcelem1 CMP_OPERATOR srcelem2 ? (dstT)(255) : (dstT)(0)))
+#define PROCESS_ELEM storedst((dstT)(srcelem1 CMP_OPERATOR srcelem2 ? (dstT)(255) : (dstT)(0)))
 
 #elif defined OP_CONVERT_SCALE_ABS
 #undef EXTRA_PARAMS
index e6bcf4e..1bdef21 100644 (file)
@@ -119,6 +119,7 @@ PARAM_TEST_CASE(ArithmTestBase, MatDepth, Channels, bool)
     int cn;
     bool use_roi;
     cv::Scalar val;
+    cv::Scalar val_in_range;
 
     TEST_DECLARE_INPUT_PARAMETER(src1)
     TEST_DECLARE_INPUT_PARAMETER(src2)
@@ -137,12 +138,15 @@ PARAM_TEST_CASE(ArithmTestBase, MatDepth, Channels, bool)
     {
         const int type = CV_MAKE_TYPE(depth, cn);
 
+        double minV = getMinVal(type);
+        double maxV = getMaxVal(type);
+
         Size roiSize = randomSize(1, MAX_VALUE);
         Border src1Border = randomBorder(0, use_roi ? MAX_VALUE : 0);
-        randomSubMat(src1, src1_roi, roiSize, src1Border, type, 2, 11);
+        randomSubMat(src1, src1_roi, roiSize, src1Border, type, minV, maxV);
 
         Border src2Border = randomBorder(0, use_roi ? MAX_VALUE : 0);
-        randomSubMat(src2, src2_roi, roiSize, src2Border, type, -1540, 1740);
+        randomSubMat(src2, src2_roi, roiSize, src2Border, type, std::max(-1540., minV), std::min(1740., maxV));
 
         Border dst1Border = randomBorder(0, use_roi ? MAX_VALUE : 0);
         randomSubMat(dst1, dst1_roi, roiSize, dst1Border, type, 5, 16);
@@ -157,6 +161,9 @@ PARAM_TEST_CASE(ArithmTestBase, MatDepth, Channels, bool)
         val = cv::Scalar(rng.uniform(-100.0, 100.0), rng.uniform(-100.0, 100.0),
                          rng.uniform(-100.0, 100.0), rng.uniform(-100.0, 100.0));
 
+        val_in_range = cv::Scalar(rng.uniform(minV, maxV), rng.uniform(minV, maxV),
+                                  rng.uniform(minV, maxV), rng.uniform(minV, maxV));
+
         UMAT_UPLOAD_INPUT_PARAMETER(src1)
         UMAT_UPLOAD_INPUT_PARAMETER(src2)
         UMAT_UPLOAD_INPUT_PARAMETER(mask)
@@ -750,12 +757,15 @@ OCL_TEST_P(Bitwise_not, Mat)
 
 typedef ArithmTestBase Compare;
 
+static const int cmp_codes[] = { CMP_EQ, CMP_GT, CMP_GE, CMP_LT, CMP_LE, CMP_NE };
+static const char* cmp_strs[] = { "CMP_EQ", "CMP_GT", "CMP_GE", "CMP_LT", "CMP_LE", "CMP_NE" };
+static const int cmp_num = sizeof(cmp_codes) / sizeof(int);
+
 OCL_TEST_P(Compare, Mat)
 {
-    int cmp_codes[] = { CMP_EQ, CMP_GT, CMP_GE, CMP_LT, CMP_LE, CMP_NE };
-    int cmp_num = sizeof(cmp_codes) / sizeof(int);
-
     for (int i = 0; i < cmp_num; ++i)
+    {
+        SCOPED_TRACE(cmp_strs[i]);
         for (int j = 0; j < test_loop_times; j++)
         {
             generateTestData();
@@ -765,6 +775,41 @@ OCL_TEST_P(Compare, Mat)
 
             Near(0);
         }
+    }
+}
+
+OCL_TEST_P(Compare, Scalar)
+{
+    for (int i = 0; i < cmp_num; ++i)
+    {
+        SCOPED_TRACE(cmp_strs[i]);
+        for (int j = 0; j < test_loop_times; j++)
+        {
+            generateTestData();
+
+            OCL_OFF(cv::compare(src1_roi, val_in_range, dst1_roi, cmp_codes[i]));
+            OCL_ON(cv::compare(usrc1_roi, val_in_range, udst1_roi, cmp_codes[i]));
+
+            Near(0);
+        }
+    }
+}
+
+OCL_TEST_P(Compare, Scalar2)
+{
+    for (int i = 0; i < cmp_num; ++i)
+    {
+        SCOPED_TRACE(cmp_strs[i]);
+        for (int j = 0; j < test_loop_times; j++)
+        {
+            generateTestData();
+
+            OCL_OFF(cv::compare(val_in_range, src1_roi, dst1_roi, cmp_codes[i]));
+            OCL_ON(cv::compare(val_in_range, usrc1_roi, udst1_roi, cmp_codes[i]));
+
+            Near(0);
+        }
+    }
 }
 
 //////////////////////////////// Pow /////////////////////////////////////////////////