Merge 3c63f54300
into bdc0518584
commit
b1683dac1b
12 changed files with 547 additions and 14 deletions
@ -0,0 +1,45 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef OPENCV_FASTCV_CHANNEL_HPP |
||||||
|
#define OPENCV_FASTCV_CHANNEL_HPP |
||||||
|
|
||||||
|
#include <opencv2/core.hpp> |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace fastcv { |
||||||
|
|
||||||
|
//! @addtogroup fastcv
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates one multi-channel mat out of several single-channel CV_8U mats. |
||||||
|
* Optimized for Qualcomm's processors |
||||||
|
* @param mv input vector of matrices to be merged; all the matrices in mv must be of CV_8UC1 and have the same size |
||||||
|
* Note: numbers of mats can be 2,3 or 4. |
||||||
|
* @param dst output array of depth CV_8U and same size as mv[0]; The number of channels |
||||||
|
* will be the total number of matrices in the matrix array |
||||||
|
*/ |
||||||
|
CV_EXPORTS_W void merge(InputArrayOfArrays mv, OutputArray dst); |
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
//! @addtogroup fastcv
|
||||||
|
//! @{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Splits an CV_8U multi-channel mat into several CV_8UC1 mats |
||||||
|
* Optimized for Qualcomm's processors |
||||||
|
* @param src input 2,3 or 4 channel mat of depth CV_8U |
||||||
|
* @param mv output vector of size src.channels() of CV_8UC1 mats |
||||||
|
*/ |
||||||
|
CV_EXPORTS_W void split(InputArray src, OutputArrayOfArrays mv); |
||||||
|
|
||||||
|
//! @}
|
||||||
|
|
||||||
|
} // fastcv::
|
||||||
|
} // cv::
|
||||||
|
|
||||||
|
#endif // OPENCV_FASTCV_CHANNEL_HPP
|
@ -0,0 +1,143 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "precomp.hpp" |
||||||
|
|
||||||
|
namespace cv { |
||||||
|
namespace fastcv { |
||||||
|
|
||||||
|
void merge(InputArrayOfArrays _mv, OutputArray _dst) |
||||||
|
{ |
||||||
|
CV_Assert(!_mv.empty()); |
||||||
|
std::vector<cv::Mat> mv; |
||||||
|
_mv.getMatVector(mv); |
||||||
|
int count = mv.size(); |
||||||
|
|
||||||
|
CV_Assert(!mv.empty()); |
||||||
|
|
||||||
|
CV_Assert(count == 2 || count == 3 || count == 4); |
||||||
|
CV_Assert(!mv[0].empty()); |
||||||
|
CV_Assert(mv[0].dims <= 2); |
||||||
|
|
||||||
|
for(int i = 0; i < count; i++ ) |
||||||
|
{ |
||||||
|
CV_Assert(mv[i].size == mv[0].size && mv[i].step[0] == mv[0].step[0] && mv[i].type() == CV_8UC1); |
||||||
|
} |
||||||
|
|
||||||
|
_dst.create(mv[0].dims, mv[0].size, CV_MAKE_TYPE(CV_8U,count)); |
||||||
|
Mat dst = _dst.getMat(); |
||||||
|
|
||||||
|
INITIALIZATION_CHECK; |
||||||
|
|
||||||
|
int nStripes = cv::getNumThreads(); |
||||||
|
|
||||||
|
switch(count) |
||||||
|
{ |
||||||
|
case 2: |
||||||
|
cv::parallel_for_(cv::Range(0, mv[0].rows), [&](const cv::Range &range){ |
||||||
|
int height_ = range.end - range.start; |
||||||
|
const uchar* yS1 = mv[0].data + static_cast<size_t>(range.start) * mv[0].step[0]; |
||||||
|
const uchar* yS2 = mv[1].data + static_cast<size_t>(range.start) * mv[1].step[0]; |
||||||
|
uchar* yD = dst.data + static_cast<size_t>(range.start) * dst.step[0]; |
||||||
|
fcvChannelCombine2Planesu8(yS1, mv[0].cols, height_, mv[0].step[0], yS2, mv[1].step[0], yD, dst.step[0]); |
||||||
|
}, nStripes); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 3: |
||||||
|
cv::parallel_for_(cv::Range(0, mv[0].rows), [&](const cv::Range &range){ |
||||||
|
int height_ = range.end - range.start; |
||||||
|
const uchar* yS1 = mv[0].data + static_cast<size_t>(range.start) * mv[0].step[0]; |
||||||
|
const uchar* yS2 = mv[1].data + static_cast<size_t>(range.start) * mv[1].step[0]; |
||||||
|
const uchar* yS3 = mv[2].data + static_cast<size_t>(range.start) * mv[2].step[0]; |
||||||
|
uchar* yD = dst.data + static_cast<size_t>(range.start) * dst.step[0]; |
||||||
|
fcvChannelCombine3Planesu8(yS1, mv[0].cols, height_, mv[0].step[0], yS2, mv[1].step[0], yS3, mv[2].step[0], yD, dst.step[0]); |
||||||
|
}, nStripes); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
case 4: |
||||||
|
cv::parallel_for_(cv::Range(0, mv[0].rows), [&](const cv::Range &range){ |
||||||
|
int height_ = range.end - range.start; |
||||||
|
const uchar* yS1 = mv[0].data + static_cast<size_t>(range.start) * mv[0].step[0]; |
||||||
|
const uchar* yS2 = mv[1].data + static_cast<size_t>(range.start) * mv[1].step[0]; |
||||||
|
const uchar* yS3 = mv[2].data + static_cast<size_t>(range.start) * mv[2].step[0]; |
||||||
|
const uchar* yS4 = mv[3].data + static_cast<size_t>(range.start) * mv[3].step[0]; |
||||||
|
uchar* yD = dst.data + static_cast<size_t>(range.start) * dst.step[0]; |
||||||
|
fcvChannelCombine4Planesu8(yS1, mv[0].cols, height_, mv[0].step[0], yS2, mv[1].step[0], yS3, mv[2].step[0], yS4, mv[3].step[0], yD, dst.step[0]); |
||||||
|
}, nStripes); |
||||||
|
|
||||||
|
break; |
||||||
|
|
||||||
|
default: |
||||||
|
CV_Error(cv::Error::StsBadArg, cv::format("count is not supported")); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void split(InputArray _src, OutputArrayOfArrays _mv) |
||||||
|
{ |
||||||
|
CV_Assert(!_src.empty()); |
||||||
|
Mat src = _src.getMat(); |
||||||
|
|
||||||
|
int depth = src.depth(), cn = src.channels(); |
||||||
|
|
||||||
|
CV_Assert(depth == CV_8U && (cn == 2 || cn == 3 || cn == 4)); |
||||||
|
CV_Assert(src.dims <= 2); |
||||||
|
|
||||||
|
for( int k = 0; k < cn; k++ ) |
||||||
|
{ |
||||||
|
_mv.create(src.dims, src.size, depth, k); |
||||||
|
} |
||||||
|
|
||||||
|
std::vector<cv::Mat> mv(cn); |
||||||
|
_mv.getMatVector(mv); |
||||||
|
|
||||||
|
INITIALIZATION_CHECK; |
||||||
|
|
||||||
|
int nStripes = cv::getNumThreads(); |
||||||
|
|
||||||
|
if(src.rows * src.cols < 640 * 480) |
||||||
|
if(cn == 3 || cn == 4) |
||||||
|
nStripes = 1; |
||||||
|
|
||||||
|
if(cn == 2) |
||||||
|
{ |
||||||
|
cv::parallel_for_(cv::Range(0, src.rows), [&](const cv::Range &range){ |
||||||
|
int height_ = range.end - range.start; |
||||||
|
const uchar* yS = src.data + static_cast<size_t>(range.start) * src.step[0]; |
||||||
|
uchar* y1D = mv[0].data + static_cast<size_t>(range.start) * mv[0].step[0]; |
||||||
|
uchar* y2D = mv[1].data + static_cast<size_t>(range.start) * mv[1].step[0]; |
||||||
|
fcvDeinterleaveu8(yS, src.cols, height_, src.step[0], y1D, mv[0].step[0], y2D, mv[1].step[0]); |
||||||
|
}, nStripes); |
||||||
|
} |
||||||
|
else if(cn == 3) |
||||||
|
{ |
||||||
|
for(int i=0; i<cn; i++) |
||||||
|
{ |
||||||
|
cv::parallel_for_(cv::Range(0, src.rows), [&](const cv::Range &range){ |
||||||
|
int height_ = range.end - range.start; |
||||||
|
const uchar* yS = src.data + static_cast<size_t>(range.start) * src.step[0]; |
||||||
|
uchar* yD = mv[i].data + static_cast<size_t>(range.start) * mv[i].step[0]; |
||||||
|
fcvChannelExtractu8(yS, src.cols, height_, src.step[0], NULL, 0, NULL, 0, (fcvChannelType)i, (fcvImageFormat)FASTCV_RGB, yD, mv[i].step[0]); |
||||||
|
}, nStripes); |
||||||
|
} |
||||||
|
} |
||||||
|
else if(cn == 4) |
||||||
|
{ |
||||||
|
for(int i=0; i<cn; i++) |
||||||
|
{ |
||||||
|
cv::parallel_for_(cv::Range(0, src.rows), [&](const cv::Range &range){ |
||||||
|
int height_ = range.end - range.start; |
||||||
|
const uchar* yS = src.data + static_cast<size_t>(range.start) * src.step[0]; |
||||||
|
uchar* yD = mv[i].data + static_cast<size_t>(range.start) * mv[i].step[0]; |
||||||
|
fcvChannelExtractu8(yS, src.cols, height_, src.step[0], NULL, 0, NULL, 0, (fcvChannelType)i, (fcvImageFormat)FASTCV_RGBX, yD, mv[i].step[0]); |
||||||
|
}, nStripes); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} // fastcv::
|
||||||
|
} // cv::
|
@ -0,0 +1,76 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved. |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "test_precomp.hpp" |
||||||
|
|
||||||
|
namespace opencv_test { namespace { |
||||||
|
|
||||||
|
typedef std::tuple<Size, int> ChannelMergeTestParams; |
||||||
|
class ChannelMergeTest : public ::testing::TestWithParam<ChannelMergeTestParams> {}; |
||||||
|
|
||||||
|
typedef std::tuple<Size, int> ChannelSplitTestParams; |
||||||
|
class ChannelSplitTest : public ::testing::TestWithParam<ChannelSplitTestParams> {}; |
||||||
|
|
||||||
|
TEST_P(ChannelMergeTest, accuracy) |
||||||
|
{ |
||||||
|
int depth = CV_8UC1; |
||||||
|
Size sz = std::get<0>(GetParam()); |
||||||
|
int count = std::get<1>(GetParam()); |
||||||
|
std::vector<Mat> src_mats; |
||||||
|
|
||||||
|
RNG& rng = cv::theRNG(); |
||||||
|
|
||||||
|
for(int i = 0; i < count; i++) |
||||||
|
{ |
||||||
|
Mat tmp(sz, depth); |
||||||
|
src_mats.push_back(tmp); |
||||||
|
cvtest::randUni(rng, src_mats[i], Scalar::all(0), Scalar::all(127)); |
||||||
|
} |
||||||
|
|
||||||
|
Mat dst; |
||||||
|
cv::fastcv::merge(src_mats, dst); |
||||||
|
|
||||||
|
Mat ref; |
||||||
|
cv::merge(src_mats, ref); |
||||||
|
|
||||||
|
double normInf = cvtest::norm(ref, dst, cv::NORM_INF); |
||||||
|
double normL2 = cvtest::norm(ref, dst, cv::NORM_L2); |
||||||
|
|
||||||
|
EXPECT_EQ(normInf, 0); |
||||||
|
EXPECT_EQ(normL2, 0); |
||||||
|
} |
||||||
|
|
||||||
|
TEST_P(ChannelSplitTest, accuracy) |
||||||
|
{ |
||||||
|
Size sz = std::get<0>(GetParam()); |
||||||
|
int cn = std::get<1>(GetParam()); |
||||||
|
std::vector<Mat> dst_mats(cn), ref_mats(cn); |
||||||
|
|
||||||
|
RNG& rng = cv::theRNG(); |
||||||
|
Mat src(sz, CV_MAKE_TYPE(CV_8U,cn)); |
||||||
|
cvtest::randUni(rng, src, Scalar::all(0), Scalar::all(127)); |
||||||
|
|
||||||
|
cv::fastcv::split(src, dst_mats); |
||||||
|
|
||||||
|
cv::split(src, ref_mats); |
||||||
|
|
||||||
|
for(int i=0; i<cn; i++) |
||||||
|
{ |
||||||
|
double normInf = cvtest::norm(ref_mats[i], dst_mats[i], cv::NORM_INF); |
||||||
|
double normL2 = cvtest::norm(ref_mats[i], dst_mats[i], cv::NORM_L2); |
||||||
|
EXPECT_EQ(normInf, 0); |
||||||
|
EXPECT_EQ(normL2, 0); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(FastCV_Extension, ChannelMergeTest, |
||||||
|
::testing::Combine(::testing::Values(perf::szODD, perf::szVGA, perf::sz720p, perf::sz1080p), // sz
|
||||||
|
::testing::Values(2,3,4))); // count
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_CASE_P(FastCV_Extension, ChannelSplitTest, |
||||||
|
::testing::Combine(::testing::Values(perf::szODD, perf::szVGA, perf::sz720p, perf::sz1080p), // sz
|
||||||
|
::testing::Values(2,3,4))); // cn
|
||||||
|
|
||||||
|
}} // namespaces opencv_test, ::
|
Loading…
Reference in new issue