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

Merge pull request #27666 from abhishek-gola:bitshift_layer_add

Added bitshift layer to new DNN engine #27666

### 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-08-18 20:03:28 +05:30
committed by GitHub
parent c41bc110aa
commit defc988c0d
8 changed files with 203 additions and 16 deletions
@@ -532,6 +532,12 @@ CV__DNN_INLINE_NS_BEGIN
static Ptr<FlattenLayer> create(const LayerParams &params);
};
class CV_EXPORTS BitShiftLayer : public Layer
{
public:
static Ptr<BitShiftLayer> create(const LayerParams& params);
};
class CV_EXPORTS SqueezeLayer : public Layer
{
public:
+1
View File
@@ -112,6 +112,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(BitShift, BitShiftLayer);
CV_DNN_REGISTER_LAYER_CLASS(Convolution, ConvolutionLayer);
CV_DNN_REGISTER_LAYER_CLASS(Deconvolution, DeconvolutionLayer);
+137
View File
@@ -0,0 +1,137 @@
// 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"
// ONNX reference: BitShift operator
// Spec: https://onnx.ai/onnx/operators/onnx__BitShift.html
// Supported opsets: ai.onnx opset 11 and newer
// NOTE: Broadcasting is NOT fully supported. Only two cases are handled:
// 1) array (input[0]) shifted by array (input[1]) of the SAME SHAPE
// 2) array (input[0]) shifted by a SCALAR (0-D) shift amount
namespace cv {
namespace dnn {
template<typename T, typename U>
static inline T doShift(T inputVal, U shiftVal, int direction, int bitWidth)
{
return (uint64_t)shiftVal >= (uint64_t)bitWidth
? T(0)
: T(direction ? (inputVal >> shiftVal) : (inputVal << shiftVal));
}
template<typename T, int CvTypeConst, int BitWidth>
void runBitShift(const Mat& input, const Mat& shift, Mat& output, int direction)
{
output.create(input.dims, input.size.p, input.type());
const size_t numElements = input.total();
const T* inputPtr = input.ptr<T>();
T* outputPtr = output.ptr<T>();
if (shift.total() == 1)
{
T shiftScalar = 0;
tensorToScalar(shift, CvTypeConst, &shiftScalar);
parallel_for_(Range(0, (int)numElements), [&](const Range& r){
for (int i = r.start; i < r.end; ++i)
outputPtr[i] = doShift<T,T>(inputPtr[i], shiftScalar, direction, BitWidth);
});
}
else
{
CV_Assert(shift.size == input.size);
CV_Assert(shift.type() == CvTypeConst);
const T* shiftPtr = shift.ptr<T>();
parallel_for_(Range(0, (int)numElements), [&](const Range& r){
for (int i = r.start; i < r.end; ++i)
outputPtr[i] = doShift<T,T>(inputPtr[i], shiftPtr[i], direction, BitWidth);
});
}
}
class BitShiftLayerImpl CV_FINAL : public BitShiftLayer
{
int direction_; // 0=LEFT, 1=RIGHT
public:
BitShiftLayerImpl(const LayerParams& params)
{
setParamsFrom(params);
direction_ = params.get<int>("direction", 0);
}
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() >= 2);
bool isScalarShift = inputs[1].empty() || total(inputs[1]) == 1;
CV_Assert(isScalarShift || inputs[1] == inputs[0]);
outputs.assign(1, inputs[0]);
return false;
}
void getTypes(const std::vector<MatType>& in, const int reqOut, const int reqInt,
std::vector<MatType>& out, std::vector<MatType>& internals) const CV_OVERRIDE
{
CV_Assert(in.size() >= 2);
int t = in[0];
CV_Assert(t == CV_8U || t == CV_16U || t == CV_32U || t == CV_64U);
CV_Assert(in[1] == in[0]);
out.assign(reqOut, MatType(t));
internals.assign(reqInt, MatType(t));
}
void forward(InputArrayOfArrays inputs_arr, OutputArrayOfArrays outputs_arr,
OutputArrayOfArrays) CV_OVERRIDE
{
std::vector<Mat> inputs, outputs;
inputs_arr.getMatVector(inputs);
outputs_arr.getMatVector(outputs);
CV_Assert(inputs.size() >= 2);
const Mat& input = inputs[0];
const Mat& shift = inputs[1];
Mat& output = outputs[0];
const int depth = input.depth();
if (depth == CV_8U)
{
runBitShift<uint8_t, CV_8U, 8>(input, shift, output, direction_);
}
else if (depth == CV_16U)
{
runBitShift<uint16_t, CV_16U, 16>(input, shift, output, direction_);
}
else if (depth == CV_32U)
{
runBitShift<uint32_t, CV_32U, 32>(input, shift, output, direction_);
}
else if (depth == CV_64U)
{
runBitShift<uint64_t, CV_64U, 64>(input, shift, output, direction_);
}
else
{
CV_Error(Error::StsNotImplemented, "BitShift: unsupported depth");
}
}
};
Ptr<BitShiftLayer> BitShiftLayer::create(const LayerParams& params)
{
return Ptr<BitShiftLayer>(new BitShiftLayerImpl(params));
}
}}
@@ -1861,6 +1861,27 @@ Mat getMatFromTensor(const opencv_onnx::TensorProto& tensor_proto, bool uint8ToI
Mat(sizes, CV_8U, rawdata).copyTo(blob);
}
}
else if (datatype == opencv_onnx::TensorProto_DataType_UINT16)
{
if (!tensor_proto.int32_data().empty())
Mat(sizes, CV_32SC1, (void*)tensor_proto.int32_data().data()).convertTo(blob, CV_16UC1);
else
Mat(sizes, CV_16UC1, rawdata).copyTo(blob);
}
else if (datatype == opencv_onnx::TensorProto_DataType_UINT32)
{
if (!tensor_proto.int32_data().empty())
Mat(sizes, CV_32SC1, (void*)tensor_proto.int32_data().data()).convertTo(blob, CV_32UC1);
else
Mat(sizes, CV_32UC1, rawdata).copyTo(blob);
}
else if (datatype == opencv_onnx::TensorProto_DataType_UINT64)
{
if (!tensor_proto.int64_data().empty())
Mat(sizes, CV_64SC1, (void*)tensor_proto.int64_data().data()).convertTo(blob, CV_64UC1);
else
Mat(sizes, CV_64UC1, rawdata).copyTo(blob);
}
else if (datatype == opencv_onnx::TensorProto_DataType_BOOL)
{
Mat(sizes, CV_Bool, rawdata).copyTo(blob);
+22
View File
@@ -229,6 +229,7 @@ protected:
void parseUnsqueeze (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseUpsample (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseTopK2 (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
void parseBitShift (LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto);
// Domain: com.microsoft
// URL: https://github.com/microsoft/onnxruntime/blob/master/docs/ContribOperators.md
@@ -1605,6 +1606,26 @@ void ONNXImporter2::parseSize(LayerParams& layerParams, const opencv_onnx::NodeP
addLayer(layerParams, node_proto);
}
void ONNXImporter2::parseBitShift(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
// ONNX spec: direction is required string attr: "LEFT" or "RIGHT"
// https://onnx.ai/onnx/operators/onnx__BitShift.html
layerParams.type = "BitShift";
String dir = layerParams.get<String>("direction", "");
CV_Assert(dir == "LEFT" || dir == "RIGHT");
CV_Assert(!dir.empty());
if (!dir.empty())
{
if (dir == "LEFT" || dir == "left")
layerParams.set("direction", 0);
else if (dir == "RIGHT" || dir == "right")
layerParams.set("direction", 1);
}
addLayer(layerParams, node_proto);
}
void ONNXImporter2::parseTrilu(LayerParams& layerParams, const opencv_onnx::NodeProto& node_proto)
{
int ninputs = node_proto.input_size();
@@ -2465,6 +2486,7 @@ void ONNXImporter2::buildDispatchMap_ONNX_AI(int opset_version)
dispatch["IsInf"] = &ONNXImporter2::parseIsInf;
dispatch["Det"] = &ONNXImporter2::parseDet;
dispatch["Upsample"] = &ONNXImporter2::parseUpsample;
dispatch["BitShift"] = &ONNXImporter2::parseBitShift;
dispatch["SoftMax"] = dispatch["Softmax"] = dispatch["LogSoftmax"] = &ONNXImporter2::parseSoftMax;
dispatch["DetectionOutput"] = &ONNXImporter2::parseDetectionOutput;
dispatch["CumSum"] = &ONNXImporter2::parseCumSum;
@@ -263,21 +263,21 @@ CASE(test_bernoulli_seed)
CASE(test_bernoulli_seed_expanded)
// no filter
CASE(test_bitshift_left_uint16)
// no filter
SKIP;
CASE(test_bitshift_left_uint32)
// no filter
SKIP;
CASE(test_bitshift_left_uint64)
// no filter
SKIP;
CASE(test_bitshift_left_uint8)
// no filter
SKIP;
CASE(test_bitshift_right_uint16)
// no filter
SKIP;
CASE(test_bitshift_right_uint32)
// no filter
SKIP;
CASE(test_bitshift_right_uint64)
// no filter
SKIP;
CASE(test_bitshift_right_uint8)
// no filter
SKIP;
CASE(test_cast_BFLOAT16_to_FLOAT)
// no filter
CASE(test_cast_DOUBLE_to_FLOAT)
@@ -105,3 +105,11 @@
"test_mod_uint16",
"test_mod_uint32",
"test_mod_uint64",
"test_bitshift_left_uint16",
"test_bitshift_left_uint32",
"test_bitshift_left_uint64",
"test_bitshift_left_uint8",
"test_bitshift_right_uint16",
"test_bitshift_right_uint32",
"test_bitshift_right_uint64",
"test_bitshift_right_uint8",
@@ -15,14 +15,6 @@
"test_bernoulli_expanded", // ---- same as above ---
"test_bernoulli_seed", // ---- same as above ---
"test_bernoulli_seed_expanded", // ---- same as above ---
"test_bitshift_left_uint16", // Issue::Unsuppoted data type
"test_bitshift_left_uint32", // Issue::Unsuppoted data type
"test_bitshift_left_uint64", // Issue::Unsuppoted data type
"test_bitshift_left_uint8", // Issues::Layer::Can't create layer "onnx_node_output_0!z" of type "BitShift" in function 'getLayerInstance'
"test_bitshift_right_uint16", // Issue::Unsuppoted data type
"test_bitshift_right_uint32", // Issue::Unsuppoted data type
"test_bitshift_right_uint64", // Issue::Unsuppoted data type
"test_bitshift_right_uint8", // Issues::Layer::Can't create layer "onnx_node_output_0!z" of type "BitShift" in function 'getLayerInstance'
"test_cast_BFLOAT16_to_FLOAT", // Issue::Unsuppoted data type
"test_cast_DOUBLE_to_FLOAT16", // Issue::Unsuppoted data type
"test_cast_FLOAT16_to_DOUBLE", // Issue::Unsuppoted data type