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