1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 07:43:03 +04:00

Merge pull request #27586 from abhishek-gola:resize_layer_add

Added fully functional resize layer to new DNN engine #27586

### 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:
Abhishek Gola
2025-09-18 11:47:04 +05:30
committed by GitHub
parent 5185dfbaee
commit 68a5aea843
7 changed files with 1188 additions and 56 deletions
@@ -1281,6 +1281,12 @@ CV__DNN_INLINE_NS_BEGIN
static Ptr<DetLayer> create(const LayerParams &params);
};
class CV_EXPORTS Resize2Layer : public Layer
{
public:
static Ptr<Resize2Layer> create(const LayerParams& params);
};
/**
* @brief Bilinear resize layer from https://github.com/cdmh/deeplab-public-ver2
*
+1
View File
@@ -101,6 +101,7 @@ void initializeLayerFactory()
CV_DNN_REGISTER_LAYER_CLASS(Reshape2, Reshape2Layer);
CV_DNN_REGISTER_LAYER_CLASS(Resize, ResizeLayer);
CV_DNN_REGISTER_LAYER_CLASS(Size, SizeLayer);
CV_DNN_REGISTER_LAYER_CLASS(Resize2, Resize2Layer);
CV_DNN_REGISTER_LAYER_CLASS(Shape, ShapeLayer);
CV_DNN_REGISTER_LAYER_CLASS(Slice, SliceLayer);
CV_DNN_REGISTER_LAYER_CLASS(Slice2, Slice2Layer);
File diff suppressed because it is too large Load Diff
+51 -14
View File
@@ -213,11 +213,12 @@ protected:
void parseTrilu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
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 parseDet (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseGridSample (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseResize (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseSize (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseUnique (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseResize2 (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseReshape (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseScatter (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseShape (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -1538,13 +1539,12 @@ void ONNXImporter2::parseIf(LayerParams& layerParams,
}
// https://github.com/onnx/onnx/blob/master/docs/Operators.md#Resize
void ONNXImporter2::parseResize(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
void ONNXImporter2::parseResize2(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
int ninputs = node_proto.input_size();
layerParams.type = "Resize";
layerParams.type = "Resize2";
String interp_mode = layerParams.get<String>("coordinate_transformation_mode", "half_pixel");
CV_Assert(interp_mode != "tf_crop_and_resize");
bool halfPixel = interp_mode == "tf_half_pixel_for_nn" || interp_mode == "half_pixel" || interp_mode == "pytorch_half_pixel";
layerParams.set("align_corners", interp_mode == "align_corners");
@@ -1566,16 +1566,39 @@ void ONNXImporter2::parseResize(LayerParams& layerParams, const opencv_onnx::Nod
if(scalesArg.idx > 0 && netimpl->isConstArg(scalesArg))
scales = netimpl->argTensor(scalesArg);
if (ninputs >= 3 && interp_mode == "tf_crop_and_resize") {
int roiInputId = 1;
Arg roiArg = node_inputs[roiInputId];
if (!netimpl->isConstArg(roiArg)) {
CV_Error(Error::StsNotImplemented, "ONNX/Resize: only empty ROI is supported");
}
Mat roi = netimpl->argTensor(roiArg);
if (!roi.empty()) {
CV_Error(Error::StsNotImplemented, "ONNX/Resize: only empty ROI is supported");
if (interp_mode == "tf_crop_and_resize")
{
CV_Assert(ninputs >= 3);
Arg roiArg = node_inputs[1];
bool hasSizes = (ninputs >= 4);
Arg sizesArg = hasSizes ? node_inputs[3] : Arg();
bool staticRoi = netimpl->isConstArg(roiArg);
bool staticSizes = hasSizes && netimpl->isConstArg(sizesArg);
if (staticRoi && (!hasSizes || staticSizes))
{
Mat roiMat = netimpl->argTensor(roiArg), roiF;
CV_CheckEQ(roiMat.total(), (size_t)4,
"ONNX/Resize: ROI must have 4 values [y1,x1,y2,x2]");
roiMat.convertTo(roiF, CV_32F);
layerParams.set("y1", roiF.at<float>(0));
layerParams.set("x1", roiF.at<float>(1));
layerParams.set("y2", roiF.at<float>(2));
layerParams.set("x2", roiF.at<float>(3));
if (hasSizes && staticSizes)
{
Mat szMat = netimpl->argTensor(sizesArg), sz;
CV_CheckEQ(szMat.total(), (size_t)4,
"ONNX/Resize: sizes must have 4 values [N,C,H,W]");
szMat.convertTo(sz, CV_32S);
layerParams.set("height", sz.at<int>(2));
layerParams.set("width", sz.at<int>(3));
}
layerParams.set("dynamic_roi", false);
}
else layerParams.set("dynamic_roi", true);
}
if (scales.total() == 4)
@@ -1599,6 +1622,20 @@ void ONNXImporter2::parseResize(LayerParams& layerParams, const opencv_onnx::Nod
ninputs = 1;
}
}
for (int i_attr = 0; i_attr < node_proto.attribute_size(); ++i_attr)
{
const auto& a = node_proto.attribute(i_attr);
if (a.name() == "nearest_mode" && a.has_s())
layerParams.set("nearest_mode", String(a.s()));
else if (a.name() == "exclude_outside" && a.has_i())
layerParams.set("exclude_outside", static_cast<int>(a.i()) != 0);
else if (a.name() == "cubic_coeff_a" && a.has_f())
layerParams.set("cubic_coeff_a", static_cast<float>(a.f()));
else if (a.name() == "extrapolation_value" && a.has_f())
layerParams.set("extrapolation_value", static_cast<float>(a.f()));
}
replaceLayerParam(layerParams, "mode", "interpolation");
addLayer(layerParams, node_proto, ninputs);
}
@@ -2501,7 +2538,7 @@ void ONNXImporter2::buildDispatchMap_ONNX_AI(int opset_version)
dispatch["GatherElements"] = &ONNXImporter2::parseGatherElements;
dispatch["Concat"] = &ONNXImporter2::parseConcat;
dispatch["If"] = &ONNXImporter2::parseIf;
dispatch["Resize"] = &ONNXImporter2::parseResize;
dispatch["Resize"] = &ONNXImporter2::parseResize2;
dispatch["Size"] = &ONNXImporter2::parseSize;
dispatch["Unique"] = &ONNXImporter2::parseUnique;
dispatch["Trilu"] = &ONNXImporter2::parseTrilu;
@@ -1584,51 +1584,51 @@ CASE(test_reshape_zero_and_negative_dim)
CASE(test_reshape_zero_dim)
SKIP;
CASE(test_resize_downsample_scales_cubic)
// no filter
SKIP;
CASE(test_resize_downsample_scales_cubic_A_n0p5_exclude_outside)
// no filter
SKIP;
CASE(test_resize_downsample_scales_cubic_align_corners)
// no filter
CASE(test_resize_downsample_scales_linear)
// no filter
SKIP;
CASE(test_resize_downsample_scales_linear_align_corners)
// no filter
CASE(test_resize_downsample_scales_nearest)
// no filter
SKIP;
CASE(test_resize_downsample_sizes_cubic)
// no filter
SKIP;
CASE(test_resize_downsample_sizes_linear_pytorch_half_pixel)
// no filter
SKIP;
CASE(test_resize_downsample_sizes_nearest)
// no filter
SKIP;
CASE(test_resize_downsample_sizes_nearest_tf_half_pixel_for_nn)
// no filter
SKIP;
CASE(test_resize_tf_crop_and_resize)
// no filter
SKIP;
CASE(test_resize_upsample_scales_cubic)
// no filter
SKIP;
CASE(test_resize_upsample_scales_cubic_A_n0p5_exclude_outside)
// no filter
SKIP;
CASE(test_resize_upsample_scales_cubic_align_corners)
// no filter
SKIP;
CASE(test_resize_upsample_scales_cubic_asymmetric)
// no filter
SKIP;
CASE(test_resize_upsample_scales_linear)
// no filter
SKIP;
CASE(test_resize_upsample_scales_linear_align_corners)
// no filter
SKIP;
CASE(test_resize_upsample_scales_nearest)
// no filter
SKIP;
CASE(test_resize_upsample_sizes_cubic)
// no filter
SKIP;
CASE(test_resize_upsample_sizes_nearest)
// no filter
SKIP;
CASE(test_resize_upsample_sizes_nearest_ceil_half_pixel)
// no filter
SKIP;
CASE(test_resize_upsample_sizes_nearest_floor_align_corners)
// no filter
SKIP;
CASE(test_resize_upsample_sizes_nearest_round_prefer_ceil_asymmetric)
// no filter
SKIP;
CASE(test_reversesequence_batch)
// no filter
CASE(test_reversesequence_time)
@@ -145,3 +145,24 @@
"test_unique_sorted_with_axis_3d",
"test_unique_sorted_with_negative_axis",
"test_unique_sorted_without_axis",
"test_resize_downsample_scales_nearest",
"test_resize_downsample_sizes_cubic",
"test_resize_downsample_sizes_nearest",
"test_resize_upsample_scales_cubic",
"test_resize_upsample_scales_cubic_A_n0p5_exclude_outside",
"test_resize_upsample_scales_cubic_align_corners",
"test_resize_upsample_scales_cubic_asymmetric",
"test_resize_upsample_scales_linear",
"test_resize_upsample_scales_linear_align_corners",
"test_resize_upsample_scales_nearest",
"test_resize_upsample_sizes_cubic",
"test_resize_upsample_sizes_nearest",
"test_resize_upsample_sizes_nearest_ceil_half_pixel",
"test_resize_upsample_sizes_nearest_floor_align_corners",
"test_resize_upsample_sizes_nearest_round_prefer_ceil_asymmetric",
"test_resize_downsample_scales_cubic",
"test_resize_downsample_scales_cubic_A_n0p5_exclude_outside",
"test_resize_downsample_scales_linear",
"test_resize_downsample_sizes_linear_pytorch_half_pixel",
"test_resize_downsample_sizes_nearest_tf_half_pixel_for_nn",
"test_resize_tf_crop_and_resize",
@@ -158,29 +158,8 @@
"test_reduce_sum_negative_axes_keepdims_example",
"test_reduce_sum_negative_axes_keepdims_random", // ---- same as above ---
"test_reshape_allowzero_reordered", // incompatible type of input tensor #0 'data': CV_8UC1 given, CV_32FC1 expected in function 'setGraphInput'
"test_resize_downsample_scales_cubic", // Issue:: Parser: layer_id.find(node_proto.input(i)) == layer_id.end() in function 'parseResize'
"test_resize_downsample_scales_cubic_A_n0p5_exclude_outside", // ---- same as above ---
"test_resize_downsample_scales_cubic_align_corners", // ---- same as above ---
"test_resize_downsample_scales_linear", // ---- same as above ---
"test_resize_downsample_scales_linear_align_corners", // ---- same as above ---
"test_resize_downsample_scales_nearest", // ---- same as above ---
"test_resize_downsample_sizes_cubic", // ---- same as above ---
"test_resize_downsample_sizes_linear_pytorch_half_pixel", // ---- same as above ---
"test_resize_downsample_sizes_nearest", // ---- same as above ---
"test_resize_downsample_sizes_nearest_tf_half_pixel_for_nn", // ---- same as above ---
"test_resize_tf_crop_and_resize", // ---- same as above ---
"test_resize_upsample_scales_cubic", // Issue:: Parser: layer_id.find(node_proto.input(i)) == layer_id.end() in function 'parseResize'
"test_resize_upsample_scales_cubic_A_n0p5_exclude_outside", // ---- same as above ---
"test_resize_upsample_scales_cubic_align_corners", // ---- same as above ---
"test_resize_upsample_scales_cubic_asymmetric", // ---- same as above ---
"test_resize_upsample_scales_linear", // ---- same as above ---
"test_resize_upsample_scales_linear_align_corners", // ---- same as above ---
"test_resize_upsample_scales_nearest", // ---- same as above ---
"test_resize_upsample_sizes_cubic", // ---- same as above ---
"test_resize_upsample_sizes_nearest", // ---- same as above ---
"test_resize_upsample_sizes_nearest_ceil_half_pixel", // ---- same as above ---
"test_resize_upsample_sizes_nearest_floor_align_corners", // ---- same as above ---
"test_resize_upsample_sizes_nearest_round_prefer_ceil_asymmetric", // ---- same as above ---
"test_reversesequence_batch", // Issue:: Parser: Can't create layer "onnx_node_output_0!y" of type "ReverseSequence" in function 'getLayerInstance'
"test_reversesequence_time", // ---- same as above ---
"test_rnn_seq_length", // Issue:: Parser: Can't create layer "onnx_node_output_1!Y_h" of type "RNN" in function 'getLayerInstance'