mirror of
https://github.com/opencv/opencv.git
synced 2026-07-30 07:43:03 +04:00
Merge pull request #27892 from abhishek-gola:center_crop_pad_layer
Added center crop pad layer to new DNN engine #27892 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
@@ -1305,6 +1305,12 @@ CV__DNN_INLINE_NS_BEGIN
|
||||
static Ptr<DetLayer> create(const LayerParams ¶ms);
|
||||
};
|
||||
|
||||
class CV_EXPORTS CenterCropPadLayer : public Layer
|
||||
{
|
||||
public:
|
||||
static Ptr<CenterCropPadLayer> create(const LayerParams& params);
|
||||
};
|
||||
|
||||
class CV_EXPORTS Resize2Layer : public Layer
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -116,6 +116,7 @@ void initializeLayerFactory()
|
||||
CV_DNN_REGISTER_LAYER_CLASS(IsNaN, IsNaNLayer);
|
||||
CV_DNN_REGISTER_LAYER_CLASS(IsInf, IsInfLayer);
|
||||
CV_DNN_REGISTER_LAYER_CLASS(Det, DetLayer);
|
||||
CV_DNN_REGISTER_LAYER_CLASS(CenterCropPad, CenterCropPadLayer);
|
||||
CV_DNN_REGISTER_LAYER_CLASS(BitShift, BitShiftLayer);
|
||||
CV_DNN_REGISTER_LAYER_CLASS(GridSample, GridSampleLayer);
|
||||
CV_DNN_REGISTER_LAYER_CLASS(Reduce2, Reduce2Layer);
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
// This file is part of OpenCV project.
|
||||
// It is subject to the license terms in the LICENSE file found in the top-level directory
|
||||
// of this distribution and at http://opencv.org/license.html.
|
||||
// Copyright (C) 2025, BigVision LLC, all rights reserved.
|
||||
// Third party copyrights are property of their respective owners.
|
||||
|
||||
#include "../precomp.hpp"
|
||||
#include "layers_common.hpp"
|
||||
#include "../net_impl.hpp"
|
||||
|
||||
namespace cv {
|
||||
namespace dnn {
|
||||
|
||||
// ONNX CenterCropPad operator
|
||||
// Spec: https://onnx.ai/onnx/operators/onnx__CenterCropPad.html
|
||||
// Supported opsets: 18
|
||||
|
||||
class CenterCropPadLayerImpl CV_FINAL : public CenterCropPadLayer
|
||||
{
|
||||
public:
|
||||
std::vector<int> axes_attr;
|
||||
|
||||
CenterCropPadLayerImpl(const LayerParams& params)
|
||||
{
|
||||
setParamsFrom(params);
|
||||
if (params.has("axes"))
|
||||
{
|
||||
DictValue dv = params.get("axes");
|
||||
axes_attr.resize(dv.size());
|
||||
for (int i = 0; i < dv.size(); ++i) axes_attr[i] = dv.get<int>(i);
|
||||
}
|
||||
}
|
||||
|
||||
bool supportBackend(int backendId) CV_OVERRIDE
|
||||
{
|
||||
return backendId == DNN_BACKEND_OPENCV;
|
||||
}
|
||||
|
||||
bool dynamicOutputShapes() const CV_OVERRIDE
|
||||
{
|
||||
Net::Impl* netimpl_ = getNetImpl(this);
|
||||
CV_Assert(netimpl_);
|
||||
const size_t ninputs = this->inputs.size();
|
||||
CV_Assert(ninputs >= 2);
|
||||
bool shapeDynamic = !netimpl_->isConstArg(this->inputs[1]);
|
||||
bool axesDynamic = (ninputs >= 3) && !netimpl_->isConstArg(this->inputs[2]);
|
||||
return shapeDynamic || axesDynamic;
|
||||
}
|
||||
|
||||
void buildTargetShape(const MatShape& inputShape, const Mat& shapeTensor, const Mat& axesTensor,
|
||||
std::vector<int>& targetShape, std::vector<int>& usedAxes) const
|
||||
{
|
||||
const int rank = inputShape.dims;
|
||||
targetShape.assign(inputShape.begin(), inputShape.end());
|
||||
usedAxes.clear();
|
||||
|
||||
CV_Assert(shapeTensor.dims == 1);
|
||||
CV_Assert(shapeTensor.type() == CV_32S || shapeTensor.type() == CV_64S);
|
||||
|
||||
std::vector<int> axes;
|
||||
if (!axesTensor.empty())
|
||||
{
|
||||
CV_Assert(axesTensor.dims == 1);
|
||||
int naxes = (int)axesTensor.total();
|
||||
axes.resize(naxes);
|
||||
if (axesTensor.type() == CV_32S)
|
||||
{
|
||||
const int32_t* a32 = (const int32_t*)axesTensor.data;
|
||||
for (int i = 0; i < naxes; ++i) axes[i] = normalize_axis((int)a32[i], rank);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int64_t* a64 = (const int64_t*)axesTensor.data;
|
||||
for (int i = 0; i < naxes; ++i) axes[i] = normalize_axis((int)a64[i], rank);
|
||||
}
|
||||
}
|
||||
else if (!axes_attr.empty())
|
||||
{
|
||||
axes.resize((int)axes_attr.size());
|
||||
for (size_t i = 0; i < axes_attr.size(); ++i) axes[i] = normalize_axis(axes_attr[i], rank);
|
||||
}
|
||||
else
|
||||
{
|
||||
axes.resize(rank);
|
||||
std::iota(axes.begin(), axes.end(), 0);
|
||||
}
|
||||
|
||||
CV_Assert((int)shapeTensor.total() == (int)axes.size());
|
||||
|
||||
if (shapeTensor.type() == CV_32S)
|
||||
{
|
||||
const int32_t* shp = (const int32_t*)shapeTensor.data;
|
||||
applyShapeFromPtr(shp, (int)axes.size(), axes, targetShape, usedAxes);
|
||||
}
|
||||
else if (shapeTensor.type() == CV_64S)
|
||||
{
|
||||
const int64_t* shp = (const int64_t*)shapeTensor.data;
|
||||
applyShapeFromPtr(shp, (int)axes.size(), axes, targetShape, usedAxes);
|
||||
}
|
||||
}
|
||||
|
||||
bool getMemoryShapes(const std::vector<MatShape>& inputs,
|
||||
const int /*requiredOutputs*/,
|
||||
std::vector<MatShape> &outputs,
|
||||
std::vector<MatShape> &internals) const CV_OVERRIDE
|
||||
{
|
||||
CV_Assert(inputs.size() >= 2);
|
||||
|
||||
const MatShape& inShape = inputs[0];
|
||||
Net::Impl* netimpl_ = getNetImpl(this);
|
||||
CV_Assert(netimpl_);
|
||||
|
||||
Mat shapeTensor = netimpl_->argTensor(this->inputs[1]);
|
||||
Mat axesTensor;
|
||||
if (this->inputs.size() >= 3)
|
||||
axesTensor = netimpl_->argTensor(this->inputs[2]);
|
||||
|
||||
std::vector<int> targetShape, usedAxes;
|
||||
buildTargetShape(inShape, shapeTensor, axesTensor, targetShape, usedAxes);
|
||||
|
||||
outputs.assign(1, MatShape(targetShape.begin(), targetShape.end()));
|
||||
internals.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
void getTypes(const std::vector<MatType>& inputs,
|
||||
const int requiredOutputs,
|
||||
const int requiredInternals,
|
||||
std::vector<MatType>& outputs,
|
||||
std::vector<MatType>& internals) const CV_OVERRIDE
|
||||
{
|
||||
CV_Assert(!inputs.empty());
|
||||
outputs.assign(requiredOutputs, inputs[0]);
|
||||
CV_Assert(requiredInternals == 0);
|
||||
internals.clear();
|
||||
}
|
||||
|
||||
void forward(InputArrayOfArrays inputs_arr,
|
||||
OutputArrayOfArrays outputs_arr,
|
||||
OutputArrayOfArrays) CV_OVERRIDE
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
CV_TRACE_ARG_VALUE(name, "name", name.c_str());
|
||||
|
||||
Size size = inputs_arr.size();
|
||||
int ninputs = size.area();
|
||||
CV_Assert(ninputs >= 2);
|
||||
|
||||
Mat X = inputs_arr.getMat(0);
|
||||
Mat shapeTensor = inputs_arr.getMat(1);
|
||||
Mat axesTensor;
|
||||
if (ninputs >= 3)
|
||||
axesTensor = inputs_arr.getMat(2);
|
||||
|
||||
const int rank = X.dims;
|
||||
std::vector<int> outShape(rank);
|
||||
std::vector<int> usedAxes;
|
||||
|
||||
MatShape inShape = X.shape();
|
||||
std::vector<int> tgt;
|
||||
buildTargetShape(inShape, shapeTensor, axesTensor, tgt, usedAxes);
|
||||
outShape.assign(tgt.begin(), tgt.end());
|
||||
|
||||
auto kind = outputs_arr.kind();
|
||||
if (kind == _InputArray::STD_VECTOR_MAT)
|
||||
{
|
||||
std::vector<Mat>& outs = outputs_arr.getMatVecRef();
|
||||
outs.resize(1);
|
||||
outs[0].fit(MatShape(outShape.begin(), outShape.end()), X.type());
|
||||
centerCropPad(X, outs[0], usedAxes);
|
||||
}
|
||||
else if (kind == _InputArray::STD_VECTOR_UMAT)
|
||||
{
|
||||
std::vector<UMat>& outs = outputs_arr.getUMatVecRef();
|
||||
outs.resize(1);
|
||||
outs[0].fit(MatShape(outShape.begin(), outShape.end()), X.type());
|
||||
Mat temp(MatShape(outShape.begin(), outShape.end()), X.type());
|
||||
centerCropPad(X, temp, usedAxes);
|
||||
temp.copyTo(outs[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
CV_Error(Error::StsNotImplemented, "");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
static void applyShapeFromPtr(const T* shp, int count, const std::vector<int>& axes,
|
||||
std::vector<int>& targetShape, std::vector<int>& usedAxes)
|
||||
{
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int a = axes[i];
|
||||
int desired = (int)shp[i];
|
||||
CV_Assert(desired >= 0);
|
||||
targetShape[a] = desired;
|
||||
usedAxes.push_back(a);
|
||||
}
|
||||
}
|
||||
|
||||
void centerCropPad(const Mat& src, Mat& dst, const std::vector<int>& axes) const
|
||||
{
|
||||
CV_Assert(src.dims == dst.dims);
|
||||
const int rank = src.dims;
|
||||
|
||||
std::vector<Range> srcRanges(rank), dstRanges(rank);
|
||||
for (int i = 0; i < rank; ++i)
|
||||
{
|
||||
int s = src.size[i];
|
||||
int d = dst.size[i];
|
||||
if (std::find(axes.begin(), axes.end(), i) == axes.end())
|
||||
{
|
||||
CV_Assert(s == d);
|
||||
srcRanges[i] = Range(0, s);
|
||||
dstRanges[i] = Range(0, d);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (d <= s)
|
||||
{
|
||||
int diff = s - d;
|
||||
int start = diff / 2;
|
||||
int end = start + d;
|
||||
srcRanges[i] = Range(start, end);
|
||||
dstRanges[i] = Range(0, d);
|
||||
}
|
||||
else
|
||||
{
|
||||
int diff = d - s;
|
||||
int start = diff / 2;
|
||||
int end = start + s;
|
||||
srcRanges[i] = Range(0, s);
|
||||
dstRanges[i] = Range(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
dst.setTo(Scalar(0));
|
||||
src(srcRanges.data()).copyTo(dst(dstRanges.data()));
|
||||
}
|
||||
};
|
||||
|
||||
Ptr<CenterCropPadLayer> CenterCropPadLayer::create(const LayerParams& params)
|
||||
{
|
||||
return Ptr<CenterCropPadLayer>(new CenterCropPadLayerImpl(params));
|
||||
}
|
||||
|
||||
} // namespace dnn
|
||||
} // namespace cv
|
||||
@@ -218,6 +218,7 @@ protected:
|
||||
void parseIsNaN (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
||||
void parseIsInf (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
||||
void parseDet (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
||||
void parseCenterCropPad (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
||||
void parseGridSample (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
||||
void parseNegativeLogLikelihoodLoss(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
||||
void parseSoftmaxCrossEntropyLoss (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
|
||||
@@ -1674,6 +1675,14 @@ void ONNXImporter2::parseResize2(LayerParams& layerParams, const opencv_onnx::No
|
||||
addLayer(layerParams, node_proto, ninputs);
|
||||
}
|
||||
|
||||
void ONNXImporter2::parseCenterCropPad(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
|
||||
{
|
||||
int ninputs = node_proto.input_size();
|
||||
CV_Assert(ninputs >= 2);
|
||||
layerParams.type = "CenterCropPad";
|
||||
addLayer(layerParams, node_proto);
|
||||
}
|
||||
|
||||
void ONNXImporter2::parseUnique(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
|
||||
{
|
||||
layerParams.type = "Unique";
|
||||
@@ -2620,6 +2629,7 @@ void ONNXImporter2::buildDispatchMap_ONNX_AI(int opset_version)
|
||||
dispatch["Trilu"] = &ONNXImporter2::parseTrilu;
|
||||
dispatch["IsNaN"] = &ONNXImporter2::parseIsNaN;
|
||||
dispatch["IsInf"] = &ONNXImporter2::parseIsInf;
|
||||
dispatch["CenterCropPad"] = &ONNXImporter2::parseCenterCropPad;
|
||||
dispatch["Det"] = &ONNXImporter2::parseDet;
|
||||
dispatch["GridSample"] = &ONNXImporter2::parseGridSample;
|
||||
dispatch["Upsample"] = &ONNXImporter2::parseUpsample;
|
||||
|
||||
@@ -394,6 +394,30 @@ CASE(test_celu)
|
||||
// no filter
|
||||
CASE(test_celu_expanded)
|
||||
// no filter
|
||||
CASE(test_center_crop_pad_crop)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_and_pad)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_and_pad_expanded)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_axes_chw)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_axes_chw_expanded)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_axes_hwc)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_axes_hwc_expanded)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_expanded)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_negative_axes_hwc)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_crop_negative_axes_hwc_expanded)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_pad)
|
||||
SKIP;
|
||||
CASE(test_center_crop_pad_pad_expanded)
|
||||
SKIP;
|
||||
CASE(test_clip)
|
||||
SKIP;
|
||||
CASE(test_clip_default_inbounds)
|
||||
|
||||
@@ -552,3 +552,15 @@
|
||||
"test_nllloss_NCd1d2d3_sum_weight_high_ii_expanded",
|
||||
"test_nllloss_NCd1d2d3d4d5_mean_weight",
|
||||
"test_nllloss_NCd1d2d3d4d5_none_no_weight",
|
||||
"test_center_crop_pad_crop",
|
||||
"test_center_crop_pad_crop_and_pad",
|
||||
"test_center_crop_pad_crop_and_pad_expanded",
|
||||
"test_center_crop_pad_crop_axes_chw",
|
||||
"test_center_crop_pad_crop_axes_chw_expanded",
|
||||
"test_center_crop_pad_crop_axes_hwc",
|
||||
"test_center_crop_pad_crop_axes_hwc_expanded",
|
||||
"test_center_crop_pad_crop_expanded",
|
||||
"test_center_crop_pad_crop_negative_axes_hwc",
|
||||
"test_center_crop_pad_crop_negative_axes_hwc_expanded",
|
||||
"test_center_crop_pad_pad",
|
||||
"test_center_crop_pad_pad_expanded",
|
||||
|
||||
@@ -286,18 +286,6 @@
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2FNUZ",
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2FNUZ_expanded",
|
||||
"test_castlike_no_saturate_FLOAT_to_FLOAT8E5M2_expanded",
|
||||
"test_center_crop_pad_crop",
|
||||
"test_center_crop_pad_crop_and_pad",
|
||||
"test_center_crop_pad_crop_and_pad_expanded",
|
||||
"test_center_crop_pad_crop_axes_chw",
|
||||
"test_center_crop_pad_crop_axes_chw_expanded",
|
||||
"test_center_crop_pad_crop_axes_hwc",
|
||||
"test_center_crop_pad_crop_axes_hwc_expanded",
|
||||
"test_center_crop_pad_crop_expanded",
|
||||
"test_center_crop_pad_crop_negative_axes_hwc",
|
||||
"test_center_crop_pad_crop_negative_axes_hwc_expanded",
|
||||
"test_center_crop_pad_pad",
|
||||
"test_center_crop_pad_pad_expanded",
|
||||
"test_clip_example_expanded", //wrong output
|
||||
"test_clip_expanded",
|
||||
"test_clip_min_greater_than_max",
|
||||
|
||||
Reference in New Issue
Block a user