Merge pull request #29785 from abhishek-gola:image_decoder_layer

Add Image Decoder ONNX Layer - #29785

### 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
2026-08-25 17:14:44 +03:00
committed by GitHub
parent e12dba3ff7
commit 8b7dc43c22
7 changed files with 156 additions and 10 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ ocv_add_dispatched_file_force_all("layers/cpu_kernels/transpose_kernels" AVX AVX
ocv_add_dispatched_file_force_all("layers/cpu_kernels/gridsample_kernels" AVX AVX2 NEON RVV LASX)
ocv_add_dispatched_file_force_all("layers/cpu_kernels/nary_eltwise_kernels" AVX AVX2 NEON RVV LASX)
ocv_add_module(dnn opencv_core opencv_imgproc opencv_geometry WRAP python java objc js)
ocv_add_module(dnn opencv_core opencv_imgproc opencv_geometry OPTIONAL opencv_imgcodecs WRAP python java objc js)
include(${CMAKE_CURRENT_LIST_DIR}/cmake/plugin.cmake)
@@ -1925,6 +1925,12 @@ CV__DNN_INLINE_NS_BEGIN
static Ptr<NonZeroLayer> create(const LayerParams& params);
};
class CV_EXPORTS ImageDecoderLayer : public Layer
{
public:
static Ptr<ImageDecoderLayer> create(const LayerParams& params);
};
class CV_EXPORTS InstanceNormLayer : public Layer {
public:
float epsilon;
+1
View File
@@ -114,6 +114,7 @@ void initializeLayerFactory()
CV_DNN_REGISTER_LAYER_CLASS(Interp, InterpLayer);
CV_DNN_REGISTER_LAYER_CLASS(Pad2, Pad2Layer);
CV_DNN_REGISTER_LAYER_CLASS(NonZero, NonZeroLayer);
CV_DNN_REGISTER_LAYER_CLASS(ImageDecoder, ImageDecoderLayer);
CV_DNN_REGISTER_LAYER_CLASS(QuantizeLinear, QuantizeLinearLayer);
CV_DNN_REGISTER_LAYER_CLASS(DynamicQuantizeLinear, DynamicQuantizeLinearLayer);
CV_DNN_REGISTER_LAYER_CLASS(NonMaxSuppression, NonMaxSuppressionLayer);
@@ -0,0 +1,122 @@
// 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) 2026, BigVision LLC, all rights reserved.
// Third party copyrights are property of their respective owners.
#include "../precomp.hpp"
#include "layers_common.hpp"
#include <opencv2/dnn/shape_utils.hpp>
#include <opencv2/imgproc.hpp>
#ifdef HAVE_OPENCV_IMGCODECS
#include <opencv2/imgcodecs.hpp>
#endif
namespace cv {
namespace dnn {
// ONNX ImageDecoder operator
// Spec: https://onnx.ai/onnx/operators/onnx__ImageDecoder.html
// Supported opsets: 20
class ImageDecoderLayerImpl CV_FINAL : public ImageDecoderLayer
{
public:
enum PixelFormat { PF_RGB, PF_BGR, PF_GRAYSCALE };
PixelFormat pixelFormat;
ImageDecoderLayerImpl(const LayerParams& params)
{
setParamsFrom(params);
String pf = params.get<String>("pixel_format", "RGB");
if (pf == "RGB")
pixelFormat = PF_RGB;
else if (pf == "BGR")
pixelFormat = PF_BGR;
else if (pf == "Grayscale")
pixelFormat = PF_GRAYSCALE;
else
CV_Error_(Error::StsBadArg, ("DNN/ImageDecoder: unsupported pixel_format '%s'", pf.c_str()));
}
virtual bool dynamicOutputShapes() const CV_OVERRIDE
{
return true;
}
bool supportBackend(int backendId) CV_OVERRIDE
{
return backendId == DNN_BACKEND_OPENCV;
}
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() == 1);
int channels = pixelFormat == PF_GRAYSCALE ? 1 : 3;
MatShape out({-1, -1, channels});
outputs.assign(1, out);
return false;
}
void getTypes(const std::vector<MatType>& /*inputs*/, const int requiredOutputs,
const int /*requiredInternals*/, std::vector<MatType>& outputs,
std::vector<MatType>& /*internals*/) const CV_OVERRIDE
{
outputs.assign(requiredOutputs, CV_8U);
}
void forward(InputArrayOfArrays in_arr, OutputArrayOfArrays out_arr, OutputArrayOfArrays) CV_OVERRIDE
{
CV_Assert(in_arr.size().area() == 1);
Mat encoded = in_arr.getMat(0);
CV_CheckTypeEQ(encoded.type(), CV_8UC1, "DNN/ImageDecoder: input must be a uint8 tensor");
CV_Assert(encoded.isContinuous());
#ifndef HAVE_OPENCV_IMGCODECS
CV_UNUSED(out_arr);
CV_Error(Error::StsNotImplemented, "DNN/ImageDecoder: OpenCV was built without imgcodecs support");
#else
Mat buf(1, (int)encoded.total(), CV_8UC1, encoded.data);
Mat decoded;
if (pixelFormat == PF_GRAYSCALE)
{
Mat color = imdecode(buf, IMREAD_COLOR_BGR);
cvtColor(color, decoded, COLOR_BGR2GRAY);
}
else
{
decoded = imdecode(buf, pixelFormat == PF_RGB ? IMREAD_COLOR_RGB : IMREAD_COLOR_BGR);
}
const int channels = pixelFormat == PF_GRAYSCALE ? 1 : 3;
MatShape outShape({decoded.rows, decoded.cols, channels});
// Zero-copy reshape; ternary guards the empty case since reshape() throws on it.
Mat Y = decoded.empty() ? Mat(outShape, CV_8U) : decoded.reshape(1, outShape);
auto kind = out_arr.kind();
if (kind == _InputArray::STD_VECTOR_MAT)
{
std::vector<Mat>& out_mats = out_arr.getMatVecRef();
out_mats.resize(1);
out_mats[0] = Y;
}
else
{
CV_Assert(kind == _InputArray::STD_VECTOR_UMAT);
std::vector<UMat>& out_umats = out_arr.getUMatVecRef();
out_umats.resize(1);
Y.copyTo(out_umats[0]);
}
#endif
}
};
Ptr<ImageDecoderLayer> ImageDecoderLayer::create(const LayerParams& params)
{
return makePtr<ImageDecoderLayerImpl>(params);
}
}} // namespace cv::dnn
+8
View File
@@ -227,6 +227,7 @@ protected:
void parseRange (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseReduce (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseNonZero (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseImageDecoder (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseRelu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseTrilu (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseIsNaN (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
@@ -2175,6 +2176,12 @@ void ONNXImporter2::parseNonZero(LayerParams& layerParams, const opencv_onnx::No
addLayer(layerParams, node_proto);
}
void ONNXImporter2::parseImageDecoder(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
layerParams.type = "ImageDecoder";
addLayer(layerParams, node_proto);
}
void ONNXImporter2::parseSoftMax(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
const std::string& layer_type = node_proto.op_type();
@@ -2941,6 +2948,7 @@ void ONNXImporter2::buildDispatchMap_ONNX_AI()
dispatch["Abs"] = &ONNXImporter2::parseAbs;
dispatch["PRelu"] = &ONNXImporter2::parsePRelu;
dispatch["NonZero"] = &ONNXImporter2::parseNonZero;
dispatch["ImageDecoder"] = &ONNXImporter2::parseImageDecoder;
dispatch["LpNormalization"] = &ONNXImporter2::parseLpNormalization;
dispatch["LRN"] = &ONNXImporter2::parseLRN;
dispatch["InstanceNormalization"] = &ONNXImporter2::parseInstanceNormalization;
@@ -1204,6 +1204,24 @@ CASE(test_if_opt)
// no filter
CASE(test_if_seq)
// no filter
CASE(test_image_decoder_decode_bmp_rgb)
SKIP;
CASE(test_image_decoder_decode_jpeg2k_rgb)
SKIP;
CASE(test_image_decoder_decode_jpeg_bgr)
SKIP;
CASE(test_image_decoder_decode_jpeg_grayscale)
SKIP;
CASE(test_image_decoder_decode_jpeg_rgb)
SKIP;
CASE(test_image_decoder_decode_png_rgb)
SKIP;
CASE(test_image_decoder_decode_pnm_rgb)
SKIP;
CASE(test_image_decoder_decode_tiff_rgb)
SKIP;
CASE(test_image_decoder_decode_webp_rgb)
SKIP;
CASE(test_instancenorm_epsilon)
// no filter
CASE(test_instancenorm_example)
@@ -201,15 +201,6 @@
"test_identity_sequence", // Issue:: Unkonwn error
"test_if_opt", // Issue::Failed to allocate 17059022683624350 bytes in function 'OutOfMemoryError'
"test_if_seq", // Issue::typeProto.has_tensor_type() in function 'dumpValueInfoProto'
"test_image_decoder_decode_bmp_rgb",
"test_image_decoder_decode_jpeg2k_rgb",
"test_image_decoder_decode_jpeg_bgr",
"test_image_decoder_decode_jpeg_grayscale",
"test_image_decoder_decode_jpeg_rgb",
"test_image_decoder_decode_png_rgb",
"test_image_decoder_decode_pnm_rgb",
"test_image_decoder_decode_tiff_rgb",
"test_image_decoder_decode_webp_rgb",
"test_l2normalization_axis_0", //nan
"test_loop13_seq", // Loop with tensor sequences output, not yet supported in OpenCV
"test_loop16_seq_none", // Loop with optional tensor sequences, not yet supported in OpenCV