diff --git a/.github/workflows/PR-4.x.yaml b/.github/workflows/PR-4.x.yaml index bd90f16ca2..f33cc37d5d 100644 --- a/.github/workflows/PR-4.x.yaml +++ b/.github/workflows/PR-4.x.yaml @@ -29,3 +29,7 @@ jobs: Linux-RISC-V-Clang: uses: opencv/ci-gha-workflow/.github/workflows/OCV-Contrib-PR-4.x-RISCV.yaml@main + + openEuler2203-x64: + if: "${{ contains(github.event.pull_request.labels.*.name, 'category: cann') }}" + uses: opencv/ci-gha-workflow/.github/workflows/OCV-Contrib-PR-4.x-O22-CANN.yaml@main diff --git a/modules/cannops/CMakeLists.txt b/modules/cannops/CMakeLists.txt new file mode 100644 index 0000000000..0c16c5eb14 --- /dev/null +++ b/modules/cannops/CMakeLists.txt @@ -0,0 +1,17 @@ + if(IOS OR WINRT OR ANDROID OR APPLE OR WIN32 OR (NOT HAVE_CANN)) + ocv_module_disable(cannops) + endif() + +set(the_description "Ascend-accelerated Operations on Matrices") + +ocv_add_module(cannops opencv_core WRAP python) +ocv_module_include_directories(${CANN_INCLUDE_DIRS}) +ocv_glob_module_sources() +ocv_install_used_external_targets(${CANN_LIBRARIES}) +ocv_create_module(${CANN_LIBRARIES}) + +ocv_include_directories(${CMAKE_SOURCE_DIR}/modules/ts/include) + +ocv_add_accuracy_tests(DEPENDS_ON opencv_cannops) +ocv_add_perf_tests(DEPENDS_ON opencv_cannops) +ocv_add_samples(opencv_cannops) diff --git a/modules/cannops/Dockerfile b/modules/cannops/Dockerfile new file mode 100644 index 0000000000..939999eed4 --- /dev/null +++ b/modules/cannops/Dockerfile @@ -0,0 +1,67 @@ +# User guides +# +# 0. Install Ascend driver on host. +# (https://www.hiascend.com/en/hardware/firmware-drivers) +# +# 1. Run docker container. +# docker run -it \ +# --name opencv \ +# --device /dev/davinci0 \ +# --device /dev/davinci_manager \ +# --device /dev/devmm_svm \ +# --device /dev/hisi_hdc \ +# -v /usr/local/dcmi:/usr/local/dcmi \ +# -v /usr/local/bin/npu-smi:/usr/local/bin/npu-smi \ +# -v /usr/local/Ascend/driver/lib64/:/usr/local/Ascend/driver/lib64/ \ +# opencv bash +# +# 2. Check environment. +# npu-smi info +# +# 3. Compile opencv with Ascend NPU backend. +# cmake -DWITH_CANN=1 +# +# 4. Run opencv_test_cannops. +# ./bin/opencv_test_cannops + +FROM openeuler/openeuler:22.03-lts-sp2 + +RUN yum install -y \ + git \ + wget \ + gcc \ + g++ \ + cmake \ + make \ + python-pip \ + python3-devel + +RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple \ + numpy \ + sympy \ + decorator \ + scipy \ + attrs \ + psutil + +# Install CANN +RUN wget https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/CANN/CANN%207.0.RC1/Ascend-cann-toolkit_7.0.RC1_linux-"$(uname -i)".run && \ + chmod +x Ascend-cann-toolkit_7.0.RC1_linux-"$(uname -i)".run && \ + ./Ascend-cann-toolkit_7.0.RC1_linux-"$(uname -i)".run --quiet --install && \ + rm -f ./Ascend-cann-toolkit_7.0.RC1_linux-"$(uname -i)".run + +# Install kernel +RUN wget https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/CANN/CANN%207.0.RC1/Ascend-cann-kernels-310p_7.0.RC1_linux.run && \ + chmod +x Ascend-cann-kernels-310p_7.0.RC1_linux.run && \ + ./Ascend-cann-kernels-310p_7.0.RC1_linux.run --quiet --install && \ + rm -f ./Ascend-cann-kernels-310p_7.0.RC1_linux.run + +ENV LD_LIBRARY_PATH=/usr/local/Ascend/driver/lib64:/usr/local/Ascend/driver/lib64/common:/usr/local/Ascend/driver/lib64/driver:$LD_LIBRARY_PATH:/usr/lib64 +ENV ASCEND_TOOLKIT_HOME=/usr/local/Ascend/ascend-toolkit/latest +ENV LD_LIBRARY_PATH=${ASCEND_TOOLKIT_HOME}/lib64:${ASCEND_TOOLKIT_HOME}/lib64/plugin/opskernel:${ASCEND_TOOLKIT_HOME}/lib64/plugin/nnengine:${ASCEND_TOOLKIT_HOME}/opp/built-in/op_impl/ai_core/tbe/op_tiling:$LD_LIBRARY_PATH +ENV PYTHONPATH=${ASCEND_TOOLKIT_HOME}/python/site-packages:${ASCEND_TOOLKIT_HOME}/opp/built-in/op_impl/ai_core/tbe:$PYTHONPATH +ENV PATH=${ASCEND_TOOLKIT_HOME}/bin:${ASCEND_TOOLKIT_HOME}/compiler/ccec_compiler/bin:$PATH +ENV ASCEND_AICPU_PATH=${ASCEND_TOOLKIT_HOME} +ENV ASCEND_OPP_PATH=${ASCEND_TOOLKIT_HOME}/opp +ENV TOOLCHAIN_HOME=${ASCEND_TOOLKIT_HOME}/toolkit +ENV ASCEND_HOME_PATH=${ASCEND_TOOLKIT_HOME} diff --git a/modules/cannops/include/opencv2/cann.hpp b/modules/cannops/include/opencv2/cann.hpp new file mode 100644 index 0000000000..30555dd825 --- /dev/null +++ b/modules/cannops/include/opencv2/cann.hpp @@ -0,0 +1,328 @@ +// 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. + +#ifndef OPENCV_CANNOPS_CANN_HPP +#define OPENCV_CANNOPS_CANN_HPP + +#include "opencv2/core.hpp" + +/** + @defgroup cann Ascend-accelerated Computer Vision + @{ + @defgroup canncore Core part + @{ + @defgroup cann_struct Data Structures + @defgroup cann_init Initializeation and Information + @} + @} + */ + +namespace cv +{ +namespace cann +{ +class AscendStream; + +//! @addtogroup cann_struct +//! @{ + +//=================================================================================== +// AscendMat +//=================================================================================== + +/** @brief Base storage class for NPU memory with reference counting. + * AscendMat class has a similar interface with Mat and AscendMat, and work on [Ascend + * NPU](https://www.hiascend.com/) backend. + * @sa Mat cuda::GpuMat + */ +class AscendStream; +class CV_EXPORTS_W AscendMat +{ +public: + class CV_EXPORTS_W Allocator + { + public: + virtual ~Allocator() {} + // basic allocator + virtual std::shared_ptr allocate(size_t size) = 0; + // allocator must fill data, step and refcount fields + virtual bool allocate(AscendMat* mat, int rows, int cols, size_t elemSize) = 0; + }; + + /** + * @brief Create default allocator for AscendMat. This allocator alloc memory from device for + * specific size. + */ + CV_WRAP static AscendMat::Allocator* defaultAllocator(); + + /** + * @brief Set allocator for AscendMat. + * @param allocator + */ + CV_WRAP static void setDefaultAllocator(AscendMat::Allocator* allocator); + + //! default constructor + CV_WRAP explicit AscendMat(AscendMat::Allocator* allocator_ = AscendMat::defaultAllocator()); + + //! constructs AscendMat of the specified size and type + CV_WRAP AscendMat(int rows, int cols, int type, + AscendMat::Allocator* allocator = AscendMat::defaultAllocator()); + //! constructs AscendMat of the specified size and type + CV_WRAP AscendMat(Size size, int type, + AscendMat::Allocator* allocator = AscendMat::defaultAllocator()); + + //! constructs AscendMat and fills it with the specified value s + CV_WRAP AscendMat(int rows, int cols, int type, Scalar& s, + AscendMat::Allocator* allocator = AscendMat::defaultAllocator()); + //! constructs AscendMat and fills it with the specified value s + CV_WRAP AscendMat(Size size, int type, Scalar& s, + AscendMat::Allocator* allocator = AscendMat::defaultAllocator()); + + //! copy constructor + CV_WRAP AscendMat(const AscendMat& m); + + //! constructs AscendMat by crop a certain area from another + CV_WRAP AscendMat(InputArray _m, const Rect& roi); + CV_WRAP AscendMat(InputArray _m, const Rect& roi, AscendStream& stream); + + //! builds AscendMat from host memory (Blocking call) + CV_WRAP explicit AscendMat(InputArray arr, AscendStream& stream, + AscendMat::Allocator* allocator = AscendMat::defaultAllocator()); + + //! assignment operators + AscendMat& operator=(const AscendMat& m); + + //! sets some of the AscendMat elements to s (Blocking call) + CV_WRAP AscendMat& setTo(const Scalar& s); + //! sets some of the AscendMat elements to s (Non-Blocking call) + CV_WRAP AscendMat& setTo(const Scalar& s, AscendStream& stream); + + //! sets all of the AscendMat elements to float (Blocking call) + CV_WRAP AscendMat& setTo(float sc); + + //! sets all of the AscendMat elements to float (Non-Blocking call) + CV_WRAP AscendMat& setTo(float sc, AscendStream& stream); + + //! swaps with other smart pointer + CV_WRAP void swap(AscendMat& mat); + + //! allocates new AscendMat data unless the AscendMat already has specified size and type + CV_WRAP void create(int rows, int cols, int type); + + //! upload host memory data to AscendMat (Blocking call) + CV_WRAP void upload(InputArray arr); + //! upload host memory data to AscendMat (Non-Blocking call) + CV_WRAP void upload(InputArray arr, AscendStream& stream); + + //! download data from AscendMat to host (Blocking call) + CV_WRAP void download(OutputArray dst) const; + //! download data from AscendMat to host (Non-Blocking call) + CV_WRAP void download(OutputArray dst, AscendStream& stream) const; + + //! converts AscendMat to another datatype (Blocking call) + CV_WRAP void convertTo(CV_OUT AscendMat& dst, int rtype) const; + + //! converts AscendMat to another datatype (Non-Blocking call) + CV_WRAP void convertTo(CV_OUT AscendMat& dst, int rtype, AscendStream& stream) const; + + //! converts AscendMat to another datatype, dst mat is allocated. (Non-Blocking call) + CV_WRAP void convertTo(CV_OUT AscendMat& dst, AscendStream& stream) const; + + //! returns true iff the AscendMat data is continuous + //! (i.e. when there are no gaps between successive rows) + CV_WRAP bool isContinuous() const; + + //! returns element size in bytes + CV_WRAP size_t elemSize() const; + + //! returns the size of element channel in bytes + CV_WRAP size_t elemSize1() const; + + //! returns element type + CV_WRAP int type() const; + + //! returns element type + CV_WRAP int depth() const; + + //! returns number of channels + CV_WRAP int channels() const; + + //! returns step/elemSize1() + CV_WRAP size_t step1() const; + + //! returns AscendMat size : width == number of columns, height == number of rows + CV_WRAP Size size() const; + + //! returns true if AscendMat data is NULL + CV_WRAP bool empty() const; + + //! internal use method: updates the continuity flag + CV_WRAP void updateContinuityFlag(); + + /*! includes several bit-fields: + - the magic signature + - continuity flag + - depth + - number of channels + */ + int flags; + + //! the number of rows and columns + int rows, cols; + + //! a distance between successive rows in bytes; includes the gap if any + CV_PROP size_t step; + + //! pointer to the data + std::shared_ptr data; + + //! helper fields used in locateROI and adjustROI + uchar* datastart; + const uchar* dataend; + + //! allocator + Allocator* allocator; +}; + +class AscendStream; +class AscendStreamAccessor; +class AscendEvent; +class AscendEventAccessor; +class DefaultDeviceInitializer; + +//=================================================================================== +// AscendStream +//=================================================================================== + +/** @brief In AscendCL Stream(AscendStream) is a task queue. Stream is used to manage the + * parallelism of tasks. The tasks inside a Stream are executed sequentially, that is, the Stream + * executes sequentially according to the sent tasks; the tasks in different Streams are executed in + * parallel. + * + * All Non-blocking functions should pass parameter stream, These function returns immediately after + * the task is submitted. Caller should wait stream until completion. + * + * Blocking functions implicityly use the default stream, and synchronize stream before function + * return. + * @sa cuda::Stream + */ + +// TODO: Stream is defined in namespace cuda, and pybind code does not use a namespace of stream, +// change stream name to AscendStream to avoid confilct. +class CV_EXPORTS_W AscendStream +{ +public: + CV_WRAP AscendStream(); + + //! blocks the current CPU thread until all operations in the stream are complete. + CV_WRAP void waitForCompletion(); + + //! blocks the current CPU thread until event trigger. + CV_WRAP void waitAscendEvent(const cv::cann::AscendEvent& event); + + /** + * @brief return default AscendStream object for default Acl stream. + */ + CV_WRAP static AscendStream& Null(); + + // acl symbols CANNOT used in any hpp files. Use a inner class to avoid acl symbols defined in + // hpp. + class Impl; + + void addTensorHolder(const std::shared_ptr& holder); + +private: + Ptr impl_; + AscendStream(const Ptr& impl); + + friend class AscendStreamAccessor; + friend class DefaultDeviceInitializer; +}; + +/** + * @brief AscendEvent to synchronize between different streams. + */ +class CV_EXPORTS_W AscendEvent +{ +public: + CV_WRAP AscendEvent(); + + //! records an event + CV_WRAP void record(AscendStream& stream); + + //! waits for an event to complete + CV_WRAP void waitForComplete() const; + + class Impl; + +private: + Ptr impl_; + AscendEvent(const Ptr& impl); + + friend class AscendEventAccessor; +}; + +/** @brief Bindings overload to create a Stream object from the address stored in an existing CANN + * Runtime API stream pointer (aclrtStream). + * @param AscendStreamAddress Memory address stored in a CANN Runtime API stream pointer + * (aclrtStream). The created Stream object does not perform any allocation or deallocation and + * simply wraps existing raw CANN Runtime API stream pointer. + * @note Overload for generation of bindings only, not exported or intended for use internally fro + * C++. + */ +CV_EXPORTS_W AscendStream wrapStream(size_t AscendStreamAddress); + +//! @} cann_struct + +//=================================================================================== +// Initialization & Info +//=================================================================================== + +//! @addtogroup cann_init +//! @{ + +//! Get Ascend matrix object from Input array, upload matrix memory if need. (Non-Blocking call) +AscendMat getInputMat(InputArray src, AscendStream& stream); + +//! Get Ascend matrix object from Output array, upload matrix memory if need. +AscendMat getOutputMat(OutputArray dst, int rows, int cols, int type, AscendStream& stream); + +//! Sync output matrix to Output array, download matrix memory if need. +void syncOutput(const AscendMat& dst, OutputArray _dst, AscendStream& stream); + +/** + * @brief Choose Ascend npu device. + */ +CV_EXPORTS_W void setDevice(int device); + +/** + * @brief Clear all context created in current Ascend device. + */ +CV_EXPORTS_W void resetDevice(); + +/** + * @brief Get current Ascend device. + */ +CV_EXPORTS_W int32_t getDevice(); + +/** + * @brief init AscendCL. + */ +CV_EXPORTS_W void initAcl(); + +/** + * @brief finalize AscendCL. + * @note finalizeAcl only can be called once for a process. Call this function after all AscendCL + * options finished. + */ +CV_EXPORTS_W void finalizeAcl(); + +//! @} cann_init + +} // namespace cann +} // namespace cv + +#include "opencv2/cann.inl.hpp" + +#endif // OPENCV_CANNOPS_CANN_HPP diff --git a/modules/cannops/include/opencv2/cann.inl.hpp b/modules/cannops/include/opencv2/cann.inl.hpp new file mode 100644 index 0000000000..4a97466b37 --- /dev/null +++ b/modules/cannops/include/opencv2/cann.inl.hpp @@ -0,0 +1,97 @@ +// 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. + +#ifndef OPENCV_CANNOPS_CANN_INL_HPP +#define OPENCV_CANNOPS_CANN_INL_HPP + +#include "opencv2/cann.hpp" + +namespace cv +{ +namespace cann +{ +inline AscendMat::AscendMat(AscendMat::Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), datastart(0), dataend(0), + allocator(allocator_) +{ + // Empty mat is also continuous. + flags |= Mat::CONTINUOUS_FLAG; +} + +inline AscendMat::AscendMat(int rows_, int cols_, int type_, AscendMat::Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), datastart(0), dataend(0), + allocator(allocator_) +{ + if (rows_ > 0 && cols_ > 0) + create(rows_, cols_, type_); +} + +inline AscendMat::AscendMat(Size size_, int type_, AscendMat::Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), datastart(0), dataend(0), + allocator(allocator_) +{ + if (size_.height > 0 && size_.width > 0) + create(size_.height, size_.width, type_); +} + +inline AscendMat::AscendMat(InputArray arr, AscendStream& stream, AscendMat::Allocator* allocator_) + : flags(0), rows(0), cols(0), step(0), datastart(0), dataend(0), + allocator(allocator_) +{ + upload(arr, stream); +} + +inline AscendMat::AscendMat(const AscendMat& m) + : flags(m.flags), rows(m.rows), cols(m.cols), step(m.step), data(m.data), + datastart(m.datastart), dataend(m.dataend), allocator(m.allocator) +{} + +inline AscendMat& AscendMat::operator=(const AscendMat& m) +{ + if (this != &m) + { + AscendMat temp(m); + swap(temp); + } + + return *this; +} + +inline void AscendMat::swap(AscendMat& b) +{ + std::swap(flags, b.flags); + std::swap(rows, b.rows); + std::swap(cols, b.cols); + std::swap(step, b.step); + std::swap(data, b.data); + std::swap(datastart, b.datastart); + std::swap(dataend, b.dataend); + std::swap(allocator, b.allocator); +} + +inline bool AscendMat::isContinuous() const { return (flags & Mat::CONTINUOUS_FLAG) != 0; } + +inline size_t AscendMat::elemSize() const { return CV_ELEM_SIZE(flags); } + +inline size_t AscendMat::elemSize1() const { return CV_ELEM_SIZE1(flags); } + +inline int AscendMat::type() const { return CV_MAT_TYPE(flags); } + +inline int AscendMat::depth() const { return CV_MAT_DEPTH(flags); } + +inline int AscendMat::channels() const { return CV_MAT_CN(flags); } + +inline size_t AscendMat::step1() const { return step / elemSize1(); } + +inline Size AscendMat::size() const { return Size(cols, rows); } + +inline bool AscendMat::empty() const { return data == 0; } + +inline AscendStream::AscendStream(const Ptr& impl) : impl_(impl) {} + +inline AscendEvent::AscendEvent(const Ptr& impl) : impl_(impl) {} +} // namespace cann +} // namespace cv + +#endif // OPENCV_CANNOPS_CANN_INL_HPP diff --git a/modules/cannops/include/opencv2/cann_call.hpp b/modules/cannops/include/opencv2/cann_call.hpp new file mode 100644 index 0000000000..651bff8bba --- /dev/null +++ b/modules/cannops/include/opencv2/cann_call.hpp @@ -0,0 +1,157 @@ +// 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. + +#ifndef OPENCV_CANNOPS_CANN_CALL_HPP +#define OPENCV_CANNOPS_CANN_CALL_HPP + +#include +#include +#include +#include +#include "opencv2/cann.hpp" + +class aclopAttr; + +namespace cv +{ +namespace cann +{ +// Warpper for functions in CANN, callers should not call CANN's api directly, but should call the +// function provided in cann_call. +void aclrtMallocWarpper(void** data, size_t size); +void aclrtFreeWarpper(void* data); +void aclrtMemcpyWarpper(std::shared_ptr& dst, size_t offset, const void* src, size_t size, + AscendStream& stream); +void aclrtMemcpyWarpper(void* dst, const std::shared_ptr& src, size_t offset, size_t size, + AscendStream& stream); +void aclrtMemcpyWarpper(std::shared_ptr& dst, size_t dstOffset, + const std::shared_ptr& src, size_t srcOffset, size_t size, + AscendStream& stream); +void aclrtMemcpy2dWarpper(std::shared_ptr& dst, size_t offset, size_t dpitch, + const void* src, size_t spitch, size_t width, size_t length, + AscendStream& stream); +void aclrtMemcpy2dWarpper(void* dst, size_t dpitch, const std::shared_ptr& src, + size_t offset, size_t spitch, size_t width, size_t length, + AscendStream& stream); +void aclrtMemsetWarpper(std::shared_ptr& ptr, int32_t value, size_t count, + AscendStream& stream); +//! Type mapping between opencv and cann. +aclDataType getACLType(int opencvdepth); +//! Malloc and upload raw data to devices. +std::shared_ptr mallocAndUpload(const void* data, size_t size, AscendStream& stream, + AscendMat::Allocator* allocator); +/** + * @brief Warpper of CANN streams. + */ +class AscendStream::Impl +{ +public: + aclrtStream stream; + bool ownStream; + /** + * @brief Ascend and CANN use stream to implement asynchronous calls. Which means when function + * returns, operator may not finish, even not start. If caller free any tensors that participate + * in this operatation, it have a chance to access invalid memory. + * All tensors should add to holder, holder will be cleaned by waitForCompletion function, or when + * the stream is destructing. + */ + std::set> tensorHolders; + Impl(); + explicit Impl(aclrtStream stream); + void AddTensorHolder(const std::shared_ptr& tensorData); +}; + +/** + * @brief Warpper of CANN event. + */ +class AscendEvent::Impl +{ +public: + aclrtEvent event; + bool ownEvent; + + Impl(); + explicit Impl(aclrtEvent event); + ~Impl(); +}; + +/** + * @brief Parameter type for call_call interfaces. + */ +struct AscendTensor +{ + const char* name; + std::shared_ptr data; + size_t dataSize; + std::vector dims; + aclDataType dtype; + aclFormat format; + AscendTensor(){}; + AscendTensor(std::shared_ptr _data, size_t _dataSize, int64_t* _dims, size_t _dimSize, + aclDataType _dtype, const char* _name = "", aclFormat _format = ACL_FORMAT_ND); + AscendTensor(std::shared_ptr _data, size_t _dataSize, std::vector& _dims, + aclDataType _dtype, const char* _name = "", aclFormat _format = ACL_FORMAT_ND) + : name(_name), data(_data), dataSize(_dataSize), dims(_dims), dtype(_dtype), + format(_format){}; + AscendTensor(const AscendMat& ascendMat, const char* _name = "", + aclFormat format = ACL_FORMAT_ND); +}; + +/** + * @brief Interface to call operators in CANN package. + */ +class OperatorRunner +{ +private: + std::vector inputBuffers_; + std::vector outputBuffers_; + std::vector inputDesc_; + std::vector outputDesc_; + aclopAttr* opAttr_; + bool opAttrInit; + std::string op; + + std::set> holder; + + OperatorRunner& addInput(AscendTensor& mat); + OperatorRunner& addOutput(AscendTensor& mat); + +public: + OperatorRunner() : opAttrInit(false) {} + virtual ~OperatorRunner() { reset(); } + OperatorRunner& setOp(const char* op); + OperatorRunner& addInput(const AscendMat& mat); + OperatorRunner& addOutput(AscendMat& mat); + OperatorRunner& addAttr(float value, const char* name); + OperatorRunner& addAttr(const char* value, const char* name); + OperatorRunner& addAttr(int value, const char* name); + OperatorRunner& addAttr(bool value, const char* name); + OperatorRunner& addAttr(const int64_t* value, int size, const char* name); + OperatorRunner& addInput(const AscendMat& mat, const char* name); + OperatorRunner& addInput(const Scalar& sc, int type, const char* name); + + template + OperatorRunner& addInput(const T* value, int64_t* dims, size_t dimSize, aclDataType type, + const char* name) + { + int64_t size = dims[0]; + for (size_t i = 1; i < dimSize; i++) + size *= dims[i]; + + size_t dataSize = size * sizeof(T); + std::shared_ptr ptr = + mallocAndUpload(value, dataSize, AscendStream::Null(), AscendMat::defaultAllocator()); + + AscendTensor tensor(ptr, dataSize, dims, dimSize, type, name); + return addInput(tensor); + } + OperatorRunner& addOutput(AscendMat& mat, const char* name); + OperatorRunner& reset(); + OperatorRunner& run(AscendStream& stream); +}; + +} // namespace cann +} // namespace cv + +#endif // OPENCV_CANNOPS_CANN_CALL_HPP diff --git a/modules/cannops/include/opencv2/cann_interface.hpp b/modules/cannops/include/opencv2/cann_interface.hpp new file mode 100644 index 0000000000..6667eb5851 --- /dev/null +++ b/modules/cannops/include/opencv2/cann_interface.hpp @@ -0,0 +1,516 @@ +// 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. + +#ifndef OPENCV_CANNOPS_CANN_INTERFACE_HPP +#define OPENCV_CANNOPS_CANN_INTERFACE_HPP + +#include "opencv2/cann.hpp" + +namespace cv +{ +namespace cann +{ + +/** + @addtogroup cann + @{ + @defgroup cannops Operations for Ascend Backend. + @{ + @defgroup cannops_elem Per-element Operations + @defgroup cannops_core Core Operations on Matrices + @defgroup cannimgproc Image Processing + @} + @} + */ + +//! @addtogroup cannops_elem +//! @{ + +/** @brief Computes a matrix-matrix or matrix-scalar sum. + * @param src1 First source matrix or scalar. + * @param src2 Second source matrix or scalar. Matrix should have the same size and type as src1 . + * @param dst Destination matrix that has the same size and number of channels as the input + * array(s). The depth is defined by dtype or src1 depth. + * @param mask Optional operation mask, 8-bit single channel array, that specifies elements of the + * destination array to be changed. The mask can be used only with single channel images. + * @param dtype Optional depth of the output array. + * @param stream AscendStream for the asynchronous version. + * @sa cv::add cuda::add + */ +CV_EXPORTS_W void add(const InputArray src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +// This code should not be compiled nor analyzed by doxygen. This interface only for python binding +// code generation. add(InputArray, InputArray ...) can accept Scalar as its parametr.(Scalar -> Mat +// -> InputArray) +#ifdef NEVER_DEFINED +CV_EXPORTS_W void add(const InputArray src1, const Scalar& src2, OutputArray dst, + const InputArray mask = noArray(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +CV_EXPORTS_W void add(const Scalar& src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +#endif +// More overload functions. In order to decouple from the main opencv repository and simplify +// user calling methods, besides the traditional Input/OutputArray parameters, some +// overloaded functions for the AcendMat parameter is also provided. +/** @overload */ +CV_EXPORTS_W void add(const AscendMat& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void add(const AscendMat& src1, const Scalar& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void add(const Scalar& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); + +/** @brief Computes a matrix-matrix or matrix-scalar difference. + * @param src1 First source matrix or scalar. + * @param src2 Second source matrix or scalar. Matrix should have the same size and type as src1 . + * @param dst Destination matrix that has the same size and number of channels as the input + * array(s). The depth is defined by dtype or src1 depth. + * @param mask Optional operation mask, 8-bit single channel array, that specifies elements of the + * destination array to be changed. The mask can be used only with single channel images. + * @param dtype Optional depth of the output array. + * @param stream AscendStream for the asynchronous version. + * @sa cv::subtract cuda::subtract + */ +CV_EXPORTS_W void subtract(const InputArray src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +#ifdef NEVER_DEFINED +CV_EXPORTS_W void subtract(const InputArray src1, const Scalar& src2, OutputArray dst, + const InputArray mask = noArray(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +CV_EXPORTS_W void subtract(const Scalar& src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +#endif +/** @overload */ +CV_EXPORTS_W void subtract(const AscendMat& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void subtract(const AscendMat& src1, const Scalar& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void subtract(const Scalar& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), int dtype = -1, + AscendStream& stream = AscendStream::Null()); + +/** @brief Computes a matrix-matrix or matrix-scalar per-element product. + * @param src1 First source matrix or scalar. + * @param src2 Second source matrix or scalar. Matrix should have the same size and type as src1 . + * @param dst Destination matrix that has the same size and number of channels as the input + * array(s). The depth is defined by dtype or src1 depth. + * @param scale Optional scale factor. + * @param dtype Optional depth of the output array. + * @param stream AscendStream for the asynchronous version. + * @sa cv::multiply cuda::multiply + */ +CV_EXPORTS_W void multiply(const InputArray src1, const InputArray src2, OutputArray dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +#ifdef NEVER_DEFINED +CV_EXPORTS_W void multiply(const InputArray src1, const Scalar& src2, OutputArray dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +CV_EXPORTS_W void multiply(const Scalar& src1, const InputArray src2, OutputArray dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +#endif +/** @overload */ +CV_EXPORTS_W void multiply(const AscendMat& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void multiply(const AscendMat& src1, const Scalar& src2, CV_OUT AscendMat& dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void multiply(const Scalar& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); + +/** @brief Computes a matrix-matrix or matrix-scalar division. + * @param src1 First source matrix or scalar. + * @param src2 Second source matrix or scalar. Matrix should have the same size and type as src1 . + * @param dst Destination matrix that has the same size and number of channels as the input + * array(s). The depth is defined by dtype or src1 depth. + * @param scale Optional scale factor. + * @param dtype Optional depth of the output array. + * @param stream AscendStream for the asynchronous version. + * @sa cv::divide cuda::divide + */ +CV_EXPORTS_W void divide(const InputArray src1, const InputArray src2, OutputArray dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +#ifdef NEVER_DEFINED +CV_EXPORTS_W void divide(const InputArray src1, const Scalar& src2, OutputArray dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +CV_EXPORTS_W void divide(const Scalar& src1, const InputArray src2, OutputArray dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +#endif +CV_EXPORTS_W void divide(const AscendMat& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void divide(const AscendMat& src1, const Scalar& src2, CV_OUT AscendMat& dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void divide(const Scalar& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + float scale = 1, int dtype = -1, + AscendStream& stream = AscendStream::Null()); + +/** @brief Performs a per-element bitwise conjunction of two matrices (or of matrix and scalar). + * @param src1 First source matrix or scalar. + * @param src2 Second source matrix or scalar. + * @param dst Destination matrix that has the same size and number of channels as the input + * array(s). The depth is defined by dtype or src1 depth. + * @param mask Optional operation mask, 8-bit single channel array, that specifies elements of the + * destination array to be changed. The mask can be used only with single channel images. + * @param stream AscendStream for the asynchronous version. + * @sa cv::bitwise_and cuda::bitwise_and + */ +CV_EXPORTS_W void bitwise_and(const InputArray src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +#ifdef NEVER_DEFINED +CV_EXPORTS_W void bitwise_and(const InputArray src1, const Scalar& src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +CV_EXPORTS_W void bitwise_and(const Scalar& src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +#endif +CV_EXPORTS_W void bitwise_and(const AscendMat& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void bitwise_and(const AscendMat& src1, const Scalar& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void bitwise_and(const Scalar& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); + +/** @brief Performs a per-element bitwise disjunction of two matrices (or of matrix and scalar). + * @param src1 First source matrix or scalar. + * @param src2 Second source matrix or scalar. + * @param dst Destination matrix that has the same size and number of channels as the input + * array(s). The depth is defined by dtype or src1 depth. + * @param mask Optional operation mask, 8-bit single channel array, that specifies elements of the + * destination array to be changed. The mask can be used only with single channel images. + * @param stream AscendStream for the asynchronous version. + * @sa cv::bitwise_or cuda::bitwise_or + */ +CV_EXPORTS_W void bitwise_or(const InputArray src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +#ifdef NEVER_DEFINED +CV_EXPORTS_W void bitwise_or(const InputArray src1, const Scalar& src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +CV_EXPORTS_W void bitwise_or(const Scalar& src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +#endif +CV_EXPORTS_W void bitwise_or(const AscendMat& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void bitwise_or(const AscendMat& src1, const Scalar& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void bitwise_or(const Scalar& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); + +/** @brief Performs a per-element bitwise exclusive or operation of two matrices (or of matrix and + * scalar). + * @param src1 First source matrix or scalar. + * @param src2 Second source matrix or scalar. + * @param dst Destination matrix that has the same size and number of channels as the input + * array(s). The depth is defined by dtype or src1 depth. + * @param mask Optional operation mask, 8-bit single channel array, that specifies elements of the + * destination array to be changed. The mask can be used only with single channel images. + * @param stream AscendStream for the asynchronous version. + * @sa cv::bitwise_xor cuda::bitwise_xor + */ +CV_EXPORTS_W void bitwise_xor(const InputArray src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +#ifdef NEVER_DEFINED +CV_EXPORTS_W void bitwise_xor(const InputArray src1, const Scalar& src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +CV_EXPORTS_W void bitwise_xor(const Scalar& src1, const InputArray src2, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +#endif +CV_EXPORTS_W void bitwise_xor(const AscendMat& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void bitwise_xor(const AscendMat& src1, const Scalar& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void bitwise_xor(const Scalar& src1, const AscendMat& src2, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); + +/** @brief Performs a per-element bitwise inversion. + * @param src First source matrix. + * @param dst Destination matrix that has the same size and number of channels as the input + * array(s). The depth is defined by dtype or src1 depth. + * @param mask Optional operation mask, 8-bit single channel array, that specifies elements of the + * destination array to be changed. The mask can be used only with single channel images. + * @param stream AscendStream for the asynchronous version. + * @sa cv::bitwise_not cuda::bitwise_not + */ +CV_EXPORTS_W void bitwise_not(const InputArray src, OutputArray dst, + const InputArray mask = noArray(), + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void bitwise_not(const AscendMat& src, CV_OUT AscendMat& dst, + const AscendMat& mask = AscendMat(), + AscendStream& stream = AscendStream::Null()); + +/** @brief Computes the weighted sum of two arrays. + +@param src1 First source array. +@param alpha Weight for the first array elements. +@param src2 Second source array of the same size and channel number as src1 . +@param beta Weight for the second array elements. +@param dst Destination array that has the same size and number of channels as the input arrays. +@param gamma Scalar added to each sum. +@param dtype Optional depth of the destination array. When both input arrays have the same depth, +dtype can be set to -1, which will be equivalent to src1.depth(). +@param stream Stream for the asynchronous version. + +The function addWeighted calculates the weighted sum of two arrays as follows: + +\f[\texttt{dst} (I)= \texttt{saturate} ( \texttt{src1} (I)* \texttt{alpha} + \texttt{src2} (I)* +\texttt{beta} + \texttt{gamma} )\f] + +where I is a multi-dimensional index of array elements. In case of multi-channel arrays, each +channel is processed independently. + +@sa cv::addWeighted cv::cuda::addWeighted + */ +CV_EXPORTS_W void addWeighted(const InputArray src1, double alpha, const InputArray src2, + double beta, double gamma, OutputArray dst, int dtype = -1, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void addWeighted(const AscendMat& src1, double alpha, const AscendMat& src2, + double beta, double gamma, CV_OUT AscendMat& dst, int dtype = -1, + AscendStream& stream = AscendStream::Null()); + +/** @brief Applies a fixed-level threshold to each array element. + +@param src Source array (single-channel). +@param dst Destination array with the same size and type as src . +@param thresh Threshold value. +@param maxval Maximum value to use with THRESH_BINARY and THRESH_BINARY_INV threshold types. +@param type Threshold type. For details, see threshold . The THRESH_MASK, THRESH_OTSU and +THRESH_TRIANGLE threshold types are not supported. +@param stream AscendStream for the asynchronous version. + +@sa cv::threshold cv::cuda::threshold +*/ +CV_EXPORTS_W double threshold(const InputArray src, OutputArray dst, double thresh, double maxval, + int type, AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W double threshold(const AscendMat& src, CV_OUT AscendMat& dst, double thresh, + double maxval, int type, AscendStream& stream = AscendStream::Null()); + +//! @} cannops_elem + +//! @addtogroup cannops_core +//! @{ + +/** @brief Makes a multi-channel matrix out of several single-channel matrices. + +@param src Array/vector of source matrices. +@param n Number of source matrices. +@param dst Destination matrix. +@param stream AscendStream for the asynchronous version. + +@sa cv::merge cv::cuda::merge + */ +CV_EXPORTS_W void merge(const AscendMat* src, size_t n, CV_OUT AscendMat& dst, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void merge(const std::vector& src, CV_OUT AscendMat& dst, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void merge(const AscendMat* src, size_t n, OutputArray& dst, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void merge(const std::vector& src, OutputArray& dst, + AscendStream& stream = AscendStream::Null()); + +/** @brief Copies each plane of a multi-channel matrix into an array. + +@param src Source matrix. +@param dst Destination array/vector of single-channel matrices. +@param stream AscendStream for the asynchronous version. + +@sa cv::split cv::cuda::split + */ +CV_EXPORTS_W void split(const AscendMat& src, AscendMat* dst, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void split(const AscendMat& src, CV_OUT std::vector& dst, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void split(const InputArray src, AscendMat* dst, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void split(const InputArray src, CV_OUT std::vector& dst, + AscendStream& stream = AscendStream::Null()); + +/** @brief Transposes a matrix. + +@param src Source matrix. +@param dst Destination matrix. +@param stream AscendStream for the asynchronous version. + +@sa cv::transpose cv::cuda::transpose + */ +CV_EXPORTS_W void transpose(InputArray src, OutputArray dst, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void transpose(const AscendMat& src, CV_OUT AscendMat& dst, + AscendStream& stream = AscendStream::Null()); +/** @brief Flips a 2D matrix around vertical, horizontal, or both axes. + +@param src Source matrix. +@param dst Destination matrix. +@param flipCode Flip mode for the source: +- 0 Flips around x-axis. +- \> 0 Flips around y-axis. +- \< 0 Flips around both axes. +@param stream AscendStream for the asynchronous version. + +@sa cv::flip cv::cuda::flip + */ +CV_EXPORTS_W void flip(InputArray src, OutputArray dst, int flipCode, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void flip(const AscendMat& src, CV_OUT AscendMat& dst, int flipCode, + AscendStream& stream = AscendStream::Null()); +/** @brief Rotates a 2D array in multiples of 90 degrees. +The function cv::rotate rotates the array in one of three different ways: +* Rotate by 90 degrees clockwise (rotateCode = ROTATE_90_CLOCKWISE). +* Rotate by 180 degrees clockwise (rotateCode = ROTATE_180). +* Rotate by 270 degrees clockwise (rotateCode = ROTATE_90_COUNTERCLOCKWISE). +@param src input array. +@param dst output array of the same type as src. The size is the same with ROTATE_180, +and the rows and cols are switched for ROTATE_90_CLOCKWISE and ROTATE_90_COUNTERCLOCKWISE. +@param rotateCode an enum to specify how to rotate the array; see the enum #RotateFlags +@param stream AscendStream for the asynchronous version. + +@sa cv::rotate +*/ +CV_EXPORTS_W void rotate(InputArray src, OutputArray dst, int rotateCode, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void rotate(const AscendMat& src, CV_OUT AscendMat& dst, int rotateMode, + AscendStream& stream = AscendStream::Null()); + +/** @brief crop a 2D array. +The function crops the matrix by given cv::Rect. +Output matrix must be of the same depth as input one, size is specified by given rect size. + +@param src input array. +@param rect a rect to crop a array to +@param stream AscendStream for the asynchronous version. + +@sa cv::gapi::crop +*/ +CV_EXPORTS_W AscendMat crop(InputArray src, const Rect& rect, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W AscendMat crop(const AscendMat& src, const Rect& rect, + AscendStream& stream = AscendStream::Null()); +/** @brief Resizes an image src down to or up to the specified size. +@param src input image +@param dst output image; it has the size dsize (when it is non-zero) or the size computed from +src.size(), fx, and fy; the type of dst is the same as of src. +@param dsize output image size; if it equals zero, it is computed as: + \f[𝚍𝚜𝚒𝚣𝚎 = 𝚂𝚒𝚣𝚎(𝚛𝚘𝚞𝚗𝚍(𝚏𝚡*𝚜𝚛𝚌.𝚌𝚘𝚕𝚜), 𝚛𝚘𝚞𝚗𝚍(𝚏𝚢*𝚜𝚛𝚌.𝚛𝚘𝚠𝚜))\f] + Either dsize or both fx and fy must be non-zero. +@param fx scale factor along the horizontal axis; when it equals 0, it is computed as +\f[(𝚍𝚘𝚞𝚋𝚕𝚎)𝚍𝚜𝚒𝚣𝚎.𝚠𝚒𝚍𝚝𝚑/𝚜𝚛𝚌.𝚌𝚘𝚕𝚜\f] + +@param fy scale factor along the vertical axis; when it equals 0, it is computed as +\f[(𝚍𝚘𝚞𝚋𝚕𝚎)𝚍𝚜𝚒𝚣𝚎.𝚑𝚎𝚒𝚐𝚑𝚝/𝚜𝚛𝚌.𝚛𝚘𝚠𝚜\f] +@param interpolation interpolation method(see **cv.cann.InterpolationFlags**) +@sa cv::resize +*/ + +//! interpolation algorithm +enum InterpolationFlags +{ + /** nearest neighbor interpolation */ + INTER_NEAREST = 0, + /** bilinear interpolation */ + INTER_LINEAR = 1, + /** bicubic interpolation */ + INTER_CUBIC = 2, + /** resampling using pixel area relation. It may be a preferred method for image decimation, as + it gives moire'-free results. But when the image is zoomed, it is similar to the INTER_NEAREST + method. */ + INTER_AREA = 3, + /** mask for interpolation codes */ + INTER_MAX = 7, +}; + +CV_EXPORTS_W void resize(InputArray _src, OutputArray _dst, Size dsize, double inv_scale_x, + double inv_scale_y, int interpolation, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void resize(const AscendMat& src, CV_OUT AscendMat& dst, Size dsize, double inv_scale_x, + double inv_scale_y, int interpolation, + AscendStream& stream = AscendStream::Null()); + +//! @} cannops_core + +//! @addtogroup cannimgproc +//! @{ + +/** @brief Converts an image from one color space to another. + +@param src Source image with CV_8U , CV_16U , or CV_32F depth and 1, 3, or 4 channels. +@param dst Destination image. +@param code Color space conversion code. For details, see cvtColor . +@param dstCn Number of channels in the destination image. If the parameter is 0, the number of the +channels is derived automatically from src and the code . +@param stream AscendStream for the asynchronous version. + +@sa cv::cvtColor cv::cuda::cvtColor + */ +CV_EXPORTS_W void cvtColor(const InputArray src, OutputArray dst, int code, int dstCn = 0, + AscendStream& stream = AscendStream::Null()); +/** @overload */ +CV_EXPORTS_W void cvtColor(const AscendMat& src, CV_OUT AscendMat& dst, int code, int dstCn = 0, + AscendStream& stream = AscendStream::Null()); + +//! @} cannimgproc + +} // namespace cann +} // namespace cv + +#endif // OPENCV_CANNOPS_CANN_INTERFACE_HPP diff --git a/modules/cannops/include/opencv2/cann_private.hpp b/modules/cannops/include/opencv2/cann_private.hpp new file mode 100644 index 0000000000..bcbe33feb1 --- /dev/null +++ b/modules/cannops/include/opencv2/cann_private.hpp @@ -0,0 +1,33 @@ +// 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. + +#ifndef OPENCV_CANNOPS_CANN_PRIVATE_HPP +#define OPENCV_CANNOPS_CANN_PRIVATE_HPP +#include "opencv2/cann.hpp" + +namespace cv +{ +namespace cann +{ +void arithm_op(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const char* op, + AscendStream& stream); +void arithm_op(const AscendMat& src, const Scalar& sc, AscendMat& dst, const char* op, + AscendStream& stream); +void arithm_op(const Scalar& sc, const AscendMat& src, AscendMat& dst, const char* op, + AscendStream& stream); +void arithm_op(const AscendMat& src, AscendMat& dst, const char* op, AscendStream& stream); +void arithm_op(const AscendMat& src, float scalar, AscendMat& dst, const char* op, + AscendStream& stream); +void transpose(const AscendMat& src, int64_t* perm, AscendMat& dst, AscendStream& stream); +void flip(const AscendMat& src, std::vector& asixs, AscendMat& dst, AscendStream& stream); +void crop(const AscendMat& src, AscendMat& dst, const AscendMat& sizeSrcNpu, int64_t* offset, + AscendStream& stream); +void transData(const AscendMat& src, AscendMat& dst, const char* from, const char* to, + AscendStream& stream); +void resize(const AscendMat& src, AscendMat& dst, int32_t* dstSize, int interpolation, + AscendStream& stream); +} // namespace cann +} // namespace cv + +#endif // OPENCV_CANNOPS_CANN_PRIVATE_HPP diff --git a/modules/cannops/include/opencv2/stream_accessor.hpp b/modules/cannops/include/opencv2/stream_accessor.hpp new file mode 100644 index 0000000000..ff64d7dcbc --- /dev/null +++ b/modules/cannops/include/opencv2/stream_accessor.hpp @@ -0,0 +1,39 @@ +// 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. + +#ifndef OPENCV_CANNOPS_STREAM_ACCESSOR_HPP +#define OPENCV_CANNOPS_STREAM_ACCESSOR_HPP + +#include +#include "opencv2/cann.hpp" + +namespace cv +{ +namespace cann +{ +//! @addtogroup cann_struct +//! @{ + +/** @brief Class that enables getting aclrtAscendStream from cann::AscendStream + */ +struct AscendStreamAccessor +{ + CV_EXPORTS static aclrtStream getStream(const AscendStream& stream); + CV_EXPORTS static AscendStream wrapStream(aclrtStream stream); +}; + +/** @brief Class that enables getting aclrtAscendEvent from cann::AscendEvent + */ +struct AscendEventAccessor +{ + CV_EXPORTS static aclrtEvent getEvent(const AscendEvent& event); + CV_EXPORTS static AscendEvent wrapEvent(aclrtEvent event); +}; + +//! @} cann_struct + +} // namespace cann +} // namespace cv + +#endif // OPENCV_CANNOPS_STREAM_ACCESSOR_HPP diff --git a/modules/cannops/misc/python/pyopencv_cann.hpp b/modules/cannops/misc/python/pyopencv_cann.hpp new file mode 100644 index 0000000000..02d62487c6 --- /dev/null +++ b/modules/cannops/misc/python/pyopencv_cann.hpp @@ -0,0 +1,28 @@ +// 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. + +#ifndef OPENCV_CANNOPS_PYOPENCV_CANN_HPP +#define OPENCV_CANNOPS_PYOPENCV_CANN_HPP + +#ifdef HAVE_OPENCV_CORE + +#include "opencv2/cann.hpp" + +typedef std::vector vector_AscendMat; +typedef cann::AscendMat::Allocator AscendMat_Allocator; + +CV_PY_TO_CLASS(cann::AscendMat); +CV_PY_TO_CLASS(cann::AscendStream); + +CV_PY_TO_CLASS_PTR(cann::AscendMat); +CV_PY_TO_CLASS_PTR(cann::AscendMat::Allocator); + +CV_PY_FROM_CLASS(cann::AscendMat); +CV_PY_FROM_CLASS(cann::AscendStream); + +CV_PY_FROM_CLASS_PTR(cann::AscendMat::Allocator); + +#endif // HAVE_OPENCV_CORE + +#endif // OPENCV_CANNOPS_PYOPENCV_CANN_HPP diff --git a/modules/cannops/misc/python/test/test_cannops.py b/modules/cannops/misc/python/test/test_cannops.py new file mode 100644 index 0000000000..f1b53bc192 --- /dev/null +++ b/modules/cannops/misc/python/test/test_cannops.py @@ -0,0 +1,281 @@ +# 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. + +import cv2 as cv +from tests_common import NewOpenCVTests +import numpy as np + +def genMask(mask, listx, listy): + for row in range(mask.shape[0]): + for col in range(mask.shape[1]): + if (row in listx and col in listx) or (row in listy and col in listy): + mask[row][col] = 1 + mask = mask.astype(np.uint8) + return mask + + +mask = np.zeros((5, 5)) +listx = [0, 1] +listy = [1, 2] +mask = genMask(mask, listx, listy) + + +class cannop_test(NewOpenCVTests): + def test_ascend(self): + cv.cann.initAcl() + cv.cann.getDevice() + cv.cann.setDevice(0) + stream = cv.cann.AscendStream_Null() + cv.cann.wrapStream(id(stream)) + cv.cann.resetDevice() + + def test_arithmetic(self): + # input data + npMat1 = np.random.random((5, 5, 3)).astype(int) + npMat2 = np.random.random((5, 5, 3)).astype(int) + cv.cann.setDevice(0) + + # ACLMat input data + aclMat1 = cv.cann.AscendMat() + aclMat1.upload(npMat1) + aclMat2 = cv.cann.AscendMat() + aclMat2.upload(npMat2) + aclMask = cv.cann.AscendMat() + aclMask.upload(mask) + aclMatDst = cv.cann.AscendMat(aclMat1.size(), aclMat1.type()) + + # InputArray interface test + self.assertTrue(np.allclose(cv.cann.add( + npMat1, npMat2), cv.add(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.subtract( + npMat1, npMat2), cv.subtract(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.multiply( + npMat1, npMat2, scale=2), cv.multiply(npMat1, npMat2, scale=2))) + self.assertTrue(np.allclose(cv.cann.divide( + npMat1, npMat2, scale=2), cv.divide(npMat1, npMat2, scale=2))) + + # AscendMat interface test + self.assertTrue(np.allclose(cv.cann.add(aclMat1, aclMat2).download(), + cv.add(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.subtract(aclMat1, aclMat2).download(), + cv.subtract(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.multiply(aclMat1, aclMat2, scale=2).download(), + cv.multiply(npMat1, npMat2, scale=2))) + self.assertTrue(np.allclose(cv.cann.divide(aclMat1, aclMat2, scale=2).download(), + cv.divide(npMat1, npMat2, scale=2))) + + # mask + self.assertTrue(np.allclose(cv.cann.add( + npMat1, npMat2, mask=mask), cv.add(npMat1, npMat2, mask=mask))) + self.assertTrue(np.allclose(cv.cann.subtract( + npMat1, npMat2, mask=mask), cv.subtract(npMat1, npMat2, mask=mask))) + self.assertTrue(np.allclose(cv.cann.multiply(npMat1, npMat2, scale=2), + cv.multiply(npMat1, npMat2, scale=2))) + self.assertTrue(np.allclose(cv.cann.divide(npMat1, npMat2, scale=2), + cv.divide(npMat1, npMat2, scale=2))) + self.assertTrue(np.allclose(cv.cann.addWeighted(npMat1, 2, npMat2, 4, 3), + cv.addWeighted(npMat1, 2, npMat2, 4, 3))) + + self.assertTrue(np.allclose(cv.cann.add(aclMat1, aclMat2, mask=aclMask).download(), + cv.add(npMat1, npMat2, mask=mask))) + self.assertTrue(np.allclose(cv.cann.subtract(aclMat1, aclMat2, mask=aclMask).download(), + cv.subtract(npMat1, npMat2, mask=mask))) + self.assertTrue(np.allclose(cv.cann.multiply(aclMat1, aclMat2, scale=2).download(), + cv.multiply(npMat1, npMat2, scale=2))) + self.assertTrue(np.allclose(cv.cann.divide(aclMat1, aclMat2, scale=2).download(), + cv.divide(npMat1, npMat2, scale=2))) + self.assertTrue(np.allclose(cv.cann.addWeighted(aclMat1, 2, aclMat2, 4, 3).download(), + cv.addWeighted(npMat1, 2, npMat2, 4, 3))) + + # stream + stream = cv.cann.AscendStream() + matDst = cv.cann.add(npMat1, npMat2, stream=stream) + stream.waitForCompletion() + self.assertTrue(np.allclose(matDst, cv.add(npMat1, npMat2))) + matDst = cv.cann.add(npMat1, npMat2, mask=mask, stream=stream) + stream.waitForCompletion() + self.assertTrue(np.allclose(matDst, cv.add(npMat1, npMat2, mask=mask))) + matDst = cv.cann.subtract(npMat1, npMat2, mask=mask, stream=stream) + stream.waitForCompletion() + self.assertTrue(np.allclose( + matDst, cv.subtract(npMat1, npMat2, mask=mask))) + + # stream AsceendMat + aclMatDst = cv.cann.add(aclMat1, aclMat2, stream=stream) + stream.waitForCompletion() + self.assertTrue(np.allclose(aclMatDst.download(), + cv.add(npMat1, npMat2))) + + aclMatDst = cv.cann.add(aclMat1, aclMat2, mask=aclMask, stream=stream) + stream.waitForCompletion() + self.assertTrue(np.allclose(aclMatDst.download(), + cv.add(npMat1, npMat2, mask=mask))) + + aclMatDst = cv.cann.subtract(aclMat1, aclMat2, mask=aclMask, stream=stream) + stream.waitForCompletion() + self.assertTrue(np.allclose(aclMatDst.download(), + cv.subtract(npMat1, npMat2, mask=mask))) + + cv.cann.resetDevice() + + def test_logical(self): + npMat1 = np.random.random((5, 5, 3)).astype(np.uint16) + npMat2 = np.random.random((5, 5, 3)).astype(np.uint16) + cv.cann.setDevice(0) + + # ACLMat input data + aclMat1 = cv.cann.AscendMat() + aclMat1.upload(npMat1) + aclMat2 = cv.cann.AscendMat() + aclMat2.upload(npMat2) + aclMask = cv.cann.AscendMat() + aclMask.upload(mask) + + self.assertTrue(np.allclose(cv.cann.bitwise_or(npMat1, npMat2), + cv.bitwise_or(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_or( + npMat1, npMat2), cv.bitwise_or(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_and(npMat1, npMat2), + cv.bitwise_and(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_and( + npMat1, npMat2), cv.bitwise_and(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_xor(npMat1, npMat2), + cv.bitwise_xor(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_xor( + npMat1, npMat2), cv.bitwise_xor(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_not(npMat1), + cv.bitwise_not(npMat1))) + self.assertTrue(np.allclose( + cv.cann.bitwise_not(npMat1), cv.bitwise_not(npMat1))) + self.assertTrue(np.allclose(cv.cann.bitwise_and(npMat1, npMat2, mask=mask), + cv.bitwise_and(npMat1, npMat2, mask=mask))) + self.assertTrue(np.allclose(cv.cann.bitwise_or(npMat1, npMat2, mask=mask), + cv.bitwise_or(npMat1, npMat2, mask=mask))) + self.assertTrue(np.allclose(cv.cann.bitwise_not(npMat1, mask=mask), + cv.bitwise_not(npMat1, mask=mask))) + self.assertTrue(np.allclose(cv.cann.bitwise_xor(npMat1, npMat2, mask=mask), + cv.bitwise_xor(npMat1, npMat2, mask=mask))) + + # AscendMat interface + self.assertTrue(np.allclose(cv.cann.bitwise_or(aclMat1, aclMat2).download(), + cv.bitwise_or(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_or(aclMat1, aclMat2).download(), + cv.bitwise_or(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_and(aclMat1, aclMat2).download(), + cv.bitwise_and(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_and( + aclMat1, aclMat2).download(), cv.bitwise_and(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_xor(aclMat1, aclMat2).download(), + cv.bitwise_xor(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_xor( + aclMat1, aclMat2).download(), cv.bitwise_xor(npMat1, npMat2))) + self.assertTrue(np.allclose(cv.cann.bitwise_not(aclMat1).download(), + cv.bitwise_not(npMat1))) + self.assertTrue(np.allclose(cv.cann.bitwise_not(aclMat1).download(), + cv.bitwise_not(npMat1))) + self.assertTrue(np.allclose(cv.cann.bitwise_and(aclMat1, aclMat2, mask=aclMask).download(), + cv.bitwise_and(npMat1, npMat2, mask=mask))) + self.assertTrue(np.allclose(cv.cann.bitwise_or(aclMat1, aclMat2, mask=aclMask).download(), + cv.bitwise_or(npMat1, npMat2, mask=mask))) + self.assertTrue(np.allclose(cv.cann.bitwise_not(aclMat1, mask=aclMask).download(), + cv.bitwise_not(npMat1, mask=mask))) + self.assertTrue(np.allclose(cv.cann.bitwise_xor(aclMat1, aclMat2, mask=aclMask).download(), + cv.bitwise_xor(npMat1, npMat2, mask=mask))) + cv.cann.resetDevice() + + def test_imgproc(self): + npMat = (np.random.random((128, 128, 3)) * 255).astype(np.uint8) + cv.cann.setDevice(0) + aclMat = cv.cann.AscendMat() + aclMatDst = aclMat + aclMat.upload(npMat) + + # TODO try pass out param, not use return value. + # merge & split + self.assertTrue(np.allclose( + cv.cann.merge(cv.cann.split(npMat)).download(), npMat)) + self.assertTrue(np.allclose( + cv.cann.merge(cv.cann.split(aclMat)).download(), npMat)) + + # transpose + self.assertTrue(np.allclose( + cv.cann.transpose(npMat), cv.transpose(npMat))) + self.assertTrue(np.allclose( + cv.cann.transpose(aclMat).download(), cv.transpose(npMat))) + + # crop + w_off, h_off, crop_w, crop_h = 0, 0, 64, 64 + roi = [w_off, h_off, crop_w, crop_h] + self.assertTrue(np.allclose( + cv.cann.crop(npMat, roi).download(), npMat[w_off:crop_w, h_off:crop_h])) + self.assertTrue(np.allclose( + cv.cann.crop(aclMat, roi).download(), npMat[w_off:crop_w, h_off:crop_h])) + + # resize + dstSize = np.array([crop_w, crop_h]) + aclMat32F = cv.cann.AscendMat() + aclMat32F.upload(npMat.astype(np.float32)) + self.assertTrue(np.allclose(cv.cann.resize(npMat.astype(np.float32), dstSize, 0, 0, 3), + cv.resize(npMat.astype(np.float32), dstSize, 0, 0, 3))) + self.assertTrue(np.allclose(cv.cann.resize(aclMat32F, dstSize, 0, 0, 3).download(), + cv.resize(npMat.astype(np.float32), dstSize, 0, 0, 3))) + # flip + flipMode = [0, 1, -1] + for fMode in flipMode: + self.assertTrue(np.allclose(cv.cann.flip( + npMat, fMode), cv.flip(npMat, fMode))) + self.assertTrue(np.allclose(cv.cann.flip( + aclMat, fMode).download(), cv.flip(npMat, fMode))) + + # rotate + rotateMode = [0, 1, 2] + for rMode in rotateMode: + self.assertTrue(np.allclose(cv.cann.rotate( + npMat, rMode), cv.rotate(npMat, rMode))) + self.assertTrue(np.allclose(cv.cann.rotate( + aclMat, rMode).download(), cv.rotate(npMat, rMode))) + + # cvtColcor + cvtModeC1 = [cv.COLOR_GRAY2BGR, cv.COLOR_GRAY2BGRA] + cvtModeC3 = [cv.COLOR_BGR2GRAY, cv.COLOR_BGRA2BGR, cv.COLOR_BGR2RGBA, cv.COLOR_RGBA2BGR, + cv.COLOR_BGR2RGB, cv.COLOR_BGRA2RGBA, cv.COLOR_RGB2GRAY, cv.COLOR_BGRA2GRAY, + cv.COLOR_RGBA2GRAY, cv.COLOR_BGR2BGRA, cv.COLOR_BGR2YUV, cv.COLOR_RGB2YUV, + cv.COLOR_YUV2BGR, cv.COLOR_YUV2RGB, cv.COLOR_BGR2YCrCb, cv.COLOR_RGB2YCrCb, + cv.COLOR_YCrCb2BGR, cv.COLOR_YCrCb2RGB, cv.COLOR_BGR2XYZ, cv.COLOR_RGB2XYZ, + cv.COLOR_XYZ2BGR, cv.COLOR_XYZ2RGB,] + for cvtM in cvtModeC3: + self.assertTrue(np.allclose(cv.cann.cvtColor( + npMat, cvtM), cv.cvtColor(npMat, cvtM), 1)) + self.assertTrue(np.allclose(cv.cann.cvtColor( + aclMat, cvtM).download(), cv.cvtColor(npMat, cvtM), 1)) + + npMatC1 = (np.random.random((128, 128, 1)) * 255).astype(np.uint8) + aclMatC1 = cv.cann.AscendMat() + aclMatC1.upload(npMatC1) + for cvtM in cvtModeC1: + self.assertTrue(np.allclose(cv.cann.cvtColor( + npMatC1, cvtM), cv.cvtColor(npMatC1, cvtM), 1)) + self.assertTrue(np.allclose(cv.cann.cvtColor( + aclMatC1, cvtM).download(), cv.cvtColor(npMatC1, cvtM), 1)) + + # threshold + threshType = [cv.THRESH_BINARY, cv.THRESH_BINARY_INV, + cv.THRESH_TRUNC, cv.THRESH_TOZERO, cv.THRESH_TOZERO_INV] + for tType in threshType: + cvRet, cvThresh = cv.threshold( + npMat.astype(np.uint8), 127, 255, tType) + cannRet, cannThresh = cv.cann.threshold( + npMat.astype(np.float32), 127, 255, tType) + self.assertTrue(np.allclose(cvThresh, cannThresh)) + self.assertTrue(np.allclose(cvRet, cannRet)) + + aclMat.upload(npMat.astype(np.float32)) + cannRet, cannThresh = cv.cann.threshold( + aclMat, 127, 255, tType) + self.assertTrue(np.allclose(cvThresh, cannThresh.download())) + self.assertTrue(np.allclose(cvRet, cannRet)) + cv.cann.resetDevice() + +if __name__ == '__main__': + NewOpenCVTests.bootstrap() diff --git a/modules/cannops/perf/perf_core.cpp b/modules/cannops/perf/perf_core.cpp new file mode 100644 index 0000000000..a9d86fca88 --- /dev/null +++ b/modules/cannops/perf/perf_core.cpp @@ -0,0 +1,161 @@ +// 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. + +#include "perf_precomp.hpp" +#include "opencv2/cann_interface.hpp" + +namespace opencv_test +{ +namespace +{ +#define TYPICAL_ASCEND_MAT_SIZES \ + Values(::perf::sz1080p, ::perf::sz2K, ::perf::sz2160p, ::perf::sz4320p) +#define DEF_PARAM_TEST(name, ...) \ + typedef ::perf::TestBaseWithParam> name + +DEF_PARAM_TEST(NPU, Size); +DEF_PARAM_TEST(CPU, Size); + +PERF_TEST_P(NPU, MERGE, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC1); + Mat dst; + declare.in(mat, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + AscendMat ascendMat[3]; + ascendMat[0].upload(mat); + ascendMat[1].upload(mat); + ascendMat[2].upload(mat); + + TEST_CYCLE() { cv::cann::merge(&ascendMat[0], 3, dst); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MERGE, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC1); + Mat dst; + declare.in(mat, WARMUP_RNG); + Mat mats[3] = {mat, mat, mat}; + TEST_CYCLE() { cv::merge(&mats[0], 3, dst); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, SPLIT, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + declare.in(mat, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + AscendMat ascendMat[3]; + + TEST_CYCLE() { cv::cann::split(mat, &ascendMat[0]); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, SPLIT, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + declare.in(mat, WARMUP_RNG); + Mat mats[3] = {mat, mat, mat}; + TEST_CYCLE() { cv::split(mat, &mats[0]); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, TRANSPOSE, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::transpose(mat, dst); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, TRANSPOSE, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + TEST_CYCLE() { cv::transpose(mat, dst); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, FLIP, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::flip(mat, dst, -1); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, FLIP, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + TEST_CYCLE() { cv::flip(mat, dst, -1); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, ROTATE, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::rotate(mat, dst, 1); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, ROTATE, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + TEST_CYCLE() { cv::rotate(mat, dst, 1); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, CROP, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + Rect b(1, 2, 4, 4); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { AscendMat cropped_cann(mat, b); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, CROP, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + Rect b(1, 2, 4, 4); + TEST_CYCLE() { Mat cropped_cv(mat, b); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, CROP_OVERLOAD, TYPICAL_ASCEND_MAT_SIZES) +{ + Mat mat(GET_PARAM(0), CV_8UC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + Rect b(1, 2, 4, 4); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::crop(mat, b); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} +} // namespace +} // namespace opencv_test diff --git a/modules/cannops/perf/perf_cvtcolor.cpp b/modules/cannops/perf/perf_cvtcolor.cpp new file mode 100644 index 0000000000..c868d4fec0 --- /dev/null +++ b/modules/cannops/perf/perf_cvtcolor.cpp @@ -0,0 +1,69 @@ +// 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. + +#include "perf_precomp.hpp" +#include "opencv2/cann_interface.hpp" + +namespace opencv_test +{ +namespace +{ + +#define CVT_COLORS_3 \ + Values(COLOR_BGR2BGRA, COLOR_BGRA2BGR, COLOR_BGR2RGBA, COLOR_RGBA2BGR, COLOR_BGR2RGB, \ + COLOR_BGRA2RGBA, COLOR_BGR2GRAY, COLOR_BGRA2GRAY, COLOR_RGBA2GRAY, COLOR_BGR2XYZ, \ + COLOR_RGB2XYZ, COLOR_XYZ2BGR, COLOR_XYZ2RGB, COLOR_BGR2YCrCb, COLOR_RGB2YCrCb, \ + COLOR_YCrCb2BGR, COLOR_YCrCb2RGB, COLOR_BGR2YUV, COLOR_RGB2YUV, COLOR_YUV2BGR, \ + COLOR_YUV2RGB) +#define CVT_COLORS_1 Values(COLOR_GRAY2BGR, COLOR_GRAY2BGRA) +#define TYPICAL_ASCEND_MAT_SIZES \ + Values(::perf::sz1080p, ::perf::sz2K) +#define DEF_PARAM_TEST(name, ...) \ + typedef ::perf::TestBaseWithParam> name + +DEF_PARAM_TEST(NPU, Size, ColorConversionCodes); +DEF_PARAM_TEST(CPU, Size, ColorConversionCodes); + +PERF_TEST_P(NPU, CVT_COLOR_3, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, CVT_COLORS_3)) +{ + Mat mat(GET_PARAM(0), CV_32FC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::cvtColor(mat, dst, GET_PARAM(1)); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, CVT_COLOR_3, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, CVT_COLORS_3)) +{ + Mat mat(GET_PARAM(0), CV_32FC3); + Mat dst; + declare.in(mat, WARMUP_RNG); + TEST_CYCLE() { cv::cvtColor(mat, dst, GET_PARAM(1)); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, CVT_COLOR_1, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, CVT_COLORS_1)) +{ + Mat mat(GET_PARAM(0), CV_32FC1); + Mat dst; + declare.in(mat, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::cvtColor(mat, dst, GET_PARAM(1)); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, CVT_COLOR_1, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, CVT_COLORS_1)) +{ + Mat mat(GET_PARAM(0), CV_32FC1); + Mat dst; + declare.in(mat, WARMUP_RNG); + TEST_CYCLE() { cv::cvtColor(mat, dst, GET_PARAM(1)); } + SANITY_CHECK_NOTHING(); +} + +} // namespace +} // namespace opencv_test diff --git a/modules/cannops/perf/perf_element_operations.cpp b/modules/cannops/perf/perf_element_operations.cpp new file mode 100644 index 0000000000..0612abe608 --- /dev/null +++ b/modules/cannops/perf/perf_element_operations.cpp @@ -0,0 +1,211 @@ +// 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. + +#include "perf_precomp.hpp" +#include "opencv2/cann_interface.hpp" + +namespace opencv_test +{ +namespace +{ + +#define ARITHM_MAT_DEPTH Values(CV_32S, CV_32SC3) +#define TYPICAL_ASCEND_MAT_SIZES \ + Values(::perf::sz1080p, ::perf::sz2K, ::perf::sz2160p, ::perf::sz4320p) +#define DEF_PARAM_TEST(name, ...) \ + typedef ::perf::TestBaseWithParam> name + +DEF_PARAM_TEST(NPU, Size, int); +DEF_PARAM_TEST(CPU, Size, int); + +PERF_TEST_P(NPU, MAT_ADD_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::add(mat1, mat2, dst, noArray(), -1); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MAT_ADD_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + TEST_CYCLE() { cv::add(mat1, mat2, dst, noArray(), -1); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, MAT_SUB_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::subtract(mat1, mat2, dst, noArray(), -1); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MAT_SUB_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + TEST_CYCLE() { cv::subtract(mat1, mat2, dst, noArray(), -1); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, MAT_MUL_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::multiply(mat1, mat2, dst, 1, -1); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MAT_MUL_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + TEST_CYCLE() { cv::multiply(mat1, mat2, dst, 1, -1); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, MAT_DIV_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::divide(mat1, mat2, dst, 1, -1); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MAT_DIV_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + TEST_CYCLE() { cv::divide(mat1, mat2, dst, 1, -1); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, MAT_BITWISE_AND_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::bitwise_and(mat1, mat2, dst, noArray()); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MAT_BITWISE_AND_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + TEST_CYCLE() { cv::bitwise_and(mat1, mat2, dst, noArray()); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, MAT_BITWISE_OR_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::bitwise_or(mat1, mat2, dst, noArray()); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MAT_BITWISE_OR_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + TEST_CYCLE() { cv::bitwise_or(mat1, mat2, dst, noArray()); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, MAT_BITWISE_XOR_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::bitwise_xor(mat1, mat2, dst, noArray()); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MAT_BITWISE_XOR_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat1(GET_PARAM(0), GET_PARAM(1)); + Mat mat2(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat1, WARMUP_RNG); + declare.in(mat2, WARMUP_RNG); + TEST_CYCLE() { cv::bitwise_xor(mat1, mat2, dst, noArray()); } + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(NPU, MAT_BITWISE_NOT_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat, WARMUP_RNG); + cv::cann::setDevice(DEVICE_ID); + TEST_CYCLE() { cv::cann::bitwise_not(mat, dst, noArray()); } + cv::cann::resetDevice(); + SANITY_CHECK_NOTHING(); +} + +PERF_TEST_P(CPU, MAT_BITWISE_NOT_MAT, testing::Combine(TYPICAL_ASCEND_MAT_SIZES, ARITHM_MAT_DEPTH)) +{ + Mat mat(GET_PARAM(0), GET_PARAM(1)); + Mat dst; + declare.in(mat, WARMUP_RNG); + TEST_CYCLE() { cv::bitwise_not(mat, dst, noArray()); } + SANITY_CHECK_NOTHING(); +} + +} // namespace +} // namespace opencv_test diff --git a/modules/cannops/perf/perf_main.cpp b/modules/cannops/perf/perf_main.cpp new file mode 100644 index 0000000000..33503ac415 --- /dev/null +++ b/modules/cannops/perf/perf_main.cpp @@ -0,0 +1,23 @@ +// 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. + +#include "perf_precomp.hpp" +#include "opencv2/cann_interface.hpp" +using namespace perf; + +class CannEnvironment : public ::testing::Environment +{ +public: + virtual ~CannEnvironment() = default; + virtual void SetUp() CV_OVERRIDE { cv::cann::initAcl(); } + virtual void TearDown() CV_OVERRIDE { cv::cann::finalizeAcl(); } +}; + +static void initTests() +{ + CannEnvironment* cannEnv = new CannEnvironment(); + ::testing::AddGlobalTestEnvironment(cannEnv); +} + +CV_PERF_TEST_MAIN("cannops", initTests()) diff --git a/modules/cannops/perf/perf_precomp.hpp b/modules/cannops/perf/perf_precomp.hpp new file mode 100644 index 0000000000..59e2fa03d7 --- /dev/null +++ b/modules/cannops/perf/perf_precomp.hpp @@ -0,0 +1,19 @@ +// 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. + +#ifndef __OPENCV_PERF_PRECOMP_HPP__ +#define __OPENCV_PERF_PRECOMP_HPP__ + +#include "opencv2/ts.hpp" +#include "opencv2/ts/ts_perf.hpp" +#include "opencv2/cann.hpp" + +#define DEVICE_ID 0 + +using namespace perf; +using namespace testing; +using namespace cv; +using namespace cv::cann; + +#endif diff --git a/modules/cannops/samples/image_processing.cpp b/modules/cannops/samples/image_processing.cpp new file mode 100644 index 0000000000..9dca2176df --- /dev/null +++ b/modules/cannops/samples/image_processing.cpp @@ -0,0 +1,60 @@ +// 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. + +#include +#include +#include +#include + +int main(int argc, char* argv[]) +{ + cv::CommandLineParser parser(argc, argv, + "{@input|puppy.png|path to input image}" + "{@output|output.png|path to output image}" + "{help||show help}"); + parser.about("This is a sample for image processing with Ascend NPU. \n"); + if (argc != 3 || parser.has("help")) + { + parser.printMessage(); + return 0; + } + + std::string imagePath = parser.get(0); + std::string outputPath = parser.get(1); + + // read input image and generate guass noise + //! [input_noise] + cv::Mat img = cv::imread(imagePath); + // Generate gauss noise that will be added into the input image + cv::Mat gaussNoise(img.rows, img.cols, img.type()); + cv::RNG rng; + rng.fill(gaussNoise, cv::RNG::NORMAL, 0, 25); + //! [input_noise] + + // setup cann + //! [setup] + cv::cann::initAcl(); + cv::cann::setDevice(0); + //! [setup] + + //! [image-process] + cv::Mat output; + // add gauss noise to the image + cv::cann::add(img, gaussNoise, output); + // rotate the image with a certain mode (0, 1 and 2, correspond to rotation of 90, 180 and 270 + // degrees clockwise respectively) + cv::cann::rotate(output, output, 0); + // flip the image with a certain mode (0, positive and negative number, correspond to flipping + // around the x-axis, y-axis and both axes respectively) + cv::cann::flip(output, output, 0); + //! [image-process] + + cv::imwrite(outputPath, output); + + //! [tear-down-cann] + cv::cann::resetDevice(); + cv::cann::finalizeAcl(); + //! [tear-down-cann] + return 0; +} diff --git a/modules/cannops/samples/image_processing.py b/modules/cannops/samples/image_processing.py new file mode 100644 index 0000000000..dc974bdd78 --- /dev/null +++ b/modules/cannops/samples/image_processing.py @@ -0,0 +1,42 @@ +# 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. + +import numpy as np +import cv2 +import argparse + +parser = argparse.ArgumentParser(description='This is a sample for image processing with Ascend NPU.') +parser.add_argument('image', help='path to input image') +parser.add_argument('output', help='path to output image') +args = parser.parse_args() + +# read input image and generate guass noise +#! [input_noise] +img = cv2.imread(args.image) +# Generate gauss noise that will be added into the input image +gaussNoise = np.random.normal(0, 25,(img.shape[0], img.shape[1], img.shape[2])).astype(img.dtype) +#! [input_noise] + +# setup cann +#! [setup] +cv2.cann.initAcl() +cv2.cann.setDevice(0) +#! [setup] + +#! [image-process] +# add gauss noise to the image +output = cv2.cann.add(img, gaussNoise) +# rotate the image with a certain mode (0, 1 and 2, correspond to rotation of 90, 180 +# and 270 degrees clockwise respectively) +output = cv2.cann.rotate(output, 0) +# flip the image with a certain mode (0, positive and negative number, correspond to flipping +# around the x-axis, y-axis and both axes respectively) +output = cv2.cann.flip(output, 0) +#! [image-process] + +cv2.imwrite(args.output, output) + +#! [tear-down-cann] +cv2.cann.finalizeAcl() +#! [tear-down-cann] diff --git a/modules/cannops/src/ascend_mat.cpp b/modules/cannops/src/ascend_mat.cpp new file mode 100644 index 0000000000..ba17a545bb --- /dev/null +++ b/modules/cannops/src/ascend_mat.cpp @@ -0,0 +1,232 @@ +// 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. + +#include "precomp.hpp" +#include + +namespace +{ +class DefaultAllocator : public cv::cann::AscendMat::Allocator +{ +public: + std::shared_ptr allocate(size_t size) CV_OVERRIDE; + bool allocate(cv::cann::AscendMat* mat, int rows, int cols, size_t elemSize) CV_OVERRIDE; +}; + +std::shared_ptr DefaultAllocator::allocate(size_t size) +{ + uchar* data; + cv::cann::aclrtMallocWarpper((void**)(&data), size); + return std::shared_ptr(data, [](void* ptr) { cv::cann::aclrtFreeWarpper(ptr); }); +} + +bool DefaultAllocator::allocate(cv::cann::AscendMat* mat, int rows, int cols, size_t elemSize) +{ + mat->data = allocate(elemSize * cols * rows); + mat->step = cols * elemSize; + + return true; +} + +DefaultAllocator cannDefaultAllocator; +cv::cann::AscendMat::Allocator* g_defaultAllocator = &cannDefaultAllocator; +} // namespace + +namespace cv +{ +namespace cann +{ +AscendMat::Allocator* AscendMat::defaultAllocator() { return g_defaultAllocator; } + +void AscendMat::setDefaultAllocator(AscendMat::Allocator* allocator) +{ + CV_Assert(allocator != 0); + g_defaultAllocator = allocator; +} + +// TODO: this function is copied from matrix.cpp, which is a local symbol there and can not +// be refreneced, consider optimizing. +static int updateContinuityFlag(int flags, int dims, const int* size, const size_t* step) +{ + int i, j; + for (i = 0; i < dims; i++) + { + if (size[i] > 1) + break; + } + + uint64 t = (uint64)size[std::min(i, dims - 1)] * CV_MAT_CN(flags); + for (j = dims - 1; j > i; j--) + { + t *= size[j]; + if (step[j] * size[j] < step[j - 1]) + break; + } + + if (j <= i && t == (uint64)(int)t) + return flags | Mat::CONTINUOUS_FLAG; + return flags & ~Mat::CONTINUOUS_FLAG; +} + +void AscendMat::updateContinuityFlag() +{ + int sz[] = {rows, cols}; + size_t steps[] = {step, elemSize()}; + flags = cv::cann::updateContinuityFlag(flags, 2, sz, steps); +} + +void AscendMat::create(int _rows, int _cols, int _type) +{ + CV_DbgAssert(_rows >= 0 && _cols >= 0); + + _type &= Mat::TYPE_MASK; + + if (rows == _rows && cols == _cols && type() == _type && data) + return; + + if (_rows > 0 && _cols > 0) + { + flags = Mat::MAGIC_VAL + _type; + rows = _rows; + cols = _cols; + + const size_t esz = elemSize(); + + bool allocSuccess = allocator->allocate(this, rows, cols, esz); + + if (!allocSuccess) + { + // custom allocator fails, try default allocator + allocator = defaultAllocator(); + allocSuccess = allocator->allocate(this, rows, cols, esz); + CV_Assert(allocSuccess); + } + + if (esz * cols == step) + flags |= Mat::CONTINUOUS_FLAG; + + datastart = data.get(); + dataend = data.get() + step * (rows - 1) + cols * esz; + } +} + +void AscendMat::upload(InputArray arr) { upload(arr, AscendStream::Null()); } + +void AscendMat::upload(InputArray arr, AscendStream& stream) +{ + Mat mat = arr.getMat(); + CV_DbgAssert(!mat.empty()); + create(mat.rows, mat.cols, mat.type()); + aclrtMemcpy2dWarpper(data, 0, step, mat.data, mat.step[0], cols * elemSize(), rows, stream); +} + +void AscendMat::download(OutputArray dst) const { download(dst, AscendStream::Null()); } + +void AscendMat::download(OutputArray _dst, AscendStream& stream) const +{ + CV_DbgAssert(!empty()); + + _dst.create(size(), type()); + Mat dst = _dst.getMat(); + aclrtMemcpy2dWarpper(dst.data, dst.step[0], data, 0, step, cols * elemSize(), rows, stream); +} + +AscendMat::AscendMat(int rows_, int cols_, int type_, Scalar& s_, AscendMat::Allocator* allocator_) + : flags(0), rows(rows_), cols(cols_), step(0), datastart(0), dataend(0), allocator(allocator_) +{ + create(rows_, cols_, type_); + setTo(s_); +} + +AscendMat::AscendMat(Size size_, int type_, Scalar& s_, AscendMat::Allocator* allocator_) + : flags(0), rows(size_.height), cols(size_.width), step(0), datastart(0), dataend(0), + allocator(allocator_) +{ + create(size_.height, size_.width, type_); + setTo(s_); +} + +AscendMat::AscendMat(InputArray _m, const Rect& roi) : AscendMat(_m, roi, AscendStream::Null()) {} + +AscendMat::AscendMat(InputArray _m, const Rect& roi, AscendStream& stream) + : rows(roi.height), cols(roi.width), allocator(defaultAllocator()) +{ + AscendMat m; + m.upload(_m, stream); + step = m.step; + data = m.data; + flags = m.flags; + CV_Assert(0 <= roi.x && 0 <= roi.width && roi.x + roi.width <= m.cols && 0 <= roi.y && + 0 <= roi.height && roi.y + roi.height <= m.rows); + size_t esz = CV_ELEM_SIZE(flags); + size_t sizeMem = esz * roi.width * roi.height * m.channels(); + size_t offset = roi.y * m.step + roi.x * esz; + + void* dst = malloc(sizeMem); + size_t dpitch = roi.width * esz; + std::shared_ptr dstDevice = allocator->allocate(sizeMem); + aclrtMemcpy2dWarpper(dst, dpitch, data, offset, step, dpitch, roi.height, stream); + aclrtMemcpy2dWarpper(dstDevice, 0, dpitch, dst, dpitch, dpitch, roi.height, stream); + data = dstDevice; + step = dpitch; + free(dst); + updateContinuityFlag(); +} + +AscendMat& AscendMat::setTo(const Scalar& sc) { return setTo(sc, AscendStream::Null()); } + +AscendMat& AscendMat::setTo(const Scalar& sc, AscendStream& stream) +{ + size_t totalBytes = (size_t)rows * cols * elemSize(); + if (totalBytes == 0) + return *this; + + aclrtMemsetWarpper(data, 0, totalBytes, stream); + AscendMat dst(rows, cols, type()); + arithm_op(*this, sc, dst, "Add", stream); + swap(dst); + + return *this; +} + +AscendMat& AscendMat::setTo(float sc) { return setTo(sc, AscendStream::Null()); } + +AscendMat& AscendMat::setTo(float sc, AscendStream& stream) +{ + size_t totalBytes = (size_t)rows * cols * elemSize(); + if (totalBytes == 0) + return *this; + + aclrtMemsetWarpper(data, 0, totalBytes, stream); + + AscendMat dst(rows, cols, type()); + arithm_op(*this, sc, dst, "Adds", stream); + swap(dst); + + return *this; +} + +void AscendMat::convertTo(AscendMat& dst, int rtype) const +{ + convertTo(dst, rtype, AscendStream::Null()); +} + +void AscendMat::convertTo(AscendMat& dst, int _rtype, AscendStream& stream) const +{ + int cn = channels(); + dst.create(rows, cols, CV_MAKE_TYPE(_rtype, cn)); + convertTo(dst, stream); +} + +void AscendMat::convertTo(AscendMat& dst, AscendStream& stream) const +{ + OperatorRunner runner; + runner.setOp("Cast") + .addInput(*this, "x") + .addOutput(dst, "y") + .addAttr((int32_t)(getACLType(dst.depth())), "dst_type") + .run(stream); +} +} // namespace cann +} // namespace cv diff --git a/modules/cannops/src/cann_call.cpp b/modules/cannops/src/cann_call.cpp new file mode 100644 index 0000000000..3b83052ccb --- /dev/null +++ b/modules/cannops/src/cann_call.cpp @@ -0,0 +1,524 @@ +// 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. + +#include +#include +#include "precomp.hpp" +#include "opencv2/core/private.hpp" +namespace cv +{ +namespace cann +{ +/*******************************Acl Error Checker*****************************/ +static inline void checkAclError(aclError err, const char* file, const int line, const char* func) +{ + if (ACL_SUCCESS != err) + { + const char* errMsg = aclGetRecentErrMsg(); + cv::error(cv::Error::StsError, errMsg == nullptr ? "" : errMsg, func, file, line); + } +} + +static inline void checkAclPtr(void* ptr, const char* file, const int line, const char* func) +{ + if (nullptr == ptr) + { + const char* errMsg = aclGetRecentErrMsg(); + cv::error(cv::Error::StsError, errMsg == nullptr ? "" : errMsg, func, file, line); + } +} + +#define CV_ACL_SAFE_CALL(expr) checkAclError((expr), __FILE__, __LINE__, CV_Func) +#define CV_ACL_SAFE_CALL_PTR(expr) \ + ({ \ + auto ptr = (expr); \ + checkAclPtr(ptr, __FILE__, __LINE__, CV_Func); \ + ptr; \ + }) + +/******************************Acl Runtime Warpper****************************/ +void aclrtMallocWarpper(void** data, size_t size) +{ + CV_ACL_SAFE_CALL(aclrtMalloc(data, size, ACL_MEM_MALLOC_HUGE_FIRST)); +} + +void aclrtFreeWarpper(void* data) { CV_ACL_SAFE_CALL(aclrtFree(data)); } + +void aclrtMemcpyWarpper(std::shared_ptr& dst, size_t offset, const void* src, size_t size, + AscendStream& stream) +{ + aclrtStream rawStream = AscendStreamAccessor::getStream(stream); + if (rawStream == nullptr) + CV_ACL_SAFE_CALL( + aclrtMemcpy(dst.get() + offset, size, src, size, ACL_MEMCPY_HOST_TO_DEVICE)); + else + { + CV_ACL_SAFE_CALL(aclrtMemcpyAsync(dst.get() + offset, size, src, size, + ACL_MEMCPY_HOST_TO_DEVICE, rawStream)); + if (offset == 0) + stream.addTensorHolder(dst); + } +} + +void aclrtMemcpyWarpper(void* dst, const std::shared_ptr& src, size_t offset, size_t size, + AscendStream& stream) +{ + aclrtStream rawStream = AscendStreamAccessor::getStream(stream); + if (rawStream == nullptr) + CV_ACL_SAFE_CALL( + aclrtMemcpy(dst, size, src.get() + offset, size, ACL_MEMCPY_DEVICE_TO_HOST)); + else + { + CV_ACL_SAFE_CALL(aclrtMemcpyAsync(dst, size, src.get() + offset, size, + ACL_MEMCPY_DEVICE_TO_HOST, rawStream)); + if (offset == 0) + stream.addTensorHolder(src); + } +} + +void aclrtMemcpyWarpper(std::shared_ptr& dst, size_t dstOffset, + const std::shared_ptr& src, size_t srcOffset, size_t size, + AscendStream& stream) +{ + aclrtStream rawStream = AscendStreamAccessor::getStream(stream); + if (rawStream == nullptr) + CV_ACL_SAFE_CALL(aclrtMemcpy(dst.get() + dstOffset, size, src.get() + srcOffset, size, + ACL_MEMCPY_DEVICE_TO_DEVICE)); + else + { + CV_ACL_SAFE_CALL(aclrtMemcpyAsync(dst.get() + dstOffset, size, src.get() + srcOffset, size, + ACL_MEMCPY_DEVICE_TO_DEVICE, rawStream)); + if (srcOffset == 0) + stream.addTensorHolder(src); + if (dstOffset == 0) + stream.addTensorHolder(dst); + } +} + +void aclrtMemcpy2dWarpper(std::shared_ptr& dst, size_t offset, size_t dpitch, + const void* src, size_t spitch, size_t width, size_t length, + AscendStream& stream) +{ + aclrtStream rawStream = AscendStreamAccessor::getStream(stream); + if (rawStream == nullptr) + CV_ACL_SAFE_CALL(aclrtMemcpy2d(dst.get() + offset, dpitch, src, spitch, width, length, + ACL_MEMCPY_HOST_TO_DEVICE)); + else + { + CV_ACL_SAFE_CALL(aclrtMemcpy2dAsync(dst.get() + offset, dpitch, src, spitch, width, length, + ACL_MEMCPY_HOST_TO_DEVICE, rawStream)); + stream.addTensorHolder(dst); + } +} + +void aclrtMemcpy2dWarpper(void* dst, size_t dpitch, const std::shared_ptr& src, + size_t offset, size_t spitch, size_t width, size_t length, + AscendStream& stream) +{ + aclrtStream rawStream = AscendStreamAccessor::getStream(stream); + if (rawStream == nullptr) + CV_ACL_SAFE_CALL(aclrtMemcpy2d(dst, dpitch, src.get() + offset, spitch, width, length, + ACL_MEMCPY_DEVICE_TO_HOST)); + else + { + CV_ACL_SAFE_CALL(aclrtMemcpy2dAsync(dst, dpitch, src.get() + offset, spitch, width, length, + ACL_MEMCPY_DEVICE_TO_HOST, rawStream)); + stream.addTensorHolder(src); + } +} + +void aclrtMemsetWarpper(std::shared_ptr& ptr, int32_t value, size_t count, + AscendStream& stream) +{ + aclrtStream rawStream = AscendStreamAccessor::getStream(stream); + if (rawStream == nullptr) + CV_ACL_SAFE_CALL(aclrtMemset(ptr.get(), count, value, count)); + else + { + CV_ACL_SAFE_CALL(aclrtMemsetAsync(ptr.get(), count, value, count, rawStream)); + stream.addTensorHolder(ptr); + } +} + +aclDataType getACLType(int opencvdepth) +{ + switch (opencvdepth) + { + case CV_8S: + return ACL_INT8; + case CV_16S: + return ACL_INT16; + case CV_8U: + return ACL_UINT8; + case CV_16U: + return ACL_UINT16; + case CV_32S: + return ACL_INT32; + case CV_32F: + return ACL_FLOAT; + case CV_64F: + return ACL_DOUBLE; + case CV_16F: + return ACL_FLOAT16; + default: + return ACL_DT_UNDEFINED; + } +} + +std::shared_ptr mallocAndUpload(const void* data, size_t size, AscendStream& stream, + AscendMat::Allocator* allocator) +{ + std::shared_ptr ptr = allocator->allocate(size); + aclrtStream rawStream = AscendStreamAccessor::getStream(stream); + + if (rawStream == nullptr) + CV_ACL_SAFE_CALL(aclrtMemcpy(ptr.get(), size, data, size, ACL_MEMCPY_HOST_TO_DEVICE)); + else + CV_ACL_SAFE_CALL( + aclrtMemcpyAsync(ptr.get(), size, data, size, ACL_MEMCPY_HOST_TO_DEVICE, rawStream)); + return ptr; +} + +/**************************Acl attribute preparation**************************/ + +OperatorRunner& OperatorRunner::reset() +{ + holder.clear(); + op.clear(); + for (auto desc : inputDesc_) + { + aclDestroyTensorDesc(desc); + } + for (auto desc : outputDesc_) + { + aclDestroyTensorDesc(desc); + } + for (auto buf : inputBuffers_) + { + CV_ACL_SAFE_CALL(aclDestroyDataBuffer(buf)); + } + for (auto buf : outputBuffers_) + { + CV_ACL_SAFE_CALL(aclDestroyDataBuffer(buf)); + } + if (opAttrInit) + aclopDestroyAttr(opAttr_); + inputDesc_.clear(); + outputDesc_.clear(); + inputBuffers_.clear(); + outputBuffers_.clear(); + opAttrInit = false; + return *this; +} + +OperatorRunner& OperatorRunner::setOp(const char* opName) +{ + reset(); + opAttr_ = CV_ACL_SAFE_CALL_PTR(aclopCreateAttr()); + opAttrInit = true; + op = std::string(opName); + return *this; +} + +OperatorRunner& OperatorRunner::addAttr(float value, const char* name) +{ + CV_ACL_SAFE_CALL(aclopSetAttrFloat(opAttr_, name, value)); + return *this; +} + +OperatorRunner& OperatorRunner::addAttr(const char* value, const char* name) +{ + CV_ACL_SAFE_CALL(aclopSetAttrString(opAttr_, name, value)); + return *this; +} + +OperatorRunner& OperatorRunner::addAttr(int value, const char* name) +{ + CV_ACL_SAFE_CALL(aclopSetAttrInt(opAttr_, name, value)); + return *this; +} + +OperatorRunner& OperatorRunner::addAttr(bool value, const char* name) +{ + CV_ACL_SAFE_CALL(aclopSetAttrBool(opAttr_, name, value)); + return *this; +} + +OperatorRunner& OperatorRunner::addAttr(const int64_t* value, int size, const char* name) +{ + CV_ACL_SAFE_CALL(aclopSetAttrListInt(opAttr_, name, size, value)); + return *this; +} + +OperatorRunner& OperatorRunner::addInput(AscendTensor& tensor) +{ + auto descPtr = CV_ACL_SAFE_CALL_PTR( + aclCreateTensorDesc(tensor.dtype, tensor.dims.size(), &tensor.dims[0], tensor.format)); + if (descPtr != nullptr) + { + if (tensor.name != nullptr && strlen(tensor.name) != 0) + aclSetTensorDescName(descPtr, tensor.name); + inputDesc_.push_back(descPtr); + } + auto bufPtr = CV_ACL_SAFE_CALL_PTR(aclCreateDataBuffer(tensor.data.get(), tensor.dataSize)); + if (bufPtr != nullptr) + inputBuffers_.push_back(bufPtr); + holder.insert(tensor.data); + return *this; +} + +OperatorRunner& OperatorRunner::addOutput(AscendTensor& tensor) +{ + auto descPtr = CV_ACL_SAFE_CALL_PTR( + aclCreateTensorDesc(tensor.dtype, tensor.dims.size(), &tensor.dims[0], tensor.format)); + if (descPtr != nullptr) + { + if (tensor.name != nullptr && strlen(tensor.name) != 0) + aclSetTensorDescName(descPtr, tensor.name); + outputDesc_.push_back(descPtr); + } + auto bufPtr = CV_ACL_SAFE_CALL_PTR(aclCreateDataBuffer(tensor.data.get(), tensor.dataSize)); + if (bufPtr != nullptr) + outputBuffers_.push_back(bufPtr); + holder.insert(tensor.data); + return *this; +} + +OperatorRunner& OperatorRunner::addInput(const AscendMat& mat, const char* name) +{ + AscendTensor tensor(mat, name); + return addInput(tensor); +} + +OperatorRunner& OperatorRunner::addOutput(AscendMat& mat, const char* name) +{ + AscendTensor tensor(mat, name); + return addOutput(tensor); +} + +OperatorRunner& OperatorRunner::addInput(const Scalar& sc, int type, const char* name) +{ + uchar rawData[32]; + cv::scalarToRawData(sc, rawData, type, 0); + std::shared_ptr scPtr = mallocAndUpload( + rawData, (CV_ELEM_SIZE(type)), AscendStream::Null(), AscendMat::defaultAllocator()); + + int64_t dims[] = {1, 1, 1, (CV_MAT_CN(type))}; + AscendTensor tensor(scPtr, (CV_ELEM_SIZE(type)), dims, sizeof(dims) / sizeof(dims[0]), + getACLType(CV_MAT_DEPTH(type)), name); + return addInput(tensor); +} + +OperatorRunner& OperatorRunner::run(AscendStream& stream) +{ + aclrtStream rawStream = AscendStreamAccessor::getStream(stream); + CV_ACL_SAFE_CALL(aclopCompileAndExecute(op.c_str(), inputDesc_.size(), inputDesc_.data(), + inputBuffers_.data(), outputDesc_.size(), + outputDesc_.data(), outputBuffers_.data(), opAttr_, + ACL_ENGINE_SYS, ACL_COMPILE_SYS, NULL, rawStream)); + if (rawStream == nullptr) + CV_ACL_SAFE_CALL(aclrtSynchronizeStream(rawStream)); + else + { + for (const auto& ptr : holder) + stream.addTensorHolder(ptr); + } + return *this; +} + +/********************************Ascend Tensor********************************/ + +AscendTensor::AscendTensor(std::shared_ptr _data, size_t _dataSize, int64_t* _dims, + size_t _dimSize, aclDataType _dtype, const char* _name, + aclFormat _format) + : name(_name), data(_data), dataSize(_dataSize), dtype(_dtype), format(_format) +{ + dims.assign(_dims, _dims + _dimSize); +} + +AscendTensor::AscendTensor(const AscendMat& ascendMat, const char* _name, aclFormat _format) + : name(_name), format(_format) +{ + data = ascendMat.data; + // Ascend can't process with gaps in matrix. + CV_Assert(ascendMat.isContinuous()); + dataSize = ascendMat.rows * ascendMat.cols * ascendMat.elemSize(); + + switch (_format) + { + case ACL_FORMAT_NHWC: + case ACL_FORMAT_ND: + dims.resize(4); + // Batch, default = 1. + dims[0] = 1; + // Default OpenCV image format = NHWC. + dims[1] = ascendMat.rows; + dims[2] = ascendMat.cols; + dims[3] = ascendMat.channels(); + break; + case ACL_FORMAT_NCHW: + dims.resize(4); + dims[0] = 1; + dims[1] = ascendMat.channels(); + dims[2] = ascendMat.rows; + dims[3] = ascendMat.cols; + break; + default: + CV_Error(Error::StsBadArg, "Unknown/unsupported matrix format"); + } + + dtype = getACLType(ascendMat.depth()); +} + +/**********************************Device*************************************/ +void setDevice(int device_id) +{ + aclrtContext context; + CV_ACL_SAFE_CALL(aclrtSetDevice(device_id)); + CV_ACL_SAFE_CALL(aclrtCreateContext(&context, device_id)); +} + +void resetDevice() { CV_ACL_SAFE_CALL(aclrtResetDevice(getDevice())); } + +int32_t getDevice() +{ + int32_t deviceId; + CV_ACL_SAFE_CALL(aclrtGetDevice(&deviceId)); + return deviceId; +} + +void initAcl() { CV_ACL_SAFE_CALL(aclInit(nullptr)); } + +void finalizeAcl() { CV_ACL_SAFE_CALL(aclFinalize()); } + +class DefaultDeviceInitializer +{ +public: + DefaultDeviceInitializer(); + ~DefaultDeviceInitializer(); + + AscendStream& getNullAscendStream(int deviceId); + +private: + std::vector> streams_; + Mutex streams_mtx_; +}; + +DefaultDeviceInitializer::DefaultDeviceInitializer() {} + +DefaultDeviceInitializer::~DefaultDeviceInitializer() { streams_.clear(); } + +AscendStream& DefaultDeviceInitializer::getNullAscendStream(int deviceId) +{ + AutoLock lock(streams_mtx_); + + if (streams_.empty()) + { + uint32_t deviceCount; + CV_ACL_SAFE_CALL(aclrtGetDeviceCount(&deviceCount)); + + if (deviceCount > 0) + streams_.resize(deviceCount); + } + + CV_DbgAssert(deviceId >= 0 && deviceId < static_cast(streams_.size())); + + if (streams_[deviceId].empty()) + { + aclrtStream stream = nullptr; + Ptr impl = makePtr(stream); + streams_[deviceId] = Ptr(new AscendStream(impl)); + } + + return *streams_[deviceId]; +} + +DefaultDeviceInitializer initializer; + +/***********************************Event*************************************/ +AscendEvent::Impl::Impl() : event(nullptr), ownEvent(true) +{ + CV_ACL_SAFE_CALL(aclrtCreateEvent(&event)); +} + +AscendEvent::Impl::Impl(aclrtEvent e) : event(e), ownEvent(false) {} + +AscendEvent::Impl::~Impl() +{ + if (event && ownEvent) + { + CV_ACL_SAFE_CALL(aclrtDestroyEvent(event)); + } +} + +aclrtEvent AscendEventAccessor::getEvent(const AscendEvent& event) { return event.impl_->event; } + +AscendEvent AscendEventAccessor::wrapEvent(aclrtEvent event) +{ + return AscendEvent(makePtr(event)); +} + +AscendEvent::AscendEvent() { impl_ = makePtr(); } + +void AscendEvent::record(AscendStream& stream) +{ + CV_ACL_SAFE_CALL(aclrtRecordEvent(impl_->event, AscendStreamAccessor::getStream(stream))); +} + +void AscendEvent::waitForComplete() const { CV_ACL_SAFE_CALL(aclrtSynchronizeEvent(impl_->event)); } + +/************************************Stream***********************************/ +void AscendStream::Impl::AddTensorHolder(const std::shared_ptr& tensorData) +{ + tensorHolders.insert(tensorData); +} + +AscendStream::Impl::Impl() : stream(nullptr), ownStream(true) +{ + CV_ACL_SAFE_CALL(aclrtCreateStream(&stream)); +} + +AscendStream::Impl::Impl(aclrtStream s) : stream(s), ownStream(false) {} + +aclrtStream AscendStreamAccessor::getStream(const AscendStream& stream) +{ + return stream.impl_->stream; +} + +AscendStream AscendStreamAccessor::wrapStream(aclrtStream stream) +{ + return AscendStream(makePtr(stream)); +} + +AscendStream wrapStream(size_t AscendStreamAddress) +{ + return AscendStreamAccessor::wrapStream(reinterpret_cast(AscendStreamAddress)); +} + +AscendStream::AscendStream() { impl_ = makePtr(); } + +void AscendStream::waitForCompletion() +{ + CV_ACL_SAFE_CALL(aclrtSynchronizeStream(impl_->stream)); + impl_->tensorHolders.clear(); +} + +void AscendStream::waitAscendEvent(const AscendEvent& event) +{ + CV_ACL_SAFE_CALL(aclrtStreamWaitEvent(impl_->stream, AscendEventAccessor::getEvent(event))); +} + +AscendStream& AscendStream::Null() +{ + const uint32_t deviceId = getDevice(); + return initializer.getNullAscendStream(deviceId); +} + +void AscendStream::addTensorHolder(const std::shared_ptr& holder) +{ + impl_->AddTensorHolder(holder); +} + +} // namespace cann +} // namespace cv diff --git a/modules/cannops/src/color.cpp b/modules/cannops/src/color.cpp new file mode 100644 index 0000000000..f08a785e57 --- /dev/null +++ b/modules/cannops/src/color.cpp @@ -0,0 +1,777 @@ +// 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. + +#include "precomp.hpp" + +namespace cv +{ +namespace cann +{ +// Integer type images will have a loss of accuracy during calculation, so they must be converted to +// float before calculation. +static AscendMat convertTo(const AscendMat& src, int dtype, AscendStream& stream) +{ + AscendMat ret; + if (src.depth() != dtype) + src.convertTo(ret, dtype, stream); + else + ret = src; + return ret; +} + +static void convertBack(const AscendMat& src, AscendMat& dst, AscendStream& stream) +{ + if (src.depth() != dst.depth()) + src.convertTo(dst, stream); +} + +//! Set alpha channel to a Mat. +static void matAlphaSet(AscendMat& mat, int dtype, AscendStream& stream) +{ + if (dtype < 0) + dtype = mat.depth(); + + if (mat.depth() == CV_8U || mat.depth() == CV_16U) + { + size_t size = mat.rows * mat.step; + aclrtMemsetWarpper(mat.data, 255, size, stream); + } + else + { + if (dtype == CV_32F) + mat.setTo(1.0f, stream); + else + mat.setTo((dtype == CV_8U ? (1 << 8) : (1 << 16)) - 1, stream); + } +} + +inline void checkImg(const AscendMat& mat) +{ + int depth = mat.depth(); + CV_Assert(!mat.empty()); + CV_Assert(depth == CV_8U || depth == CV_16U || depth == CV_32F); +} + +inline void cvtBGRtoBGR(const AscendMat& src, AscendMat& dst, int dcn, bool swapBlue, + AscendStream& stream) +{ + checkImg(src); + CV_Assert(src.channels() == 3 || src.channels() == 4); + + AscendMat matChannels[4]; + split(src, matChannels, stream); + + if (swapBlue) + std::swap(matChannels[0], matChannels[2]); + + if (dcn == 4 && src.channels() != 4) + { + AscendMat& alpha = matChannels[3]; + alpha.create(src.rows, src.cols, CV_MAKE_TYPE(src.depth(), 1)); + matAlphaSet(alpha, -1, stream); + } + + merge(matChannels, dcn, dst, stream); +} + +inline void cvtBGRtoBGR(InputArray& _src, OutputArray& _dst, int dcn, bool swapBlue, + AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + cvtBGRtoBGR(src, dst, dcn, swapBlue, stream); + dst.download(_dst, stream); +} + +// TODO duplicated code +static const float B2YF = 0.114f; +static const float G2YF = 0.587f; +static const float R2YF = 0.299f; + +inline void cvtBGRtoGray(const AscendMat& src, AscendMat& dst, int, bool swapBlue, + AscendStream& stream) +{ + checkImg(src); + CV_Assert(src.channels() == 3 || src.channels() == 4); + + float coeffs[] = {B2YF, G2YF, R2YF}; + dst.create(src.rows, src.cols, CV_MAKE_TYPE(src.depth(), 1)); + AscendMat formatedSrc = convertTo(src, CV_32F, stream); + AscendMat formatedDst = convertTo(dst, CV_32F, stream); + + // For RGB + if (swapBlue) + std::swap(coeffs[0], coeffs[2]); + + Scalar sc = {coeffs[0], coeffs[1], coeffs[2], 0}; + AscendMat grayRet(formatedSrc.rows, formatedSrc.cols, formatedSrc.type()); + arithm_op(formatedSrc, sc, grayRet, "Mul", stream); + + AscendMat matChannels[4]; + split(grayRet, matChannels, stream); + + OperatorRunner runner; + runner.setOp("AddN") + .addInput(matChannels[0], "x0") + .addInput(matChannels[1], "x1") + .addInput(matChannels[2], "x2") + .addOutput(formatedDst, "y") + .addAttr(3, "N") + .run(stream); + + convertBack(formatedDst, dst, stream); +} + +inline void cvtBGRtoGray(const InputArray& _src, OutputArray& _dst, int, bool swapBlue, + AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + cvtBGRtoGray(src, dst, 0, swapBlue, stream); + dst.download(_dst, stream); +} + +inline void cvtGraytoBGR(const AscendMat& src, AscendMat& dst, int dcn, bool, AscendStream& stream) +{ + checkImg(src); + CV_Assert(src.channels() == 1); + + AscendMat matChannels[4]; + for (int i = 0; i < 3; i++) + matChannels[i] = src; + + if (dcn == 4) + { + AscendMat& alpha = matChannels[3]; + alpha.create(src.rows, src.cols, CV_MAKE_TYPE(src.depth(), 1)); + matAlphaSet(alpha, -1, stream); + } + + merge(matChannels, dcn, dst, stream); +} + +inline void cvtGraytoBGR(const InputArray& _src, OutputArray& _dst, int dcn, bool, + AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + cvtGraytoBGR(src, dst, dcn, false, stream); + dst.download(_dst, stream); +} + +static const float RGB2XYZ_D65[] = {0.412453, 0.357580, 0.180423, 0.212671, 0.715160, + 0.072169, 0.019334, 0.119193, 0.950227}; + +static const float XYZ2RGB_D65[] = {3.240479, -1.53715, -0.498535, -0.969256, 1.875991, + 0.041556, 0.055648, -0.204043, 1.057311}; + +inline void matMulRGB(const AscendMat& src, AscendMat& dst, float* matrix, AscendStream& stream) +{ + checkImg(src); + CV_Assert(src.channels() == 3); + + dst.create(src.rows, src.cols, src.type()); + AscendMat formatedSrc = convertTo(src, CV_32F, stream); + AscendMat formatedDst = convertTo(dst, CV_32F, stream); + + int64_t dims[] = {3, 3}; + OperatorRunner runner; + runner.setOp("BatchMatMulV2") + .addInput(formatedSrc, "x1") + .addInput(matrix, dims, 2, getACLType(CV_32F), "x2") + .addOutput(formatedDst, "y") + .addAttr(false, "adj_x1") + .addAttr(true, "adj_x2") + .run(stream); + + if (src.depth() != CV_32F) + { + AscendMat thresholdTempMat(formatedSrc.size(), formatedSrc.type()); + uint16_t thresh = (src.depth() == CV_8U ? (1 << 8) : (1 << 16)) - 1; + threshold(formatedDst, thresholdTempMat, thresh, 0, 2 /*THRESH_TRUNC*/, stream); + threshold(thresholdTempMat, formatedDst, 0, 0, 3 /*THRESH_TOZERO*/, stream); + } + + convertBack(formatedDst, dst, stream); +} + +// TODO: should deal with overflow. set 255 instead of cut off. +inline void cvtBGRtoXYZ(const AscendMat& src, AscendMat& dst, int, bool swapBlue, + AscendStream& stream) +{ + float coeffs[9]; + memcpy(coeffs, RGB2XYZ_D65, 9 * sizeof(float)); + if (!swapBlue) + { + std::swap(coeffs[0], coeffs[2]); + std::swap(coeffs[3], coeffs[5]); + std::swap(coeffs[6], coeffs[8]); + } + matMulRGB(src, dst, coeffs, stream); +} + +inline void cvtBGRtoXYZ(const InputArray& _src, OutputArray& _dst, int, bool swapBlue, + AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + cvtBGRtoXYZ(src, dst, 0, swapBlue, stream); + dst.download(_dst, stream); +} + +inline void cvtXYZtoBGR(const AscendMat& src, AscendMat& dst, int dcn, bool swapBlue, + AscendStream& stream) +{ + float coeffs[9]; + memcpy(coeffs, XYZ2RGB_D65, 9 * sizeof(float)); + if (!swapBlue) + { + std::swap(coeffs[0], coeffs[6]); + std::swap(coeffs[1], coeffs[7]); + std::swap(coeffs[2], coeffs[8]); + } + + if (dcn == 4) + { + AscendMat tempMat[2]; + matMulRGB(src, tempMat[0], coeffs, stream); + tempMat[1].create(tempMat[0].rows, tempMat[0].cols, CV_MAKE_TYPE(tempMat[0].depth(), 1)); + matAlphaSet(tempMat[1], -1, stream); + merge(tempMat, 2, dst, stream); + } + else + matMulRGB(src, dst, coeffs, stream); +} + +inline void cvtXYZtoBGR(const InputArray& _src, OutputArray& _dst, int dcn, bool swapBlue, + AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + cvtXYZtoBGR(src, dst, dcn, swapBlue, stream); + dst.download(_dst, stream); +} + +// TODO duplicated code +static const float YCRF = 0.713f; +static const float YCBF = 0.564f; +static const float R2VF = 0.877f; +static const float B2UF = 0.492f; +inline void cvtBGRtoYCrCb(const AscendMat& src, AscendMat& dst, float* coeffs, bool swapBlue, + bool yuvOrder, AscendStream& stream) +{ + checkImg(src); + CV_Assert(src.channels() == 3); + + int buleIdx = swapBlue ? 2 : 0; + int depth = src.depth(); + float delta = (depth == CV_8U) ? 128 : ((depth == CV_16U) ? 32768 : 0.5); + + dst.create(src.rows, src.cols, src.type()); + AscendMat formatedSrc = convertTo(src, CV_32F, stream); + AscendMat formatedDst = convertTo(dst, CV_32F, stream); + + AscendMat YCrCb[3], RGB[3]; + split(formatedSrc, RGB, stream); + cvtBGRtoGray(formatedSrc, YCrCb[0], 1, swapBlue, stream); + YCrCb[1].create(YCrCb[0].rows, YCrCb[0].cols, YCrCb[0].type()); + YCrCb[2].create(YCrCb[0].rows, YCrCb[0].cols, YCrCb[0].type()); + + AscendMat tempMat1(formatedSrc.size(), CV_MAKE_TYPE(formatedSrc.depth(), 1)), + tempMat2(formatedSrc.size(), CV_MAKE_TYPE(formatedSrc.depth(), 1)); + + arithm_op(RGB[buleIdx ^ 2], YCrCb[0], tempMat1, "Sub", stream); + arithm_op(tempMat1, coeffs[0], tempMat2, "Muls", stream); + arithm_op(tempMat2, delta, YCrCb[1], "Adds", stream); + + arithm_op(RGB[buleIdx], YCrCb[0], tempMat1, "Sub", stream); + arithm_op(tempMat1, coeffs[1], tempMat2, "Muls", stream); + arithm_op(tempMat2, delta, YCrCb[2], "Adds", stream); + + if (yuvOrder) + std::swap(YCrCb[1], YCrCb[2]); + + merge(YCrCb, 3, formatedDst, stream); + if (src.depth() != CV_32F) + { + AscendMat thresholdTempMat(formatedSrc.size(), formatedSrc.type()); + uint16_t thresh = (src.depth() == CV_8U ? (1 << 8) : (1 << 16)) - 1; + threshold(formatedDst, thresholdTempMat, thresh, 0, 2 /*THRESH_TRUNC*/, stream); + threshold(thresholdTempMat, formatedDst, 0, 0, 3 /*THRESH_TOZERO*/, stream); + } + + convertBack(formatedDst, dst, stream); +} + +inline void cvtBGRtoYCrCb(const InputArray& _src, OutputArray& _dst, float* coeffs, bool swapBlue, + bool yuvOrder, AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + cvtBGRtoYCrCb(src, dst, coeffs, swapBlue, yuvOrder, stream); + dst.download(_dst, stream); +} + +static const float CR2RF = 1.403f; +static const float CR2GF = -0.714f; +static const float CB2GF = -0.344f; +static const float CB2BF = 1.773f; + +static const float V2RF = 1.140f; +static const float V2GF = -0.581f; +static const float U2GF = -0.395f; +static const float U2BF = 2.032f; + +inline void cvtYCrCbtoBGR(const AscendMat& src, AscendMat& dst, int dcn, float* coeffs, + bool swapBlue, bool yuvOrder, AscendStream& stream) +{ + checkImg(src); + CV_Assert(src.channels() == 3); + + int buleIdx = swapBlue ? 2 : 0; + int depth = src.depth(); + float delta = (depth == CV_8U) ? 128 : ((depth == CV_16U) ? 32768 : 0.5); + + dst.create(src.rows, src.cols, CV_MAKE_TYPE(src.depth(), dcn)); + AscendMat formatedSrc = convertTo(src, CV_32F, stream); + AscendMat formatedDst = convertTo(dst, CV_32F, stream); + + AscendMat YCrCb[3], RGB[4]; + split(formatedSrc, YCrCb, stream); + if (yuvOrder) + std::swap(YCrCb[1], YCrCb[2]); + + RGB[0].create(formatedSrc.rows, formatedSrc.cols, CV_MAKE_TYPE(formatedSrc.depth(), 1)); + RGB[1].create(formatedSrc.rows, formatedSrc.cols, CV_MAKE_TYPE(formatedSrc.depth(), 1)); + RGB[2].create(formatedSrc.rows, formatedSrc.cols, CV_MAKE_TYPE(formatedSrc.depth(), 1)); + AscendMat tempMat1(formatedSrc.size(), CV_MAKE_TYPE(formatedSrc.depth(), 1)), + tempMat2(formatedSrc.size(), CV_MAKE_TYPE(formatedSrc.depth(), 1)), + CbSubDelta(formatedSrc.size(), CV_MAKE_TYPE(formatedSrc.depth(), 1)), + CrSubDelta(formatedSrc.size(), CV_MAKE_TYPE(formatedSrc.depth(), 1)); + + arithm_op(YCrCb[1], (0.0f - delta), CrSubDelta, "Adds", stream); + arithm_op(YCrCb[2], (0.0f - delta), CbSubDelta, "Adds", stream); + arithm_op(CrSubDelta, coeffs[0], tempMat1, "Muls", stream); + arithm_op(YCrCb[0], tempMat1, RGB[buleIdx ^ 2], "Add", stream); + + arithm_op(CrSubDelta, coeffs[1], tempMat1, "Muls", stream); + arithm_op(YCrCb[0], tempMat1, tempMat2, "Add", stream); + arithm_op(CbSubDelta, coeffs[2], tempMat1, "Muls", stream); + arithm_op(tempMat2, tempMat1, RGB[1], "Add", stream); + + arithm_op(CbSubDelta, coeffs[3], tempMat1, "Muls", stream); + arithm_op(YCrCb[0], tempMat1, RGB[buleIdx], "Add", stream); + + if (dcn == 4) + { + RGB[3].create(RGB[0].rows, RGB[0].cols, RGB[0].type()); + matAlphaSet(RGB[3], src.depth(), stream); + } + + merge(RGB, dcn, formatedDst, stream); + if (src.depth() != CV_32F) + { + AscendMat thresholdTempMat(formatedSrc.size(), CV_MAKE_TYPE(formatedSrc.depth(), dcn)); + uint16_t thresh = (src.depth() == CV_8U ? (1 << 8) : (1 << 16)) - 1; + threshold(formatedDst, thresholdTempMat, thresh, 0, 2 /*THRESH_TRUNC*/, stream); + threshold(thresholdTempMat, formatedDst, 0, 0, 3 /*THRESH_TOZERO*/, stream); + } + + convertBack(formatedDst, dst, stream); +} + +inline void cvtYCrCbtoBGR(const InputArray& _src, OutputArray& _dst, int dcn, float* coeffs, + bool swapBlue, bool yuvOrder, AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + cvtYCrCbtoBGR(src, dst, dcn, coeffs, swapBlue, yuvOrder, stream); + dst.download(_dst, stream); +} + +// The input may be Input/OutputArray or AscendMat. Use templates to reduce duplicate code. +template +inline void BGR2BGRA(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoBGR(src, dst, 4, false, stream); +} + +template +inline void BGRA2BGR(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoBGR(src, dst, 3, false, stream); +} + +template +inline void BGR2RGBA(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoBGR(src, dst, 4, true, stream); +} + +template +inline void RGBA2BGR(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoBGR(src, dst, 3, true, stream); +} + +template +inline void BGR2RGB(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoBGR(src, dst, 3, true, stream); +} + +template +inline void BGRA2RGBA(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoBGR(src, dst, 4, true, stream); +} + +template +inline void BGR2GRAY(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoGray(src, dst, 1, false, stream); +} + +template +inline void RGB2GRAY(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoGray(src, dst, 1, true, stream); +} + +template +inline void GRAY2BGR(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtGraytoBGR(src, dst, 3, false, stream); +} + +template +inline void GRAY2BGRA(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtGraytoBGR(src, dst, 4, false, stream); +} + +template +inline void BGRA2GRAY(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoGray(src, dst, 1, false, stream); +} + +template +inline void RGBA2GRAY(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoGray(src, dst, 1, true, stream); +} + +template +inline void BGR2XYZ(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoXYZ(src, dst, 3, false, stream); +} + +template +inline void RGB2XYZ(const SRC& src, DST& dst, int, AscendStream& stream) +{ + cvtBGRtoXYZ(src, dst, 3, true, stream); +} + +template +inline void XYZ2BGR(const SRC& src, DST& dst, int dcn, AscendStream& stream) +{ + if (dcn <= 0) + dcn = 3; + cvtXYZtoBGR(src, dst, dcn, false, stream); +} + +template +inline void XYZ2RGB(const SRC& src, DST& dst, int dcn, AscendStream& stream) +{ + if (dcn <= 0) + dcn = 3; + cvtXYZtoBGR(src, dst, dcn, true, stream); +} + +template +inline void BGR2YCrCb(const SRC& src, DST& dst, int, AscendStream& stream) +{ + float coeffs[2]; + coeffs[0] = YCRF; + coeffs[1] = YCBF; + cvtBGRtoYCrCb(src, dst, coeffs, false, false, stream); +} + +template +inline void RGB2YCrCb(const SRC& src, DST& dst, int, AscendStream& stream) +{ + float coeffs[2]; + coeffs[0] = YCRF; + coeffs[1] = YCBF; + cvtBGRtoYCrCb(src, dst, coeffs, true, false, stream); +} + +template +inline void YCrCb2BGR(const SRC& src, DST& dst, int dcn, AscendStream& stream) +{ + float coeffs[4]; + coeffs[0] = CR2RF; + coeffs[1] = CR2GF; + coeffs[2] = CB2GF; + coeffs[3] = CB2BF; + if (dcn <= 0) + dcn = 3; + cvtYCrCbtoBGR(src, dst, dcn, coeffs, false, false, stream); +} + +template +inline void YCrCb2RGB(const SRC& src, DST& dst, int dcn, AscendStream& stream) +{ + float coeffs[4]; + coeffs[0] = CR2RF; + coeffs[1] = CR2GF; + coeffs[2] = CB2GF; + coeffs[3] = CB2BF; + if (dcn <= 0) + dcn = 3; + cvtYCrCbtoBGR(src, dst, dcn, coeffs, true, false, stream); +} + +template +inline void BGR2YUV(const SRC& src, DST& dst, int, AscendStream& stream) +{ + float coeffs[2]; + coeffs[0] = R2VF; + coeffs[1] = B2UF; + cvtBGRtoYCrCb(src, dst, coeffs, false, true, stream); +} + +template +inline void RGB2YUV(const SRC& src, DST& dst, int, AscendStream& stream) +{ + float coeffs[2]; + coeffs[0] = R2VF; + coeffs[1] = B2UF; + cvtBGRtoYCrCb(src, dst, coeffs, true, true, stream); +} + +template +inline void YUV2BGR(const SRC& src, DST& dst, int dcn, AscendStream& stream) +{ + float coeffs[4]; + coeffs[0] = V2RF; + coeffs[1] = V2GF; + coeffs[2] = U2GF; + coeffs[3] = U2BF; + if (dcn <= 0) + dcn = 3; + cvtYCrCbtoBGR(src, dst, dcn, coeffs, false, true, stream); +} + +template +inline void YUV2RGB(const SRC& src, DST& dst, int dcn, AscendStream& stream) +{ + float coeffs[4]; + coeffs[0] = V2RF; + coeffs[1] = V2GF; + coeffs[2] = U2GF; + coeffs[3] = U2BF; + if (dcn <= 0) + dcn = 3; + cvtYCrCbtoBGR(src, dst, dcn, coeffs, true, true, stream); +} + +template +void cvtColorDo(const SRC& src, DST& dst, int code, int dcn, AscendStream& stream) +{ + typedef void (*func_t)(const SRC& src, DST& dst, int dcn, AscendStream& stream); + static const func_t funcs[] = { + BGR2BGRA, // CV_BGR2BGRA =0 + BGRA2BGR, // CV_BGRA2BGR =1 + BGR2RGBA, // CV_BGR2RGBA =2 + RGBA2BGR, // CV_RGBA2BGR =3 + BGR2RGB, // CV_BGR2RGB =4 + BGRA2RGBA, // CV_BGRA2RGBA =5 + + BGR2GRAY, // CV_BGR2GRAY =6 + RGB2GRAY, // CV_RGB2GRAY =7 + GRAY2BGR, // CV_GRAY2BGR =8 + GRAY2BGRA, // CV_GRAY2BGRA =9 + BGRA2GRAY, // CV_BGRA2GRAY =10 + RGBA2GRAY, // CV_RGBA2GRAY =11 + + 0, // CV_BGR2BGR565 =12 + 0, // CV_RGB2BGR565 =13 + 0, // CV_BGR5652BGR =14 + 0, // CV_BGR5652RGB =15 + 0, // CV_BGRA2BGR565 =16 + 0, // CV_RGBA2BGR565 =17 + 0, // CV_BGR5652BGRA =18 + 0, // CV_BGR5652RGBA =19 + + 0, // CV_GRAY2BGR565 =20 + 0, // CV_BGR5652GRAY =21 + + 0, // CV_BGR2BGR555 =22 + 0, // CV_RGB2BGR555 =23 + 0, // CV_BGR5552BGR =24 + 0, // CV_BGR5552RGB =25 + 0, // CV_BGRA2BGR555 =26 + 0, // CV_RGBA2BGR555 =27 + 0, // CV_BGR5552BGRA =28 + 0, // CV_BGR5552RGBA =29 + + 0, // CV_GRAY2BGR555 =30 + 0, // CV_BGR5552GRAY =31 + + BGR2XYZ, // CV_BGR2XYZ =32 + RGB2XYZ, // CV_RGB2XYZ =33 + XYZ2BGR, // CV_XYZ2BGR =34 + XYZ2RGB, // CV_XYZ2RGB =35 + + BGR2YCrCb, // CV_BGR2YCrCb =36 + RGB2YCrCb, // CV_RGB2YCrCb =37 + YCrCb2BGR, // CV_YCrCb2BGR =38 + YCrCb2RGB, // CV_YCrCb2RGB =39 + + 0, // CV_BGR2HSV =40 + 0, // CV_RGB2HSV =41 + + 0, // =42 + 0, // =43 + + 0, // CV_BGR2Lab =44 + 0, // CV_RGB2Lab =45 + + 0, // CV_BayerBG2BGR =46 + 0, // CV_BayeRGB2BGR =47 + 0, // CV_BayerRG2BGR =48 + 0, // CV_BayerGR2BGR =49 + + 0, // CV_BGR2Luv =50 + 0, // CV_RGB2Luv =51 + + 0, // CV_BGR2HLS =52 + 0, // CV_RGB2HLS =53 + + 0, // CV_HSV2BGR =54 + 0, // CV_HSV2RGB =55 + + 0, // CV_Lab2BGR =56 + 0, // CV_Lab2RGB =57 + 0, // CV_Luv2BGR =58 + 0, // CV_Luv2RGB =59 + + 0, // CV_HLS2BGR =60 + 0, // CV_HLS2RGB =61 + + 0, // CV_BayerBG2BGR_VNG =62 + 0, // CV_BayeRGB2BGR_VNG =63 + 0, // CV_BayerRG2BGR_VNG =64 + 0, // CV_BayerGR2BGR_VNG =65 + + 0, // CV_BGR2HSV_FULL = 66 + 0, // CV_RGB2HSV_FULL = 67 + 0, // CV_BGR2HLS_FULL = 68 + 0, // CV_RGB2HLS_FULL = 69 + + 0, // CV_HSV2BGR_FULL = 70 + 0, // CV_HSV2RGB_FULL = 71 + 0, // CV_HLS2BGR_FULL = 72 + 0, // CV_HLS2RGB_FULL = 73 + + 0, // CV_LBGR2Lab = 74 + 0, // CV_LRGB2Lab = 75 + 0, // CV_LBGR2Luv = 76 + 0, // CV_LRGB2Luv = 77 + + 0, // CV_Lab2LBGR = 78 + 0, // CV_Lab2LRGB = 79 + 0, // CV_Luv2LBGR = 80 + 0, // CV_Luv2LRGB = 81 + + BGR2YUV, // CV_BGR2YUV = 82 + RGB2YUV, // CV_RGB2YUV = 83 + YUV2BGR, // CV_YUV2BGR = 84 + YUV2RGB, // CV_YUV2RGB = 85 + + 0, // CV_BayerBG2GRAY = 86 + 0, // CV_BayeRGB2GRAY = 87 + 0, // CV_BayerRG2GRAY = 88 + 0, // CV_BayerGR2GRAY = 89 + + // YUV 4:2:0 formats family + 0, // CV_YUV2RGB_NV12 = 90, + 0, // CV_YUV2BGR_NV12 = 91, + 0, // CV_YUV2RGB_NV21 = 92, + 0, // CV_YUV2BGR_NV21 = 93, + + 0, // CV_YUV2RGBA_NV12 = 94, + 0, // CV_YUV2BGRA_NV12 = 95, + 0, // CV_YUV2RGBA_NV21 = 96, + 0, // CV_YUV2BGRA_NV21 = 97, + + 0, // CV_YUV2RGB_YV12 = 98, + 0, // CV_YUV2BGR_YV12 = 99, + 0, // CV_YUV2RGB_IYUV = 100, + 0, // CV_YUV2BGR_IYUV = 101, + + 0, // CV_YUV2RGBA_YV12 = 102, + 0, // CV_YUV2BGRA_YV12 = 103, + 0, // CV_YUV2RGBA_IYUV = 104, + 0, // CV_YUV2BGRA_IYUV = 105, + + 0, // CV_YUV2GRAY_420 = 106, + + // YUV 4:2:2 formats family + 0, // CV_YUV2RGB_UYVY = 107, + 0, // CV_YUV2BGR_UYVY = 108, + 0, // //CV_YUV2RGB_VYUY = 109, + 0, // //CV_YUV2BGR_VYUY = 110, + + 0, // CV_YUV2RGBA_UYVY = 111, + 0, // CV_YUV2BGRA_UYVY = 112, + 0, // //CV_YUV2RGBA_VYUY = 113, + 0, // //CV_YUV2BGRA_VYUY = 114, + + 0, // CV_YUV2RGB_YUY2 = 115, + 0, // CV_YUV2BGR_YUY2 = 116, + 0, // CV_YUV2RGB_YVYU = 117, + 0, // CV_YUV2BGR_YVYU = 118, + + 0, // CV_YUV2RGBA_YUY2 = 119, + 0, // CV_YUV2BGRA_YUY2 = 120, + 0, // CV_YUV2RGBA_YVYU = 121, + 0, // CV_YUV2BGRA_YVYU = 122, + + 0, // CV_YUV2GRAY_UYVY = 123, + 0, // CV_YUV2GRAY_YUY2 = 124, + + // alpha premultiplication + 0, // CV_RGBA2mRGBA = 125, + 0, // CV_mRGBA2RGBA = 126, + + 0, // CV_COLORCVT_MAX = 127 + }; + + CV_Assert(code < 128); + + func_t func = funcs[code]; + + if (func == 0) + CV_Error(Error::StsBadFlag, "Unknown/unsupported color conversion code"); + + func(src, dst, dcn, stream); +} + +// Instantiate templates to avoid confusion in python code generation +void cvtColor(const InputArray src, OutputArray dst, int code, int dcn, AscendStream& stream) +{ + cvtColorDo(src, dst, code, dcn, stream); +} + +void cvtColor(const AscendMat& src, AscendMat& dst, int code, int dcn, AscendStream& stream) +{ + cvtColorDo(src, dst, code, dcn, stream); +} + +} // namespace cann +} // namespace cv \ No newline at end of file diff --git a/modules/cannops/src/core.cpp b/modules/cannops/src/core.cpp new file mode 100644 index 0000000000..7d328915ef --- /dev/null +++ b/modules/cannops/src/core.cpp @@ -0,0 +1,310 @@ +// 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. + +#include "precomp.hpp" + +namespace cv +{ +namespace cann +{ +// Transform data type from one to another. eg. from NCHW to NHWC. +void transData(const AscendMat& src, AscendMat& dst, const char* from, const char* to, + AscendStream& stream) +{ + OperatorRunner runner; + runner.setOp("TransData") + .addInput(src, "src") + .addOutput(dst, "dst") + .addAttr(from, "src_format") + .addAttr(to, "dst_format") + .run(stream); +} + +void merge(const AscendMat* src, size_t n, AscendMat& dst, AscendStream& stream) +{ + if (src == nullptr || n < 2) + return; + + int depth = src->depth(); + int rows = src->rows; + int cols = src->cols; + + // All matrix must have same size and type + for (size_t i = 1; i < n; i++) + { + CV_Assert(src[i].depth() == depth && src[i].channels() == 1); + CV_Assert(src[i].rows == rows && src[i].cols == cols); + } + + int cns = 0; + for (size_t i = 0; i < n; i++) + cns += src[i].channels(); + dst.create(src->rows, src->cols, CV_MAKE_TYPE(src->depth(), cns)); + + OperatorRunner runner; + runner.setOp("ConcatD"); + + for (size_t i = 0; i < n; i++) + { + runner.addInput(src[i], ("x" + std::to_string(i)).c_str()); + } + + runner.addOutput(dst, "output_data").addAttr(3, "concat_dim").run(stream); +} + +void merge(const std::vector& src, AscendMat& dst, AscendStream& stream) +{ + merge(&src[0], src.size(), dst, stream); +} + +void merge(const AscendMat* src, size_t n, OutputArray& _dst, AscendStream& stream) +{ + AscendMat dst; + merge(src, n, dst, stream); + dst.download(_dst, stream); +} +void merge(const std::vector& src, OutputArray& dst, AscendStream& stream) +{ + merge(&src[0], src.size(), dst, stream); +} + +void split(const AscendMat& src, AscendMat* dst, AscendStream& stream) +{ + if (src.empty() || dst == nullptr) + return; + + int cn = src.channels(); + + OperatorRunner runner; + runner.setOp("SplitD").addInput(src, "x"); + for (int i = 0; i < cn; i++) + { + dst[i].create(src.rows, src.cols, CV_MAKE_TYPE(src.depth(), 1)); + runner.addOutput(dst[i], ("y" + std::to_string(i)).c_str()); + } + runner.addAttr(3, "split_dim").addAttr(cn, "num_split").run(stream); +} + +void split(const AscendMat& src, std::vector& dst, AscendStream& stream) +{ + dst.resize(src.channels()); + split(src, &dst[0], stream); +} + +void split(const InputArray _src, AscendMat* dst, AscendStream& stream) +{ + AscendMat src; + src.upload(_src, stream); + split(src, dst, stream); +} +void split(const InputArray _src, std::vector& dst, AscendStream& stream) +{ + AscendMat src; + src.upload(_src, stream); + dst.resize(src.channels()); + split(_src, &dst[0], stream); +} + +void transpose(const AscendMat& src, int64_t* perm, AscendMat& dst, AscendStream& stream) +{ + OperatorRunner runner; + runner.setOp("TransposeD") + .addInput(src, "x") + .addOutput(dst, "y") + .addAttr(perm, 4, "perm") + .run(stream); +} + +void transpose(const AscendMat& src, AscendMat& dst, AscendStream& stream) +{ + int64_t perm[] = {0, 2, 1, 3}; + dst.create(src.cols, src.rows, src.type()); + transpose(src, perm, dst, stream); +} + +void transpose(InputArray _src, OutputArray _dst, AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + transpose(src, dst, stream); + dst.download(_dst, stream); +} + +void flip(const AscendMat& src, std::vector& asixs, AscendMat& dst, AscendStream& stream) +{ + int64_t dim = asixs.size(); + OperatorRunner runner; + runner.setOp("ReverseV2") + .addInput(src, "x") + .addInput(&asixs.at(0), &dim, 1, ACL_INT32, "axis") + .addOutput(dst, "y") + .run(stream); +} + +void flip(const AscendMat& src, AscendMat& dst, int flipCode, AscendStream& stream) +{ + std::vector asix; + if (flipCode == 0) + asix.push_back(1); + else if (flipCode > 0) + asix.push_back(2); + else + { + asix.push_back(1); + asix.push_back(2); + } + dst.create(src.rows, src.cols, src.type()); + flip(src, asix, dst, stream); +} + +void flip(const InputArray _src, OutputArray _dst, int flipCode, AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + flip(src, dst, flipCode, stream); + dst.download(_dst, stream); +} + +void rotate(const AscendMat& src, AscendMat& dst, int rotateMode, AscendStream& stream) +{ + AscendMat tempMat; + switch (rotateMode) + { + case ROTATE_90_CLOCKWISE: + { + dst.create(src.cols, src.rows, src.type()); + transpose(src, tempMat, stream); + flip(tempMat, dst, 1, stream); + break; + } + case ROTATE_180: + { + dst.create(src.rows, src.cols, src.type()); + flip(src, dst, -1, stream); + break; + } + case ROTATE_90_COUNTERCLOCKWISE: + { + dst.create(src.cols, src.rows, src.type()); + transpose(src, tempMat, stream); + flip(tempMat, dst, 0, stream); + break; + } + default: + break; + } +} + +void rotate(InputArray _src, OutputArray _dst, int rotateMode, AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + rotate(src, dst, rotateMode, stream); + dst.download(_dst, stream); +} + +void crop(const AscendMat& src, AscendMat& dst, const AscendMat& sizeSrcNpu, int64_t* offset, + AscendStream& stream) +{ + OperatorRunner runner; + runner.setOp("Crop") + .addInput(src, "x") + .addInput(sizeSrcNpu, "size") + .addAttr(1, "axis") + .addAttr(offset, 3, "offsets") + .addOutput(dst, "y") + .run(stream); +} + +AscendMat crop(const AscendMat& src, const Rect& rect, AscendStream& stream) +{ + AscendMat dst, sizeSrcNpu; + // left-up conner + int x = rect.x, y = rect.y, width = rect.width, height = rect.height; + int64_t offset[] = {y, x, 0}; + + CV_Assert(x + width <= src.cols && y + height <= src.rows); + int size1[] = {1, src.channels(), height, width}; + dst.create(height, width, src.type()); + + Mat sizeSrc(height, width, src.type(), size1); + sizeSrcNpu.upload(sizeSrc); + crop(src, dst, sizeSrcNpu, offset, stream); + + return dst; +} +AscendMat crop(InputArray _src, const Rect& rect, AscendStream& stream) +{ + AscendMat src; + src.upload(_src, stream); + return crop(src, rect, stream); +} + +void resize(const AscendMat& src, AscendMat& dst, int32_t* dstSize, int interpolation, + AscendStream& stream) +{ + OperatorRunner runner; + int64_t dims[] = {2}; + char const* mode = ""; + switch (interpolation) + { + case INTER_CUBIC: + mode = "ResizeBicubic"; + break; + case INTER_AREA: + mode = "ResizeArea"; + break; + default: + break; + } + + runner.setOp(mode) + .addInput(src, "images") + .addInput(dstSize, dims, 1, ACL_INT32, "size") + .addAttr(true, "half_pixel_centers") + .addOutput(dst, "y") + .run(stream); +} + +void resize(const AscendMat& src, AscendMat& dst, Size dsize, double inv_scale_x, + double inv_scale_y, int interpolation, AscendStream& stream) +{ + Size ssize = src.size(); + CV_Assert(!ssize.empty()); + float_t scaleX = (float_t)inv_scale_x; + float_t scaleY = (float_t)inv_scale_y; + CV_Assert(interpolation == INTER_CUBIC || interpolation == INTER_AREA); + + if (dsize.empty()) + { + CV_Assert(scaleX > 0); + CV_Assert(scaleY > 0); + dsize = Size(saturate_cast(ssize.width * inv_scale_x), + saturate_cast(ssize.height * inv_scale_y)); + CV_Assert(!dsize.empty()); + } + else + { + scaleX = (float_t)dsize.width / ssize.width; + scaleY = (float_t)dsize.height / ssize.height; + CV_Assert(scaleX > 0); + CV_Assert(scaleY > 0); + } + + int32_t dstSize[] = {dsize.width, dsize.height}; + dst.create(dstSize[0], dstSize[1], src.type()); + resize(src, dst, dstSize, interpolation, stream); +} + +void resize(InputArray _src, OutputArray _dst, Size dsize, double inv_scale_x, double inv_scale_y, + int interpolation, AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + resize(src, dst, dsize, inv_scale_x, inv_scale_y, interpolation, stream); + dst.download(_dst, stream); +} + +} // namespace cann +} // namespace cv diff --git a/modules/cannops/src/element_operations.cpp b/modules/cannops/src/element_operations.cpp new file mode 100644 index 0000000000..402658369b --- /dev/null +++ b/modules/cannops/src/element_operations.cpp @@ -0,0 +1,499 @@ +// 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. + +#include "precomp.hpp" +namespace cv +{ +namespace cann +{ + +static inline void applyMask(const AscendMat& src, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + int mtype = mask.type(); + CV_Assert((mtype == CV_8UC1 || mtype == CV_8SC1) && mask.size() == src.size()); + AscendMat onesMask, castedMask; + onesMask.create(mask.rows, mask.cols, mask.type()); + + OperatorRunner runner; + runner.setOp("Div") + .addInput(mask, "x1") + .addInput(mask, "x2") + .addOutput(onesMask, "y") + .run(stream); + + onesMask.convertTo(castedMask, dst.depth(), stream); + arithm_op(src, castedMask, dst, "Mul", stream); +} + +static inline void applyScale(const AscendMat& src, AscendMat& dst, float scale, + AscendStream& stream) +{ + OperatorRunner runner; + arithm_op(src, scale, dst, "Muls", stream); +} + +void arithm_op(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const char* op, + AscendStream& stream) +{ + if (src2.empty()) + arithm_op(src1, dst, op, stream); + else + { + OperatorRunner runner; + runner.setOp(op).addInput(src1, "x1").addInput(src2, "x2").addOutput(dst, "y").run(stream); + } +} + +void arithm_op(const AscendMat& src, const Scalar& sc, AscendMat& dst, const char* op, + AscendStream& stream) +{ + OperatorRunner runner; + runner.setOp(op) + .addInput(src, "x1") + .addInput(sc, src.type(), "x2") + .addOutput(dst, "y") + .run(stream); +} + +void arithm_op(const Scalar& sc, const AscendMat& src, AscendMat& dst, const char* op, + AscendStream& stream) +{ + OperatorRunner runner; + runner.setOp(op) + .addInput(sc, src.type(), "x1") + .addInput(src, "x2") + .addOutput(dst, "y") + .run(stream); +} + +void arithm_op(const AscendMat& src, AscendMat& dst, const char* op, AscendStream& stream) +{ + OperatorRunner runner; + runner.setOp(op).addInput(src, "x").addOutput(dst, "y").run(stream); +} + +void arithm_op(const AscendMat& src, float scalar, AscendMat& dst, const char* op, + AscendStream& stream) +{ + OperatorRunner runner; + runner.setOp(op).addInput(src, "x").addAttr(scalar, "value").addOutput(dst, "y").run(stream); +} + +// Helper function for template arithm_op. all function called in template arithm_op should be +// done in both AscendMat and Scalar. +static void getInputInfo(const AscendMat& src, int& depth, int& cn, Size& size) +{ + depth = src.depth(); + cn = src.channels(); + size = src.size(); +} + +static void getInputInfo(const Scalar& src, int& depth, int& cn, Size& size) +{ + CV_UNUSED(src); + depth = -1; + cn = -1; + size = {-1, -1}; +} + +static void convert(const AscendMat& src, AscendMat& dst, AscendStream& stream) +{ + src.convertTo(dst, CV_32F, stream); +} + +static void convert(const Scalar& src, Scalar& dst, AscendStream& stream) +{ + CV_UNUSED(stream); + dst = src; +} + +template +static void arithm_op(const T1& src1, const T2& src2, AscendMat& dst, const AscendMat& mask, float scale, + int dtype, const char* op, AscendStream& stream) +{ + T1 castedSrc1; + T2 castedSrc2; + AscendMat castedRet; + + int sdepth1, sdepth2, scn1, scn2; + Size ssize1, ssize2; + getInputInfo(src1, sdepth1, scn1, ssize1); + getInputInfo(src2, sdepth2, scn2, ssize2); + + int sdepth = sdepth1 == -1 ? sdepth2 : sdepth1; + int cn = scn1 == -1 ? scn2 : scn1; + Size size = sdepth1 == -1 ? ssize2 : ssize1; + + if (sdepth1 != -1 && sdepth2 != -1 && !ssize1.empty() && !ssize2.empty()) + CV_Assert(sdepth1 == sdepth2 && scn1 == scn2 && ssize1 == ssize2); + + if (dtype < 0) + dtype = sdepth; + const int ddepth = CV_MAT_DEPTH(dtype); + CV_Assert(sdepth <= CV_16F && ddepth <= CV_16F); + + dst.create(size.height, size.width, CV_MAKE_TYPE(ddepth, cn)); + + // In order to achieve high accuracy, convert integers to float for calculation. + if (scale != 1 && dtype < CV_32F) + { + convert(src1, castedSrc1, stream); + convert(src2, castedSrc2, stream); + castedRet.create(size.height, size.width, CV_MAKE_TYPE(CV_32F, cn)); + } + else + { + castedSrc1 = src1; + castedSrc2 = src2; + castedRet = dst; + } + + // step1, calculate operator. + OperatorRunner runner; + arithm_op(castedSrc1, castedSrc2, castedRet, op, stream); + + // step2, apply mask if need. + if (!mask.empty()) + applyMask(castedRet, castedRet, mask, stream); + + // step3, apply scale if need. + if (scale != 1) + applyScale(castedRet, castedRet, scale, stream); + + // After rounding the result, convert the type to the original type. + if (castedRet.depth() != dst.depth()) + { + runner.setOp("Round").addInput(castedRet, "x").addOutput(castedRet, "y").run(stream); + castedRet.convertTo(dst, stream); + } +} + +static void arithm_op(const InputArray _src1, const InputArray _src2, OutputArray _dst, const InputArray _mask, + float scale, int dtype, const char* op, AscendStream& stream) +{ + const bool isScalar1 = (_src1.kind() == _InputArray::MATX); + const bool isScalar2 = (_src2.kind() == _InputArray::MATX); + + if (isScalar1 && isScalar2) + CV_Error(Error::StsBadArg, "At list one matrix parameter shoule be passwd."); + + AscendMat src1, src2, dst, mask; + Mat scalar; + + if (!isScalar1 && !_src1.empty()) + src1.upload(_src1, stream); + if (!isScalar2 && !_src2.empty()) + src2.upload(_src2, stream); + + if (!_mask.empty()) + mask.upload(_mask, stream); + + Scalar val; + if (isScalar1) + scalar = _src1.getMat(); + else if (isScalar2) + scalar = _src2.getMat(); + + if (!scalar.empty()) + { + CV_Assert(scalar.total() <= 4); + scalar.convertTo(Mat_(scalar.rows, scalar.cols, &val[0]), CV_64F); + } + + if (isScalar1) + arithm_op(val, src2, dst, mask, scale, dtype, op, stream); + else if (isScalar2) + arithm_op(src1, val, dst, mask, scale, dtype, op, stream); + else + arithm_op(src1, src2, dst, mask, scale, dtype, op, stream); + + dst.download(_dst, stream); +} + +// In order to supply more interfaces, differnet function declaration shoule be done. +void add(const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, dtype, "Add", stream); +} + +void add(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, dtype, "Add", stream); +} + +void add(const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, dtype, "Add", stream); +} + +void add(const Scalar& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, dtype, "Add", stream); +} + + +void subtract(const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, dtype, "Sub", stream); +} + +void subtract(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, dtype, "Sub", stream); +} + +void subtract(const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, dtype, "Sub", stream); +} + +void subtract(const Scalar& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, dtype, "Sub", stream); +} + + +void multiply(const InputArray src1, const InputArray src2, OutputArray dst, float scale, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, noArray(), scale, dtype, "Mul", stream); +} + +void multiply(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, AscendMat(), scale, dtype, "Mul", stream); +} + +void multiply(const AscendMat& src1, const Scalar& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, AscendMat(), scale, dtype, "Mul", stream); +} + +void multiply(const Scalar& src1, const AscendMat& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, AscendMat(), scale, dtype, "Mul", stream); +} + + +void divide(const InputArray src1, const InputArray src2, OutputArray dst, float scale, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, noArray(), scale, dtype, "RealDiv", stream); +} + +void divide(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, AscendMat(), scale, dtype, "RealDiv", stream); +} + +void divide(const AscendMat& src1, const Scalar& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, AscendMat(), scale, dtype, "RealDiv", stream); +} + +void divide(const Scalar& src1, const AscendMat& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, AscendMat(), scale, dtype, "RealDiv", stream); +} + + +void bitwise_and(const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseAnd", stream); +} + +void bitwise_and(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseAnd", stream); +} + +void bitwise_and(const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseAnd", stream); +} + +void bitwise_and(const Scalar& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseAnd", stream); +} + + +void bitwise_or(const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseOr", stream); +} + +void bitwise_or(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseOr", stream); +} + +void bitwise_or(const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseOr", stream); +} + +void bitwise_or(const Scalar& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseOr", stream); +} + + +void bitwise_xor(const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseXor", stream); +} + +void bitwise_xor(const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseXor", stream); +} + +void bitwise_xor(const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseXor", stream); +} + +void bitwise_xor(const Scalar& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) +{ + arithm_op(src1, src2, dst, mask, 1, -1, "BitwiseXor", stream); +} + + +void bitwise_not(const InputArray src, OutputArray dst, const InputArray mask, AscendStream& stream) +{ + arithm_op(src, noArray(), dst, mask, 1, -1, "Invert", stream); +} + +void bitwise_not(const AscendMat& src, AscendMat& dst, const AscendMat& mask, AscendStream& stream) +{ + arithm_op(src, AscendMat(), dst, mask, 1, -1, "Invert", stream); +} + + +void addWeighted(const AscendMat& src1, double alpha, const AscendMat& src2, double beta, double gamma, + AscendMat& dst, int dtype, AscendStream& stream) +{ + if (dtype < 0) + dtype = src1.depth(); + + CV_Assert(src2.depth() == src1.depth() && src2.size() == src1.size() && + src1.channels() == src2.channels()); + + int type = CV_MAKE_TYPE(dtype, src1.channels()); + dst.create(src1.rows, src1.cols, type); + + // TODO: Consider overflow, should extend type or not? + AscendMat src1Weighted(src1.size(), type), src2Weighted(src1.size(), type), + srcWeightedSumRet(src1.size(), type); + + arithm_op(src1, (float)alpha, src1Weighted, "Muls", stream); + arithm_op(src2, (float)beta, src2Weighted, "Muls", stream); + arithm_op(src1Weighted, src2Weighted, srcWeightedSumRet, "Add", stream); + arithm_op(srcWeightedSumRet, (float)gamma, dst, "Adds", stream); +} + +void addWeighted(const InputArray _src1, double alpha, const InputArray _src2, double beta, double gamma, + OutputArray _dst, int dtype, AscendStream& stream) +{ + AscendMat src1, src2, dst; + src1.upload(_src1, stream); + src2.upload(_src2, stream); + addWeighted(src1, alpha, src2, beta, gamma, dst, dtype, stream); + dst.download(_dst, stream); +} + +double threshold(const AscendMat& src, AscendMat& dst, double thresh, double maxval, int type, + AscendStream& stream) +{ + // ThresholdTypes is defined in opencv2/imgproc, This type is the only Symbol we need. + // Add imgproc to dependence is too heavy, use magic number instead. + CV_Assert(type <= 4 /*THRESH_TOZERO_INV*/); + + AscendMat threshMat(src.size(), src.type()); + + dst.create(src.rows, src.cols, src.type()); + + OperatorRunner runner; + runner.setOp("Threshold") + .addInput(src, "x") + .addOutput(threshMat, "y") + .addAttr((float)thresh, "threshold") + .run(stream); + + // THRESH_*_INV, THRESH_TRUNC need a inverse threshMat. + // THRESH_BINARY_INV = 1, THRESH_TRUNC = 2, THRESH_TOZERO_INV = 4, + if (type == 1 || type == 2 || type == 4) + { + AscendMat threshInvMat(src.size(), src.type()); + AscendMat ones(src.size(), src.type()); + Scalar s(1, 1, 1, 1); + ones.setTo(s, stream); + arithm_op(ones, threshMat, threshInvMat, "Sub", stream); + + if (type == 1) + arithm_op(threshInvMat, (float)maxval, dst, "Muls", stream); + else if (type == 2) + { + AscendMat ToZeroInvMat(src.size(), src.type()); + AscendMat TruncMat(src.size(), src.type()); + arithm_op(threshInvMat, src, ToZeroInvMat, "Mul", stream); + arithm_op(threshMat, (float)thresh, TruncMat, "Muls", stream); + arithm_op(ToZeroInvMat, TruncMat, dst, "Add", stream); + } + else + arithm_op(threshInvMat, src, dst, "Mul", stream); + } + else + { + if (type == 0) /* THRESH_BINARY = 0 */ + arithm_op(threshMat, (float)maxval, dst, "Muls", stream); + else if (type == 3) /* THRESH_TOZERO = 3 */ + arithm_op(threshMat, src, dst, "Mul", stream); + else + CV_Error(Error::StsError, "Unknown/unsupported threshold type"); + } + return thresh; +} + +double threshold(const InputArray _src, OutputArray _dst, double thresh, double maxval, int type, + AscendStream& stream) +{ + AscendMat src, dst; + src.upload(_src, stream); + dst.create(src.rows, src.cols, src.type()); + double ret = threshold(src, dst, thresh, maxval, type, stream); + dst.download(_dst, stream); + return ret; +} + +} // namespace cann +} // namespace cv diff --git a/modules/cannops/src/precomp.hpp b/modules/cannops/src/precomp.hpp new file mode 100644 index 0000000000..8411cc4040 --- /dev/null +++ b/modules/cannops/src/precomp.hpp @@ -0,0 +1,14 @@ +// 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. + +#ifndef __OPENCV_PRECOMP_H__ +#define __OPENCV_PRECOMP_H__ + +#include "opencv2/cann.hpp" +#include "opencv2/stream_accessor.hpp" +#include "opencv2/cann_call.hpp" +#include "opencv2/cann_interface.hpp" +#include "opencv2/cann_private.hpp" + +#endif /* __OPENCV_PRECOMP_H__ */ diff --git a/modules/cannops/test/test_core.cpp b/modules/cannops/test/test_core.cpp new file mode 100644 index 0000000000..6b63a8cf06 --- /dev/null +++ b/modules/cannops/test/test_core.cpp @@ -0,0 +1,217 @@ +// 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. + +#include "test_precomp.hpp" +#include + +namespace opencv_test +{ +namespace +{ +TEST(CORE, MERGE) +{ + Mat m1 = (Mat_(2, 2) << 1, 4, 7, 10); + Mat m2 = (Mat_(2, 2) << 2, 5, 8, 11); + Mat m3 = (Mat_(2, 2) << 3, 6, 9, 12); + Mat channels[3] = {m1, m2, m3}; + Mat m; + cv::merge(channels, 3, m); + + cv::cann::setDevice(0); + + AscendMat a1, a2, a3; + a1.upload(m1); + a2.upload(m2); + a3.upload(m3); + AscendMat aclChannels[3] = {a1, a2, a3}; + std::vector aclChannelsVector; + aclChannelsVector.push_back(a1); + aclChannelsVector.push_back(a2); + aclChannelsVector.push_back(a3); + + Mat checker1, checker2; + cv::cann::merge(aclChannels, 3, checker1); + cv::cann::merge(aclChannelsVector, checker2); + + EXPECT_MAT_NEAR(m, checker1, 0.0); + EXPECT_MAT_NEAR(m, checker2, 0.0); + + cv::cann::resetDevice(); +} + +TEST(CORE, SPLIT) +{ + char d[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; + Mat m(2, 2, CV_8UC3, d); + Mat channels[3]; + cv::split(m, channels); + + cv::cann::setDevice(0); + + AscendMat aclChannels[3]; + std::vector aclChannelsVector; + + cv::cann::split(m, aclChannels); + cv::cann::split(m, aclChannelsVector); + + Mat checker1[3], checker2[3]; + aclChannels[0].download(checker1[0]); + aclChannels[1].download(checker1[1]); + aclChannels[2].download(checker1[2]); + + aclChannelsVector[0].download(checker2[0]); + aclChannelsVector[1].download(checker2[1]); + aclChannelsVector[2].download(checker2[2]); + + EXPECT_MAT_NEAR(channels[0], checker1[0], 0.0); + EXPECT_MAT_NEAR(channels[1], checker1[1], 0.0); + EXPECT_MAT_NEAR(channels[2], checker1[2], 0.0); + + EXPECT_MAT_NEAR(channels[0], checker2[0], 0.0); + EXPECT_MAT_NEAR(channels[1], checker2[1], 0.0); + EXPECT_MAT_NEAR(channels[2], checker2[2], 0.0); + + AscendMat npuM; + npuM.upload(m); + cv::cann::split(npuM, aclChannels); + cv::cann::split(npuM, aclChannelsVector); + + aclChannels[0].download(checker1[0]); + aclChannels[1].download(checker1[1]); + aclChannels[2].download(checker1[2]); + + aclChannelsVector[0].download(checker2[0]); + aclChannelsVector[1].download(checker2[1]); + aclChannelsVector[2].download(checker2[2]); + + EXPECT_MAT_NEAR(channels[0], checker1[0], 0.0); + EXPECT_MAT_NEAR(channels[1], checker1[1], 0.0); + EXPECT_MAT_NEAR(channels[2], checker1[2], 0.0); + + EXPECT_MAT_NEAR(channels[0], checker2[0], 0.0); + EXPECT_MAT_NEAR(channels[1], checker2[1], 0.0); + EXPECT_MAT_NEAR(channels[2], checker2[2], 0.0); + cv::cann::resetDevice(); +} + +TEST(CORE, TRANSPOSE) +{ + Mat cpuMat = randomMat(10, 10, CV_32SC3), cpuRetMat, checker; + cv::transpose(cpuMat, cpuRetMat); + cv::cann::transpose(cpuMat, checker); + EXPECT_MAT_NEAR(cpuRetMat, checker, 0.0); + + AscendMat npuMat, npuChecker; + npuMat.upload(cpuMat); + cv::cann::transpose(npuMat, npuChecker); + npuChecker.download(checker); + EXPECT_MAT_NEAR(cpuRetMat, checker, 0.0); +} + +TEST(CORE, FLIP) +{ + Mat cpuMat = randomMat(10, 10, CV_32SC3), cpuRetMat, checker; + + int flipMode; + + for (flipMode = -1; flipMode < 2; flipMode++) + { + cv::flip(cpuMat, cpuRetMat, flipMode); + cv::cann::flip(cpuMat, checker, flipMode); + EXPECT_MAT_NEAR(cpuRetMat, checker, 0.0); + } + + AscendMat npuMat, npuChecker; + npuMat.upload(cpuMat); + for (flipMode = -1; flipMode < 2; flipMode++) + { + cv::flip(cpuMat, cpuRetMat, flipMode); + cv::cann::flip(npuMat, npuChecker, flipMode); + npuChecker.download(checker); + EXPECT_MAT_NEAR(cpuRetMat, checker, 0.0); + } +} + +TEST(CORE, ROTATE) +{ + Mat cpuRetMat, checker, cpuMat = randomMat(3, 5, CV_16S, 0.0, 255.0); + + int rotateMode; + for (rotateMode = 0; rotateMode < 3; rotateMode++) + { + cv::rotate(cpuMat, cpuRetMat, rotateMode); + cv::cann::rotate(cpuMat, checker, rotateMode); + EXPECT_MAT_NEAR(cpuRetMat, checker, 0.0); + } + + AscendMat npuMat, npuChecker; + npuMat.upload(cpuMat); + for (rotateMode = 0; rotateMode < 3; rotateMode++) + { + cv::rotate(cpuMat, cpuRetMat, rotateMode); + cv::cann::rotate(npuMat, npuChecker, rotateMode); + npuChecker.download(checker); + EXPECT_MAT_NEAR(cpuRetMat, checker, 0.0); + } +} + +TEST(CORE, CROP) +{ + Mat cpuOpRet, checker, cpuMat = randomMat(6, 6, CV_32SC3, 0.0, 255.0); + Rect b(1, 2, 4, 4); + Mat cropped_cv(cpuMat, b); + AscendMat cropped_cann(cpuMat, b); + cropped_cann.download(checker); + EXPECT_MAT_NEAR(cropped_cv, checker, 1e-10); +} + +TEST(CORE, CROP_OVERLOAD) +{ + Mat cpuOpRet, checker, cpuMat = randomMat(6, 6, CV_16SC3, 0.0, 255.0); + const Rect b(1, 2, 4, 4); + Mat cropped_cv = cpuMat(b); + AscendMat cropped_cann = cv::cann::crop(cpuMat, b); + cropped_cann.download(checker); + EXPECT_MAT_NEAR(cropped_cv, checker, 1e-10); + + AscendMat npuMat; + npuMat.upload(cpuMat); + cropped_cann = cv::cann::crop(npuMat, b); + cropped_cann.download(checker); + EXPECT_MAT_NEAR(cropped_cv, checker, 1e-10); +} + +TEST(CORE, RESIZE) +{ + Mat resized_cv, checker, cpuMat = randomMat(10, 10, CV_32F, 100.0, 255.0); + Size dsize = Size(6, 6); + // only support {2 INTER_CUBIC} and {3 INTER_AREA} + // only the resize result of INTER_AREA is close to CV's. + int flags = 3; + cv::cann::setDevice(0); + cv::resize(cpuMat, resized_cv, dsize, 0, 0, flags); + cv::cann::resize(cpuMat, checker, dsize, 0, 0, flags); + EXPECT_MAT_NEAR(resized_cv, checker, 1e-4); + + cv::resize(cpuMat, resized_cv, Size(), 0.5, 0.5, flags); + cv::cann::resize(cpuMat, checker, Size(), 0.5, 0.5, flags); + EXPECT_MAT_NEAR(resized_cv, checker, 1e-4); + + AscendMat npuMat, npuChecker; + npuMat.upload(cpuMat); + cv::resize(cpuMat, resized_cv, dsize, 0, 0, flags); + cv::cann::resize(npuMat, npuChecker, dsize, 0, 0, flags); + npuChecker.download(checker); + EXPECT_MAT_NEAR(resized_cv, checker, 1e-4); + + cv::resize(cpuMat, resized_cv, Size(), 0.5, 0.5, flags); + cv::cann::resize(npuMat, npuChecker, Size(), 0.5, 0.5, flags); + npuChecker.download(checker); + EXPECT_MAT_NEAR(resized_cv, checker, 1e-4); + cv::cann::resetDevice(); +} + + +} // namespace +} // namespace opencv_test diff --git a/modules/cannops/test/test_cvtcolor.cpp b/modules/cannops/test/test_cvtcolor.cpp new file mode 100644 index 0000000000..27a9229896 --- /dev/null +++ b/modules/cannops/test/test_cvtcolor.cpp @@ -0,0 +1,89 @@ +// 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. + +#include "test_precomp.hpp" + +namespace opencv_test +{ +namespace +{ + +void cvtColorTest(int code, int cn, int dcn = 3, float diff = 0.0f) +{ + cv::cann::setDevice(DEVICE_ID); + Mat cpuRet, npuRet; + + Mat img8U = randomMat(512, 512, CV_MAKETYPE(CV_8U, cn), 0.0f, 255.0f); + Mat img16U = randomMat(512, 512, CV_MAKETYPE(CV_16U, cn), 0.0f, 65535.0f); + Mat img32F = randomMat(512, 512, CV_MAKETYPE(CV_32F, cn), 0.0f, 65535.0f); + + cv::cvtColor(img8U, cpuRet, code, dcn); + cv::cann::cvtColor(img8U, npuRet, code, dcn); + EXPECT_MAT_NEAR(cpuRet, npuRet, diff); + + cv::cvtColor(img16U, cpuRet, code, dcn); + cv::cann::cvtColor(img16U, npuRet, code, dcn); + EXPECT_MAT_NEAR(cpuRet, npuRet, diff); + + cv::cvtColor(img32F, cpuRet, code, dcn); + cv::cann::cvtColor(img32F, npuRet, code, dcn); + EXPECT_MAT_NEAR(cpuRet, npuRet, diff); + cv::cann::resetDevice(); +} + +TEST(CVT_COLOR, BGR2BGRA) { cvtColorTest(COLOR_BGR2BGRA, 3, 4); } +TEST(CVT_COLOR, BGRA2BGR) { cvtColorTest(COLOR_BGRA2BGR, 4); } +TEST(CVT_COLOR, BGR2RGBA) { cvtColorTest(COLOR_BGR2RGBA, 3, 4); } +TEST(CVT_COLOR, RGBA2BGR) { cvtColorTest(COLOR_RGBA2BGR, 4); } +TEST(CVT_COLOR, BGR2RGB) { cvtColorTest(COLOR_BGR2RGB, 3); } +TEST(CVT_COLOR, BGRA2RGBA) { cvtColorTest(COLOR_BGRA2RGBA, 4, 4); } + +// Due to parameter accuracy issues, the calculation results have certain accuracy differences. +TEST(CVT_COLOR, BGR2GRAY) { cvtColorTest(COLOR_BGR2GRAY, 3, 1, 10.0f); } +TEST(CVT_COLOR, RGB2GRAY) { cvtColorTest(COLOR_BGR2GRAY, 3, 1, 10.0f); } +TEST(CVT_COLOR, GRAY2BGR) { cvtColorTest(COLOR_GRAY2BGR, 1); } +TEST(CVT_COLOR, GRAY2BGRA) { cvtColorTest(COLOR_GRAY2BGRA, 1, 4); } +TEST(CVT_COLOR, BGRA2GRAY) { cvtColorTest(COLOR_BGRA2GRAY, 4, 1, 10.0f); } +TEST(CVT_COLOR, RGBA2GRAY) { cvtColorTest(COLOR_RGBA2GRAY, 4, 1, 10.0f); } + +TEST(CVT_COLOR, RGB2XYZ) { cvtColorTest(COLOR_RGB2XYZ, 3, 3, 50.0f); } +TEST(CVT_COLOR, BGR2XYZ) { cvtColorTest(COLOR_BGR2XYZ, 3, 3, 50.0f); } +TEST(CVT_COLOR, XYZ2BGR) { cvtColorTest(COLOR_XYZ2BGR, 3, 3, 150.0f); } +TEST(CVT_COLOR, XYZ2RGB) { cvtColorTest(COLOR_XYZ2RGB, 3, 3, 150.0f); } +TEST(CVT_COLOR, XYZ2BGR_DC4) { cvtColorTest(COLOR_XYZ2BGR, 3, 4, 150.0f); } +TEST(CVT_COLOR, XYZ2RGB_DC4) { cvtColorTest(COLOR_XYZ2RGB, 3, 4, 150.0f); } + +TEST(CVT_COLOR, BGR2YCrCb) { cvtColorTest(COLOR_BGR2YCrCb, 3, 3, 10.0f); } +TEST(CVT_COLOR, RGB2YCrCb) { cvtColorTest(COLOR_RGB2YCrCb, 3, 3, 10.0f); } +TEST(CVT_COLOR, YCrCb2BGR) { cvtColorTest(COLOR_YCrCb2BGR, 3, 3, 10.0f); } +TEST(CVT_COLOR, YCrCb2RGB) { cvtColorTest(COLOR_YCrCb2RGB, 3, 3, 10.0f); } +TEST(CVT_COLOR, YCrCb2BGR_DC4) { cvtColorTest(COLOR_YCrCb2BGR, 3, 4, 10.0f); } +TEST(CVT_COLOR, YCrCb2RGB_DC4) { cvtColorTest(COLOR_YCrCb2RGB, 3, 4, 10.0f); } + +TEST(CVT_COLOR, BGR2YUV) { cvtColorTest(COLOR_BGR2YUV, 3, 3, 10.0f); } +TEST(CVT_COLOR, RGB2YUV) { cvtColorTest(COLOR_RGB2YUV, 3, 3, 10.0f); } +TEST(CVT_COLOR, YUV2BGR) { cvtColorTest(COLOR_YUV2BGR, 3, 3, 10.0f); } +TEST(CVT_COLOR, YUV2RGB) { cvtColorTest(COLOR_YUV2RGB, 3, 3, 10.0f); } +TEST(CVT_COLOR, YUV2BGR_DC4) { cvtColorTest(COLOR_YUV2BGR, 3, 4, 10.0f); } +TEST(CVT_COLOR, YUV2RGB_DC4) { cvtColorTest(COLOR_YUV2RGB, 3, 4, 10.0f); } + +// Test of AscendMat. Since the logic is the same, only interface test is needed. +TEST(CVT_COLOR, COLOR_BGR2BGRA_ASCENDMAT) +{ + cv::cann::setDevice(DEVICE_ID); + Mat cpuRet, npuRet; + + Mat img8U = randomMat(512, 512, CV_8UC3, 0.0f, 255.0f); + cv::cvtColor(img8U, cpuRet, COLOR_BGR2BGRA, 4); + + AscendMat npuImg8U, npuChecker; + npuImg8U.upload(img8U); + cv::cann::cvtColor(npuImg8U, npuChecker, COLOR_BGR2BGRA, 4); + npuChecker.download(npuRet); + EXPECT_MAT_NEAR(cpuRet, npuRet, 10.0f); + cv::cann::resetDevice(); +} + +} // namespace +} // namespace opencv_test diff --git a/modules/cannops/test/test_element_operations.cpp b/modules/cannops/test/test_element_operations.cpp new file mode 100644 index 0000000000..76c103a65f --- /dev/null +++ b/modules/cannops/test/test_element_operations.cpp @@ -0,0 +1,697 @@ +// 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. + +#include "test_precomp.hpp" +#include + +namespace opencv_test +{ +namespace +{ +template +void testMatOpMat(FCV cvFunc, FCANN cannFunc, PARAMS... param) +{ + cv::cann::setDevice(DEVICE_ID); + Mat mat1 = randomMat(10, 10, CV_32SC3); + Mat mat2 = randomMat(10, 10, CV_32SC3); + Mat cpuDst, check; + + cvFunc(mat1, mat2, cpuDst, param...); + cannFunc(mat1, mat2, check, param..., AscendStream::Null()); + EXPECT_MAT_NEAR(cpuDst, check, 1.0); + + AscendStream stream; + cannFunc(mat1, mat2, check, param..., stream); + stream.waitForCompletion(); + EXPECT_MAT_NEAR(cpuDst, check, 1.0); + + cv::cann::resetDevice(); +} + +template +void testAscendMatOpAscendMatMask(FCV cvFunc, FCANN cannFunc, DTMASK mask = AscendMat(), + PARAMS... param) +{ + cv::cann::setDevice(DEVICE_ID); + Mat mat1 = randomMat(10, 10, CV_32SC3); + Mat mat2 = randomMat(10, 10, CV_32SC3); + Mat cpuDst, check, cpuMask; + AscendMat npuMat1, npuMat2, npuCheck; + npuMat1.upload(mat1); + npuMat2.upload(mat2); + if (mask.empty()) + { + cvFunc(mat1, mat2, cpuDst, noArray(), param...); + } + else + { + mask.download(cpuMask); + cvFunc(mat1, mat2, cpuDst, cpuMask, param...); + } + + cannFunc(npuMat1, npuMat2, npuCheck, mask, param..., AscendStream::Null()); + npuCheck.download(check); + EXPECT_MAT_NEAR(cpuDst, check, 1.0); + + AscendStream stream; + cannFunc(npuMat1, npuMat2, npuCheck, mask, param..., stream); + npuCheck.download(check); + stream.waitForCompletion(); + EXPECT_MAT_NEAR(cpuDst, check, 1.0); + + cv::cann::resetDevice(); +} + +template +void testAscendMatOpAscendMat(FCV cvFunc, FCANN cannFunc, PARAMS... param) +{ + cv::cann::setDevice(DEVICE_ID); + Mat mat1 = randomMat(10, 10, CV_32SC3); + Mat mat2 = randomMat(10, 10, CV_32SC3); + Mat cpuDst, check; + AscendMat npuMat1, npuMat2, npuCheck; + npuMat1.upload(mat1); + npuMat2.upload(mat2); + cvFunc(mat1, mat2, cpuDst, param...); + cannFunc(npuMat1, npuMat2, npuCheck, param..., AscendStream::Null()); + npuCheck.download(check); + EXPECT_MAT_NEAR(cpuDst, check, 1.0); + + AscendStream stream; + cannFunc(npuMat1, npuMat2, npuCheck, param..., stream); + npuCheck.download(check); + stream.waitForCompletion(); + EXPECT_MAT_NEAR(cpuDst, check, 1.0); + + cv::cann::resetDevice(); +} + +TEST(ELEMENTWISE_OP, MAT_ADD_MAT) +{ + testMatOpMat( + cv::add, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + noArray(), -1); + testAscendMatOpAscendMatMask( + cv::add, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + AscendMat(), -1); +} + +TEST(ELEMENTWISE_OP, MAT_SUB_MAT) +{ + testMatOpMat( + cv::subtract, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + int dtype, AscendStream& stream) + { cv::cann::subtract(src1, src2, dst, mask, dtype, stream); }, + noArray(), -1); + testAscendMatOpAscendMatMask( + cv::subtract, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::subtract(src1, src2, dst, mask, dtype, stream); }, + AscendMat(), -1); +} + +TEST(ELEMENTWISE_OP, MAT_MUL_MAT) +{ + testMatOpMat( + cv::multiply, + [](const InputArray src1, const InputArray src2, OutputArray dst, float scale, int dtype, + AscendStream& stream) { cv::cann::multiply(src1, src2, dst, scale, dtype, stream); }, + 1, -1); + testAscendMatOpAscendMat( + cv::multiply, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) { cv::cann::multiply(src1, src2, dst, scale, dtype, stream); }, + 1, -1); +} + +TEST(ELEMENTWISE_OP, MAT_DIV_MAT) +{ + testMatOpMat([](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) + { cv::divide(src1, src2, dst, scale, dtype); }, + [](const InputArray src1, const InputArray src2, OutputArray dst, float scale, + int dtype, AscendStream& stream) + { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, + 1, -1); + testAscendMatOpAscendMat( + [](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) + { cv::divide(src1, src2, dst, scale, dtype); }, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, + 1, -1); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_AND_MAT) +{ + testMatOpMat( + cv::bitwise_and, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_and(src1, src2, dst, mask, stream); }, + noArray()); + testAscendMatOpAscendMatMask( + cv::bitwise_and, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_and(src1, src2, dst, mask, stream); }, + AscendMat()); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_OR_MAT) +{ + testMatOpMat( + cv::bitwise_or, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_or(src1, src2, dst, mask, stream); }, + noArray()); + testAscendMatOpAscendMatMask( + cv::bitwise_or, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_or(src1, src2, dst, mask, stream); }, + AscendMat()); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_XOR_MAT) +{ + testMatOpMat( + cv::bitwise_xor, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_xor(src1, src2, dst, mask, stream); }, + noArray()); + testAscendMatOpAscendMatMask( + cv::bitwise_xor, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_xor(src1, src2, dst, mask, stream); }, + AscendMat()); +} + +TEST(ELEMENTWISE_OP, MAT_ADD_MAT_WITH_MASK_AND_DTYPE) +{ + testMatOpMat( + cv::add, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + genMask(), CV_32SC3); + testAscendMatOpAscendMatMask( + cv::add, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + AscendMat(), CV_32SC3); +} + +TEST(ELEMENTWISE_OP, MAT_SUB_MAT_WITH_MASK_AND_DTYPE) +{ + testMatOpMat( + cv::subtract, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + int dtype, AscendStream& stream) + { cv::cann::subtract(src1, src2, dst, mask, dtype, stream); }, + genMask(), CV_32SC3); + testAscendMatOpAscendMatMask( + cv::subtract, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::subtract(src1, src2, dst, mask, dtype, stream); }, + AscendMat(), CV_32SC3); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_AND_MAT_WITH_MASK) +{ + testMatOpMat( + cv::bitwise_and, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_and(src1, src2, dst, mask, stream); }, + genMask()); + testAscendMatOpAscendMatMask( + cv::bitwise_and, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_and(src1, src2, dst, mask, stream); }, + genNpuMask()); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_OR_MAT_WITH_MASK) +{ + testMatOpMat( + cv::bitwise_or, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_or(src1, src2, dst, mask, stream); }, + genMask()); + testAscendMatOpAscendMatMask( + cv::bitwise_or, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_or(src1, src2, dst, mask, stream); }, + genNpuMask()); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_XOR_MAT_WITH_MASK) +{ + testMatOpMat( + cv::bitwise_xor, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_xor(src1, src2, dst, mask, stream); }, + genMask()); + testAscendMatOpAscendMatMask( + cv::bitwise_xor, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_xor(src1, src2, dst, mask, stream); }, + genNpuMask()); +} + +float randomScale = randomNum(); +TEST(ELEMENTWISE_OP, MAT_MUL_MAT_WITH_SCALE) +{ + testMatOpMat( + cv::multiply, + [](const InputArray src1, const InputArray src2, OutputArray dst, float scale, int dtype, + AscendStream& stream) { cv::cann::multiply(src1, src2, dst, scale, dtype, stream); }, + randomScale, -1); + testAscendMatOpAscendMat( + cv::multiply, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) { cv::cann::multiply(src1, src2, dst, scale, dtype, stream); }, + randomScale, -1); +} + +TEST(ELEMENTWISE_OP, MAT_DIV_MAT_WITH_SCALE) +{ + testMatOpMat([](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) + { cv::divide(src1, src2, dst, scale, dtype); }, + [](const InputArray src1, const InputArray src2, OutputArray dst, float scale, + int dtype, AscendStream& stream) + { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, + randomScale, -1); + testAscendMatOpAscendMat( + [](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) + { cv::divide(src1, src2, dst, scale, dtype); }, + [](const AscendMat& src1, const AscendMat& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, + randomScale, -1); +} + +template +void testMatOpScalar(FCV cvFunc, FCANN cannFunc, PARAMS... param) +{ + Scalar scalar = randomScalar(); + Mat mat(10, 10, CV_32SC3, randomScalar()); + Mat cpuDst1, cpuDst2, checker1, checker2; + + cvFunc(Mat(10, 10, CV_32SC3, scalar), mat, cpuDst1, param...); + cvFunc(mat, Mat(10, 10, CV_32SC3, scalar), cpuDst2, param...); + cv::cann::setDevice(DEVICE_ID); + + cannFunc(scalar, mat, checker1, param..., AscendStream::Null()); + cannFunc(mat, scalar, checker2, param..., AscendStream::Null()); + + EXPECT_MAT_NEAR(cpuDst1, checker1, 1.0); + EXPECT_MAT_NEAR(cpuDst2, checker2, 1.0); + + AscendStream stream; + cannFunc(scalar, mat, checker1, param..., stream); + cannFunc(mat, scalar, checker2, param..., stream); + stream.waitForCompletion(); + EXPECT_MAT_NEAR(cpuDst1, checker1, 1.0); + EXPECT_MAT_NEAR(cpuDst2, checker2, 1.0); + + cv::cann::resetDevice(); +} + +template +void testAscendMatOpScalarMask(FCV cvFunc, FCANN cannFunc, DTMASK mask, PARAMS... param) +{ + Scalar scalar = randomScalar(); + Mat mat(10, 10, CV_32SC3, randomScalar()); + Mat cpuDst, checker, cpuMask; + AscendMat npuMat, npuChecker; + npuMat.upload(mat); + if (mask.empty()) + { + cvFunc(mat, Mat(10, 10, CV_32SC3, scalar), cpuDst, noArray(), param...); + } + else + { + mask.download(cpuMask); + cvFunc(mat, Mat(10, 10, CV_32SC3, scalar), cpuDst, cpuMask, param...); + } + cv::cann::setDevice(DEVICE_ID); + + cannFunc(npuMat, scalar, npuChecker, mask, param..., AscendStream::Null()); + npuChecker.download(checker); + EXPECT_MAT_NEAR(cpuDst, checker, 1.0); + + AscendStream stream; + cannFunc(npuMat, scalar, npuChecker, mask, param..., stream); + stream.waitForCompletion(); + npuChecker.download(checker); + EXPECT_MAT_NEAR(cpuDst, checker, 1.0); + + cv::cann::resetDevice(); +} +template +void testScalarOpAscendMatMask(FCV cvFunc, FCANN cannFunc, DTMASK mask, PARAMS... param) +{ + Scalar scalar = randomScalar(); + Mat mat(10, 10, CV_32SC3, randomScalar()); + Mat cpuDst, checker, cpuMask; + AscendMat npuMat, npuChecker; + npuMat.upload(mat); + if (mask.empty()) + { + cvFunc(Mat(10, 10, CV_32SC3, scalar), mat, cpuDst, noArray(), param...); + } + else + { + mask.download(cpuMask); + cvFunc(Mat(10, 10, CV_32SC3, scalar), mat, cpuDst, cpuMask, param...); + } + cv::cann::setDevice(DEVICE_ID); + + cannFunc(scalar, npuMat, npuChecker, mask, param..., AscendStream::Null()); + npuChecker.download(checker); + EXPECT_MAT_NEAR(cpuDst, checker, 1.0); + + AscendStream stream; + cannFunc(scalar, npuMat, npuChecker, mask, param..., stream); + stream.waitForCompletion(); + npuChecker.download(checker); + EXPECT_MAT_NEAR(cpuDst, checker, 1.0); + + cv::cann::resetDevice(); +} +template +void testAscendMatOpScalar(FCV cvFunc, FCANN cannFunc, PARAMS... param) +{ + Scalar scalar = randomScalar(); + Mat mat(10, 10, CV_32SC3, randomScalar()); + Mat cpuDst, checker; + AscendMat npuMat, npuChecker; + npuMat.upload(mat); + + cvFunc(mat, Mat(10, 10, CV_32SC3, scalar), cpuDst, param...); + cv::cann::setDevice(DEVICE_ID); + + cannFunc(npuMat, scalar, npuChecker, param..., AscendStream::Null()); + npuChecker.download(checker); + EXPECT_MAT_NEAR(cpuDst, checker, 1.0); + + AscendStream stream; + cannFunc(npuMat, scalar, npuChecker, param..., stream); + stream.waitForCompletion(); + npuChecker.download(checker); + + cv::cann::resetDevice(); +} + +TEST(ELEMENTWISE_OP, MAT_ADD_SCALAR) +{ + testMatOpScalar( + cv::add, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + noArray(), -1); + testAscendMatOpScalarMask( + cv::add, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + AscendMat(), -1); + testScalarOpAscendMatMask( + cv::add, + [](const Scalar& src1, const AscendMat& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + AscendMat(), -1); +} + +TEST(ELEMENTWISE_OP, MAT_SUB_SCALAR) +{ + testMatOpScalar( + cv::subtract, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + int dtype, AscendStream& stream) + { cv::cann::subtract(src1, src2, dst, mask, dtype, stream); }, + noArray(), -1); + testAscendMatOpScalarMask( + cv::subtract, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::subtract(src1, src2, dst, mask, dtype, stream); }, + AscendMat(), -1); +} + +TEST(ELEMENTWISE_OP, MAT_MUL_SCALAR) +{ + testMatOpScalar( + cv::multiply, + [](const InputArray src1, const InputArray src2, OutputArray dst, float scale, int dtype, + AscendStream& stream) { cv::cann::multiply(src1, src2, dst, scale, dtype, stream); }, + 1, -1); + testAscendMatOpScalar( + cv::multiply, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) { cv::cann::multiply(src1, src2, dst, scale, dtype, stream); }, + 1, -1); +} + +TEST(ELEMENTWISE_OP, MAT_DIV_SCALAR) +{ + testMatOpScalar( + [](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) + { cv::divide(src1, src2, dst, scale, dtype); }, + [](const InputArray src1, const InputArray src2, OutputArray dst, float scale, int dtype, + AscendStream& stream) { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, + 1, -1); + testAscendMatOpScalar( + [](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) + { cv::divide(src1, src2, dst, scale, dtype); }, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, float scale, int dtype, + AscendStream& stream) { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, + 1, -1); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_AND_SCALAR) +{ + testMatOpScalar( + cv::bitwise_and, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_and(src1, src2, dst, mask, stream); }, + noArray()); + testAscendMatOpScalarMask( + cv::bitwise_and, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_and(src1, src2, dst, mask, stream); }, + AscendMat()); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_OR_SCALAR) +{ + testMatOpScalar( + cv::bitwise_or, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_or(src1, src2, dst, mask, stream); }, + noArray()); + testAscendMatOpScalarMask( + cv::bitwise_or, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_or(src1, src2, dst, mask, stream); }, + AscendMat()); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_XOR_SCALAR) +{ + testMatOpScalar( + cv::bitwise_xor, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_xor(src1, src2, dst, mask, stream); }, + noArray()); + testAscendMatOpScalarMask( + cv::bitwise_xor, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_xor(src1, src2, dst, mask, stream); }, + AscendMat()); +} + +TEST(ELEMENTWISE_OP, MAT_ADD_SCALAR_WITH_MASK_AND_DETYPE) +{ + testMatOpScalar( + cv::add, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + genMask(), CV_32SC3); + testAscendMatOpScalarMask( + cv::add, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::add(src1, src2, dst, mask, dtype, stream); }, + genNpuMask(), CV_32SC3); +} + +TEST(ELEMENTWISE_OP, MAT_SUB_SCALAR_WITH_MASK_AND_DETYPE) +{ + testMatOpScalar( + cv::subtract, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + int dtype, AscendStream& stream) + { cv::cann::subtract(src1, src2, dst, mask, dtype, stream); }, + genMask(), CV_32SC3); + testAscendMatOpScalarMask( + cv::subtract, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + int dtype, AscendStream& stream) + { cv::cann::subtract(src1, src2, dst, mask, dtype, stream); }, + genNpuMask(), CV_32SC3); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_AND_SCALAR_WITH_MASK) +{ + testMatOpScalar( + cv::bitwise_and, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_and(src1, src2, dst, mask, stream); }, + genMask()); + testAscendMatOpScalarMask( + cv::bitwise_and, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_and(src1, src2, dst, mask, stream); }, + genNpuMask()); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_OR_SCALAR_WITH_MASK) +{ + testMatOpScalar( + cv::bitwise_or, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_or(src1, src2, dst, mask, stream); }, + genMask()); + testAscendMatOpScalarMask( + cv::bitwise_or, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_or(src1, src2, dst, mask, stream); }, + genNpuMask()); +} + +TEST(ELEMENTWISE_OP, MAT_BITWISE_XOR_SCALAR_WITH_MASK) +{ + testMatOpScalar( + cv::bitwise_xor, + [](const InputArray src1, const InputArray src2, OutputArray dst, const InputArray mask, + AscendStream& stream) { cv::cann::bitwise_xor(src1, src2, dst, mask, stream); }, + genMask()); + testAscendMatOpScalarMask( + cv::bitwise_xor, + [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, const AscendMat& mask, + AscendStream& stream) { cv::cann::bitwise_xor(src1, src2, dst, mask, stream); }, + genNpuMask()); +} + +// TODO: I think the cv result is wrong, which has truncated middle result. +// Disable these two test case bacause it't not stable. +// TEST(ELEMENTWISE_OP, MAT_MUL_SCALAR_WITH_SCALE) +// { +// testMatOpScalar( +// cv::multiply, +// [](const InputArray src1, const InputArray src2, OutputArray dst, float scale, int dtype, +// AscendStream& stream) { cv::cann::multiply(src1, src2, dst, scale, dtype, stream); }, +// randomScale, CV_32SC3); +// testAscendMatOpScalar( +// [](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) +// { cv::divide(src1, src2, dst, scale, dtype); }, +// [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, float scale, int dtype, +// AscendStream& stream) { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, +// randomScale, -1); +// } + +// TEST(ELEMENTWISE_OP, MAT_DIV_SCALAR_WITH_SCALE) +// { +// testMatOpScalar( +// [](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) +// { cv::divide(src1, src2, dst, scale, dtype); }, +// [](const InputArray src1, const InputArray src2, OutputArray dst, float scale, int dtype, +// AscendStream& stream) { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, +// randomScale, -1); +// testAscendMatOpScalar( +// [](const cv::Mat& src1, const cv::Mat& src2, cv::Mat& dst, double scale, int dtype) +// { cv::divide(src1, src2, dst, scale, dtype); }, +// [](const AscendMat& src1, const Scalar& src2, AscendMat& dst, float scale, int dtype, +// AscendStream& stream) { cv::cann::divide(src1, src2, dst, scale, dtype, stream); }, +// randomScale, -1); +// } + +TEST(ELEMENTWISE_OP, MAT_BITWISE_NOT) +{ + Mat cpuOpRet, checker, cpuMat = randomMat(10, 10, CV_32SC3); + cv::cann::setDevice(DEVICE_ID); + cv::bitwise_not(cpuMat, cpuOpRet); + cv::cann::bitwise_not(cpuMat, checker); + EXPECT_MAT_NEAR(cpuOpRet, checker, 0.0); + + AscendMat npuMat, npuOpRet; + npuMat.upload(cpuMat); + cv::cann::bitwise_not(npuMat, npuOpRet); + npuOpRet.download(checker); + EXPECT_MAT_NEAR(cpuOpRet, checker, 0.0); + + cv::cann::resetDevice(); +} + +// TODO random test matrix +TEST(ELEMENTWISE_OP, MAT_ADD_WEIGHTED) +{ + Mat cpuOpRet, checker, cpuMat1 = Mat::ones(5, 5, CV_32S), cpuMat2 = Mat::ones(5, 5, CV_32S); + + cv::cann::setDevice(DEVICE_ID); + cv::addWeighted(cpuMat1, 2, cpuMat2, 3, 5, cpuOpRet); + cv::cann::addWeighted(cpuMat1, 2, cpuMat2, 3, 5, checker); + EXPECT_MAT_NEAR(cpuOpRet, checker, 0.0); + + AscendMat npuOpRet, npuMat1, npuMat2; + npuMat1.upload(cpuMat1); + npuMat2.upload(cpuMat2); + cv::cann::addWeighted(npuMat1, 2, npuMat2, 3, 5, npuOpRet); + npuOpRet.download(checker); + EXPECT_MAT_NEAR(cpuOpRet, checker, 0.0); + + cv::cann::resetDevice(); +} + +TEST(ELEMENTWISE_OP, MAT_THRESHOLD) +{ + Mat cpuOpRet, checker, cpuMat = randomMat(10, 10, CV_16SC3, 0.0, 255.0); + + AscendMat ascendMat, ascendMat16F, aclOpRet, aclOpRet16S; + cv::cann::setDevice(DEVICE_ID); + ascendMat.upload(cpuMat); + ascendMat.convertTo(ascendMat16F, CV_16F); + + Mat cpuMat16F, checker16F; + cpuMat.convertTo(cpuMat16F, CV_16F); + + for (int i = 0; i <= 4; i++) + { + cv::threshold(cpuMat, cpuOpRet, 128, 250, i); + // TODO find the reason empty AscendMat is not continuous. + cv::cann::threshold(ascendMat16F, aclOpRet, 128, 250, i); + aclOpRet.convertTo(aclOpRet16S, CV_16S); + aclOpRet16S.download(checker); + + EXPECT_MAT_NEAR(cpuOpRet, checker, 1e-10); + + cv::cann::threshold(cpuMat16F, checker16F, 128, 250, i); + checker16F.convertTo(checker, CV_16S); + EXPECT_MAT_NEAR(cpuOpRet, checker, 1e-10); + } + + cv::cann::resetDevice(); +} + +} // namespace +} // namespace opencv_test diff --git a/modules/cannops/test/test_main.cpp b/modules/cannops/test/test_main.cpp new file mode 100644 index 0000000000..202c6af27e --- /dev/null +++ b/modules/cannops/test/test_main.cpp @@ -0,0 +1,21 @@ +// 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. + +#include "test_precomp.hpp" + +class CannEnvironment : public ::testing::Environment +{ +public: + virtual ~CannEnvironment() = default; + virtual void SetUp() CV_OVERRIDE { initAcl(); } + virtual void TearDown() CV_OVERRIDE { finalizeAcl(); } +}; + +static void initTests() +{ + CannEnvironment* cannEnv = new CannEnvironment(); + ::testing::AddGlobalTestEnvironment(cannEnv); +} + +CV_TEST_MAIN("cannops", initTests()); diff --git a/modules/cannops/test/test_npumat.cpp b/modules/cannops/test/test_npumat.cpp new file mode 100644 index 0000000000..1ff445399f --- /dev/null +++ b/modules/cannops/test/test_npumat.cpp @@ -0,0 +1,146 @@ +// 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. + +#include "test_precomp.hpp" + +namespace opencv_test +{ +namespace +{ + +class DummyAllocator : public AscendMat::Allocator +{ +public: + std::shared_ptr allocate(size_t size) CV_OVERRIDE + { + CV_UNUSED(size); + return std::shared_ptr(); + } + bool allocate(cv::cann::AscendMat* mat, int rows, int cols, size_t elemSize) CV_OVERRIDE + { + CV_UNUSED(rows); + CV_UNUSED(cols); + CV_UNUSED(elemSize); + mat->data = std::shared_ptr((uchar*)0x12345, [](void* ptr) { CV_UNUSED(ptr); }); + return true; + } +}; + +TEST(AscendMat, Construct) +{ + cv::cann::setDevice(0); + // 1 Default constructor. + AscendMat defaultAscendMat; + AscendMat::Allocator* defaultAllocator = AscendMat::defaultAllocator(); + ASSERT_EQ(defaultAscendMat.allocator, defaultAllocator); + + // 2 get & set allocator. + DummyAllocator dummyAllocator; + AscendMat::setDefaultAllocator(&dummyAllocator); + ASSERT_EQ(defaultAscendMat.defaultAllocator(), &dummyAllocator); + AscendMat::setDefaultAllocator(defaultAllocator); + + // 3 constructs AscendMat of the specified size and type + AscendMat specifiedSizeAscendMat1(5, 6, CV_8UC3); + AscendMat specifiedSizeAscendMat2(Size(300, 200), CV_64F); + + ASSERT_EQ(specifiedSizeAscendMat1.rows, 5); + ASSERT_EQ(specifiedSizeAscendMat1.cols, 6); + ASSERT_EQ(specifiedSizeAscendMat1.depth(), CV_8U); + ASSERT_EQ(specifiedSizeAscendMat1.channels(), 3); + + ASSERT_EQ(specifiedSizeAscendMat2.cols, 300); + ASSERT_EQ(specifiedSizeAscendMat2.rows, 200); + ASSERT_EQ(specifiedSizeAscendMat2.depth(), CV_64F); + ASSERT_EQ(specifiedSizeAscendMat2.channels(), 1); + + // 4 constructs AscendMat and fills it with the specified value s + srand((unsigned int)(time(NULL))); + Scalar sc(rand() % 256, rand() % 256, rand() % 256, rand() % 256); + + Mat scalarToMat(7, 8, CV_8UC3, sc); + AscendMat scalarToAscendMat1(7, 8, CV_8UC3, sc); + Mat scalarToMatChecker; + scalarToAscendMat1.download(scalarToMatChecker); + + EXPECT_MAT_NEAR(scalarToMat, scalarToMatChecker, 0.0); + + AscendMat scalarToAscendMat2(Size(123, 345), CV_32S); + + ASSERT_EQ(scalarToAscendMat1.rows, 7); + ASSERT_EQ(scalarToAscendMat1.cols, 8); + ASSERT_EQ(scalarToAscendMat1.depth(), CV_8U); + ASSERT_EQ(scalarToAscendMat1.channels(), 3); + + ASSERT_EQ(scalarToAscendMat2.cols, 123); + ASSERT_EQ(scalarToAscendMat2.rows, 345); + ASSERT_EQ(scalarToAscendMat2.depth(), CV_32S); + ASSERT_EQ(scalarToAscendMat2.channels(), 1); + + // 6 builds AscendMat from host memory + Scalar sc2(rand() % 256, rand() % 256, rand() % 256, rand() % 256); + Mat randomMat(7, 8, CV_8UC3, sc2); + InputArray arr = randomMat; + + AscendMat fromInputArray(arr, AscendStream::Null()); + Mat randomMatChecker; + fromInputArray.download(randomMatChecker); + EXPECT_MAT_NEAR(randomMat, randomMatChecker, 0.0); + + cv::cann::resetDevice(); +} + +TEST(AscendMat, Assignment) +{ + DummyAllocator dummyAllocator; + AscendMat mat1; + AscendMat mat2(3, 4, CV_8SC1, &dummyAllocator); + mat1 = mat2; + + ASSERT_EQ(mat1.rows, 3); + ASSERT_EQ(mat1.cols, 4); + ASSERT_EQ(mat1.depth(), CV_8S); + ASSERT_EQ(mat1.channels(), 1); + ASSERT_EQ(mat1.data.get(), (uchar*)0x12345); +} + +TEST(AscendMat, SetTo) +{ + cv::cann::setDevice(0); + + srand((unsigned int)(time(NULL))); + Scalar sc(rand() % 256, rand() % 256, rand() % 256, rand() % 256); + + AscendMat ascendMat(2, 2, CV_8UC4); + ascendMat.setTo(sc); + Mat mat(2, 2, CV_8UC4, sc); + Mat checker; + ascendMat.download(checker); + + EXPECT_MAT_NEAR(mat, checker, 0.0); + + cv::cann::resetDevice(); +} + +TEST(AscendMat, ConvertTo) +{ + cv::cann::setDevice(0); + + srand((unsigned int)(time(NULL))); + Scalar sc(rand() % 256, rand() % 256, rand() % 256, rand() % 256); + + AscendMat ascendMat(2, 2, CV_8UC4, sc); + AscendMat convertedAscendMat; + ascendMat.convertTo(convertedAscendMat, CV_16S); + Mat mat(2, 2, CV_16SC4, sc); + Mat checker; + convertedAscendMat.download(checker); + + EXPECT_MAT_NEAR(mat, checker, 0.0); + + cv::cann::resetDevice(); +} + +} // namespace +} // namespace opencv_test diff --git a/modules/cannops/test/test_precomp.hpp b/modules/cannops/test/test_precomp.hpp new file mode 100644 index 0000000000..f7bdbea0b0 --- /dev/null +++ b/modules/cannops/test/test_precomp.hpp @@ -0,0 +1,28 @@ +// 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. + +#ifndef __OPENCV_TEST_PRECOMP_HPP__ +#define __OPENCV_TEST_PRECOMP_HPP__ + +#include "opencv2/ts.hpp" +#include "opencv2/cann.hpp" +#include "opencv2/ts/cuda_test.hpp" +#include "opencv2/cann_interface.hpp" + +using namespace cv; +using namespace cv::cann; +#undef EXPECT_MAT_NEAR +#define EXPECT_MAT_NEAR(m1, m2, eps) EXPECT_PRED_FORMAT3(cvtest::assertMatNear, m1, m2, eps) +#define ASSERT_MAT_NEAR(m1, m2, eps) ASSERT_PRED_FORMAT3(cvtest::assertMatNear, m1, m2, eps) + +#define DEVICE_ID 0 + +Mat randomMat(int w, int h, int dtype, float min = 1.0f, float max = 10.0f); +Scalar randomScalar(); +float randomNum(); +int randomInterger(); +Mat genMask(); +AscendMat genNpuMask(); + +#endif //__OPENCV_TEST_PRECOMP_HPP__ diff --git a/modules/cannops/test/test_utils.cpp b/modules/cannops/test/test_utils.cpp new file mode 100644 index 0000000000..d2bd31647b --- /dev/null +++ b/modules/cannops/test/test_utils.cpp @@ -0,0 +1,49 @@ +// 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. + +#include "test_precomp.hpp" + +// Random Generator +Mat randomMat(int w, int h, int dtype, float min, float max) +{ + Mat rnMat(w, h, dtype); + RNG rng(getTickCount()); + rng.fill(rnMat, RNG::UNIFORM, min, max); + return rnMat; +} +Scalar randomScalar() +{ + RNG rng(getTickCount()); + Scalar sc; + rng.fill(sc, RNG::UNIFORM, 1.0, 5.0); + return sc; +} +float randomNum() +{ + RNG rng(getTickCount()); + float rdnNum = float(rng.uniform(1.0, 5.0)); + return rdnNum; +} + +int randomInterger() +{ + RNG rng(getTickCount()); + float rdnNum = float(rng.uniform(1, 5)); + return rdnNum; +} + +Mat genMask() +{ + Mat mask = Mat::zeros(Size(10, 10), CV_8UC1); + rectangle(mask, cv::Rect(5, 5, 3, 3), Scalar(255), -1); + return mask; +} + +AscendMat genNpuMask() +{ + cv::Mat mask = genMask(); + cv::cann::AscendMat npuMask; + npuMask.upload(mask); + return npuMask; +} diff --git a/modules/cannops/tutorials/ascend_npu_image_processing.markdown b/modules/cannops/tutorials/ascend_npu_image_processing.markdown new file mode 100644 index 0000000000..ed905831d3 --- /dev/null +++ b/modules/cannops/tutorials/ascend_npu_image_processing.markdown @@ -0,0 +1,130 @@ +Ascend NPU Image Processing {#tutorial_ascend_npu_image_processing} +========================================================== + +## Goal + +In this guide, you will gain insights into the thread safety of Ascend operators already in use, as well as discover how to effectively employ Ascend operators for image preprocessing and understand their usage limitations. + +## Preface + +We provide a suite of common matrix operation operators that support the [Ascend NPU](https://www.hiascend.com/en/) within OpenCV. For user convenience, the new 'AscendMat' structure and its associated operators maintain compatibility with the 'Mat' interface in OpenCV. These operators encompass a wide range of frequently used functions, including arithmetic operations, image processing operations, and image color space conversion. All of these operators are implemented utilizing [CANN](https://www.hiascend.com/en/software/cann)(Compute Architecture of Neural Networks). The Ascend operator facilitates accelerated operations on the NPU by making use of CANN. This acceleration effect is particularly noticeable when working with larger images, such as those with dimensions like 2048x2048, 3840x2160, 7680x4320, etc. + + +## Instructions on Thread Safety + +Our stream function is implemented by invoking the CANN operators. In the same stream, tasks are executed sequentially, while across different streams, tasks are executed in parallel. The use of event mechanisms ensures synchronization of tasks between streams, please refer to the [**Stream Management**](https://www.hiascend.com/document/detail/en/CANNCommunityEdition/600alphaX/infacldevg/aclcppdevg/aclcppdevg_000147.html) documentation for details. + + +## Example for Image Preprocessing + +In this section, you will discover how to use Ascend operators for image preprocessing, including functions below: + +- Add +- Rotate +- Flip + + +### code + +@add_toggle_cpp +@include opencv_contrib/modules/cannops/samples/image_processing.cpp +@end_toggle + +@add_toggle_python +@include opencv_contrib/modules/cannops/samples/image_processing.py +@end_toggle + +### Explanation + +**Input Image** + +@add_toggle_cpp +@snippet opencv_contrib/modules/cannops/samples/image_processing.cpp input_noise +@end_toggle + +@add_toggle_python + +```python +# Read the input image +img = cv2.imread("/path/to/img") +# Generate gauss noise that will be added into the input image +gaussNoise = np.random.normal(mean=0,sigma=25,(img.shape[0],img.shape[1],img.shape[2])).astype(img.dtype) +``` + +@end_toggle + +**Setup CANN** + +@add_toggle_cpp + +@snippet opencv_contrib/modules/cannops/samples/image_processing.cpp setup + +@end_toggle + +@add_toggle_python + +@snippet opencv_contrib/modules/cannops/samples/image_processing.py setup + +@end_toggle +**Image Preprocessing Example** + +@add_toggle_cpp + +@snippet opencv_contrib/modules/cannops/samples/image_processing.cpp image-process + +@end_toggle + +@add_toggle_python + +@snippet opencv_contrib/modules/cannops/samples/image_processing.py image-process + +@end_toggle + +**Tear down CANN** + +@add_toggle_cpp +@snippet opencv_contrib/modules/cannops/samples/image_processing.cpp tear-down-cann + +@end_toggle + +@add_toggle_python + +@snippet opencv_contrib/modules/cannops/samples/image_processing.py tear-down-cann + +@end_toggle +Results + +1. The original RGB input image with dimensions of (480, 640, 3): + + ![puppy](./puppy.jpg) + +2. After introducing Gaussian noise, we obtain the following result: + + ![puppy_noisy](./puppy_noisy.jpg) + +3. When applying the rotate operation with a rotation code of 0 (90 degrees clockwise), we obtain this result: + + ![puppy_noisy_rotate](./puppy_noisy_rotate.jpg) + +4. Upon applying the flip operation with a flip code of 0 (flipping around the x-axis), we achieve the final result: + + ![puppy_processed_normalized](./puppy_processed.jpg) + + + +## Usage Limitations + +While Ascend supports most commonly used operators, there are still some limitations that need to be addressed. + +- There is no strict limit on the size of the input image used for encoding; however, it depends on the available RAM size of your device. +- Please note that not all data types (dtypes) are supported by every operator. The current dtype limitations are outlined in the following table. We are actively working on addressing these limitations through automatic dtype conversion in an upcoming commit. + + +| Operator | Supported Dtype | +| ---------------------- | ------------------------------------------------------------ | +| multiply (with scale) | float16,float32,int32 | +| divide (with scale) | float16,float,int32,int8,uint8 | +| bitwise add/or/xor/not | int32,int16,uint16 | +| flip | float16,float,int64,int32,int16,uint16 | +| transpose | float16,float,int64,int32,int16,int8,uint64,uint32,uint16,uint8,bool | +| rotate | float16,float,int64,int32,int16,uint16 | diff --git a/modules/cannops/tutorials/puppy.jpg b/modules/cannops/tutorials/puppy.jpg new file mode 100644 index 0000000000..b0f0595e5c Binary files /dev/null and b/modules/cannops/tutorials/puppy.jpg differ diff --git a/modules/cannops/tutorials/puppy_noisy.jpg b/modules/cannops/tutorials/puppy_noisy.jpg new file mode 100644 index 0000000000..e90cadb172 Binary files /dev/null and b/modules/cannops/tutorials/puppy_noisy.jpg differ diff --git a/modules/cannops/tutorials/puppy_noisy_rotate.jpg b/modules/cannops/tutorials/puppy_noisy_rotate.jpg new file mode 100644 index 0000000000..e62b04834d Binary files /dev/null and b/modules/cannops/tutorials/puppy_noisy_rotate.jpg differ diff --git a/modules/cannops/tutorials/puppy_processed.jpg b/modules/cannops/tutorials/puppy_processed.jpg new file mode 100644 index 0000000000..296b47aefe Binary files /dev/null and b/modules/cannops/tutorials/puppy_processed.jpg differ