COMPMID-378 NEON/CL Sobel 3x3 and 5x5 tests.

Change-Id: Ic90edd77c7f694f95663d8163623db3837a48616
Reviewed-on: http://mpd-gerrit.cambridge.arm.com/78284
Tested-by: Kaizen <jeremy.johnson+kaizengerrit@arm.com>
Reviewed-by: Moritz Pflanzer <moritz.pflanzer@arm.com>
diff --git a/tests/validation/CL/CMakeLists.txt b/tests/validation/CL/CMakeLists.txt
index 4144ec9..9b67d1d 100644
--- a/tests/validation/CL/CMakeLists.txt
+++ b/tests/validation/CL/CMakeLists.txt
@@ -29,7 +29,9 @@
     ${CMAKE_CURRENT_SOURCE_DIR}/CLFixture.h
     ${CMAKE_CURRENT_SOURCE_DIR}/CLFixture.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/BitwiseAnd.cpp
-`   ${CMAKE_CURRENT_SOURCE_DIR}/IntegralImage.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/IntegralImage.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/Sobel3x3.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/Sobel5x5.cpp
 )
 
 add_library(arm_compute_test_validation_OPENCL OBJECT
diff --git a/tests/validation/CL/Sobel3x3.cpp b/tests/validation/CL/Sobel3x3.cpp
new file mode 100644
index 0000000..382e296
--- /dev/null
+++ b/tests/validation/CL/Sobel3x3.cpp
@@ -0,0 +1,216 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "CL/CLAccessor.h"
+#include "CL/Helper.h"
+#include "Globals.h"
+#include "PaddingCalculator.h"
+#include "TensorLibrary.h"
+#include "TypePrinter.h"
+#include "Utils.h"
+#include "validation/Datasets.h"
+#include "validation/Reference.h"
+#include "validation/Validation.h"
+#include "validation/ValidationUserConfiguration.h"
+
+#include "arm_compute/core/Helpers.h"
+#include "arm_compute/core/Types.h"
+#include "arm_compute/runtime/CL/CLSubTensor.h"
+#include "arm_compute/runtime/CL/CLTensor.h"
+#include "arm_compute/runtime/CL/CLTensorAllocator.h"
+#include "arm_compute/runtime/CL/functions/CLSobel3x3.h"
+
+#include "boost_wrapper.h"
+
+#include <random>
+#include <string>
+
+using namespace arm_compute;
+using namespace arm_compute::test;
+using namespace arm_compute::test::cl;
+using namespace arm_compute::test::validation;
+
+namespace
+{
+/** Compute CL Sobel 3x3 function.
+ *
+ * @param[in] shape                 Shape of the input and output tensors.
+ * @param[in] border_mode           BorderMode used by the input tensor
+ * @param[in] constant_border_value Constant to use if @p border_mode == CONSTANT
+ *
+ * @return Computed output tensor.
+ */
+std::pair<CLTensor, CLTensor> compute_sobel_3x3(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value)
+{
+    // Create tensors
+    CLTensor src   = create_tensor(shape, DataType::U8);
+    CLTensor dst_x = create_tensor(shape, DataType::S16);
+    CLTensor dst_y = create_tensor(shape, DataType::S16);
+
+    src.info()->set_format(Format::U8);
+    dst_x.info()->set_format(Format::S16);
+    dst_y.info()->set_format(Format::S16);
+
+    // Create sobel image configure function
+    CLSobel3x3 sobel_3x3;
+    sobel_3x3.configure(&src, &dst_x, &dst_y, border_mode, constant_border_value);
+
+    // Allocate tensors
+    src.allocator()->allocate();
+    dst_x.allocator()->allocate();
+    dst_y.allocator()->allocate();
+
+    BOOST_TEST(!src.info()->is_resizable());
+    BOOST_TEST(!dst_x.info()->is_resizable());
+    BOOST_TEST(!dst_y.info()->is_resizable());
+
+    // Fill tensors
+    library->fill_tensor_uniform(CLAccessor(src), 0);
+
+    // Compute function
+    sobel_3x3.run();
+
+    return std::make_pair(std::move(dst_x), std::move(dst_y));
+}
+} // namespace
+
+#ifndef DOXYGEN_SKIP_THIS
+BOOST_AUTO_TEST_SUITE(CL)
+BOOST_AUTO_TEST_SUITE(Sobel3x3)
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly"))
+BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * BorderModes(), shape, border_mode)
+{
+    // Create tensors
+    CLTensor src   = create_tensor(shape, DataType::U8);
+    CLTensor dst_x = create_tensor(shape, DataType::S16);
+    CLTensor dst_y = create_tensor(shape, DataType::S16);
+
+    src.info()->set_format(Format::U8);
+    dst_x.info()->set_format(Format::S16);
+    dst_y.info()->set_format(Format::S16);
+
+    BOOST_TEST(src.info()->is_resizable());
+    BOOST_TEST(dst_x.info()->is_resizable());
+    BOOST_TEST(dst_y.info()->is_resizable());
+
+    // Create sobel 3x3 configure function
+    CLSobel3x3 sobel_3x3;
+    sobel_3x3.configure(&src, &dst_x, &dst_y, border_mode);
+
+    // Validate valid region
+    const ValidRegion src_valid_region = shape_to_valid_region(shape);
+    ValidRegion       dst_valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1));
+    }
+
+    validate(src.info()->valid_region(), src_valid_region);
+    validate(dst_x.info()->valid_region(), dst_valid_region);
+    validate(dst_y.info()->valid_region(), dst_valid_region);
+
+    // Validate padding
+    PaddingCalculator calculator(shape.x(), 8);
+
+    calculator.set_border_mode(border_mode);
+    calculator.set_border_size(1);
+
+    const PaddingSize dst_padding = calculator.required_padding();
+
+    calculator.set_accessed_elements(16);
+    calculator.set_access_offset(-1);
+
+    const PaddingSize src_padding = calculator.required_padding();
+
+    validate(src.info()->padding(), src_padding);
+    validate(dst_x.info()->padding(), dst_padding);
+    validate(dst_y.info()->padding(), dst_padding);
+}
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit"))
+BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * BorderModes(), shape, border_mode)
+{
+    uint8_t constant_border_value = 0;
+
+    // Generate a random constant value if border_mode is constant
+    if(border_mode == BorderMode::CONSTANT)
+    {
+        std::mt19937                           gen(user_config.seed.get());
+        std::uniform_int_distribution<uint8_t> distribution(0, 255);
+        constant_border_value = distribution(gen);
+    }
+
+    // Compute function
+    std::pair<CLTensor, CLTensor> dst = compute_sobel_3x3(shape, border_mode, constant_border_value);
+
+    // Compute reference
+    std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_3x3(shape, border_mode, constant_border_value);
+
+    // Calculate valid region
+    ValidRegion valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1));
+    }
+
+    // Validate output
+    validate(CLAccessor(dst.first), ref_dst.first, valid_region);
+    validate(CLAccessor(dst.second), ref_dst.second, valid_region);
+}
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly"))
+BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * BorderModes(), shape, border_mode)
+{
+    uint8_t constant_border_value = 0;
+
+    // Generate a random constant value if border_mode is constant
+    if(border_mode == BorderMode::CONSTANT)
+    {
+        std::mt19937                           gen(user_config.seed.get());
+        std::uniform_int_distribution<uint8_t> distribution(0, 255);
+        constant_border_value = distribution(gen);
+    }
+
+    // Compute function
+    std::pair<CLTensor, CLTensor> dst = compute_sobel_3x3(shape, border_mode, constant_border_value);
+
+    // Compute reference
+    std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_3x3(shape, border_mode, constant_border_value);
+
+    // Calculate valid region
+    ValidRegion valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1));
+    }
+
+    // Validate output
+    validate(CLAccessor(dst.first), ref_dst.first, valid_region);
+    validate(CLAccessor(dst.second), ref_dst.second, valid_region);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/tests/validation/CL/Sobel5x5.cpp b/tests/validation/CL/Sobel5x5.cpp
new file mode 100644
index 0000000..b653808
--- /dev/null
+++ b/tests/validation/CL/Sobel5x5.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "CL/CLAccessor.h"
+#include "CL/Helper.h"
+#include "Globals.h"
+#include "PaddingCalculator.h"
+#include "TensorLibrary.h"
+#include "TypePrinter.h"
+#include "Utils.h"
+#include "validation/Datasets.h"
+#include "validation/Reference.h"
+#include "validation/Validation.h"
+#include "validation/ValidationUserConfiguration.h"
+
+#include "arm_compute/core/Helpers.h"
+#include "arm_compute/core/Types.h"
+#include "arm_compute/runtime/CL/CLSubTensor.h"
+#include "arm_compute/runtime/CL/CLTensor.h"
+#include "arm_compute/runtime/CL/CLTensorAllocator.h"
+#include "arm_compute/runtime/CL/functions/CLSobel5x5.h"
+
+#include "boost_wrapper.h"
+
+#include <random>
+#include <string>
+
+using namespace arm_compute;
+using namespace arm_compute::test;
+using namespace arm_compute::test::cl;
+using namespace arm_compute::test::validation;
+
+namespace
+{
+/** Compute CL Sobel 5x5 function.
+ *
+ * @param[in] shape                 Shape of the input and output tensors.
+ * @param[in] border_mode           BorderMode used by the input tensor
+ * @param[in] constant_border_value Constant to use if @p border_mode == CONSTANT
+ *
+ * @return Computed output tensor.
+ */
+std::pair<CLTensor, CLTensor> compute_sobel_5x5(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value)
+{
+    // Create tensors
+    CLTensor src   = create_tensor(shape, DataType::U8);
+    CLTensor dst_x = create_tensor(shape, DataType::S16);
+    CLTensor dst_y = create_tensor(shape, DataType::S16);
+
+    src.info()->set_format(Format::U8);
+    dst_x.info()->set_format(Format::S16);
+    dst_y.info()->set_format(Format::S16);
+
+    // Create sobel image configure function
+    CLSobel5x5 sobel_5x5;
+    sobel_5x5.configure(&src, &dst_x, &dst_y, border_mode, constant_border_value);
+
+    // Allocate tensors
+    src.allocator()->allocate();
+    dst_x.allocator()->allocate();
+    dst_y.allocator()->allocate();
+
+    BOOST_TEST(!src.info()->is_resizable());
+    BOOST_TEST(!dst_x.info()->is_resizable());
+    BOOST_TEST(!dst_y.info()->is_resizable());
+
+    // Fill tensors
+    library->fill_tensor_uniform(CLAccessor(src), 0);
+
+    // Compute function
+    sobel_5x5.run();
+
+    return std::make_pair(std::move(dst_x), std::move(dst_y));
+}
+} // namespace
+
+#ifndef DOXYGEN_SKIP_THIS
+BOOST_AUTO_TEST_SUITE(CL)
+BOOST_AUTO_TEST_SUITE(Sobel5x5)
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly"))
+BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * BorderModes(), shape, border_mode)
+{
+    // Create tensors
+    CLTensor src   = create_tensor(shape, DataType::U8);
+    CLTensor dst_x = create_tensor(shape, DataType::S16);
+    CLTensor dst_y = create_tensor(shape, DataType::S16);
+
+    src.info()->set_format(Format::U8);
+    dst_x.info()->set_format(Format::S16);
+    dst_y.info()->set_format(Format::S16);
+
+    BOOST_TEST(src.info()->is_resizable());
+    BOOST_TEST(dst_x.info()->is_resizable());
+    BOOST_TEST(dst_y.info()->is_resizable());
+
+    // Create sobel 5x5 configure function
+    CLSobel5x5 sobel_5x5;
+    sobel_5x5.configure(&src, &dst_x, &dst_y, border_mode);
+
+    // Validate valid region
+    const ValidRegion src_valid_region = shape_to_valid_region(shape);
+    ValidRegion       dst_valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2));
+    }
+
+    validate(src.info()->valid_region(), src_valid_region);
+    validate(dst_x.info()->valid_region(), dst_valid_region);
+    validate(dst_y.info()->valid_region(), dst_valid_region);
+
+    // Validate padding
+    PaddingCalculator calculator(shape.x(), 8);
+    calculator.set_border_mode(border_mode);
+    calculator.set_border_size(2);
+
+    const PaddingSize dst_padding = calculator.required_padding();
+
+    calculator.set_accessed_elements(16);
+    calculator.set_access_offset(-2);
+
+    const PaddingSize src_padding = calculator.required_padding();
+
+    validate(src.info()->padding(), src_padding);
+    validate(dst_x.info()->padding(), dst_padding);
+    validate(dst_y.info()->padding(), dst_padding);
+}
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit"))
+BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * BorderModes(), shape, border_mode)
+{
+    uint8_t constant_border_value = 0;
+
+    // Generate a random constant value if border_mode is constant
+    if(border_mode == BorderMode::CONSTANT)
+    {
+        std::mt19937                           gen(user_config.seed.get());
+        std::uniform_int_distribution<uint8_t> distribution(0, 255);
+        constant_border_value = distribution(gen);
+    }
+
+    // Compute function
+    std::pair<CLTensor, CLTensor> dst = compute_sobel_5x5(shape, border_mode, constant_border_value);
+
+    // Compute reference
+    std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_5x5(shape, border_mode, constant_border_value);
+
+    // Calculate valid region
+    ValidRegion valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2));
+    }
+
+    // Validate output
+    validate(CLAccessor(dst.first), ref_dst.first, valid_region);
+    validate(CLAccessor(dst.second), ref_dst.second, valid_region);
+}
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly"))
+BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * BorderModes(), shape, border_mode)
+{
+    uint8_t constant_border_value = 0;
+
+    // Generate a random constant value if border_mode is constant
+    if(border_mode == BorderMode::CONSTANT)
+    {
+        std::mt19937                           gen(user_config.seed.get());
+        std::uniform_int_distribution<uint8_t> distribution(0, 255);
+        constant_border_value = distribution(gen);
+    }
+
+    // Compute function
+    std::pair<CLTensor, CLTensor> dst = compute_sobel_5x5(shape, border_mode, constant_border_value);
+
+    // Compute reference
+    std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_5x5(shape, border_mode, constant_border_value);
+
+    // Calculate valid region
+    ValidRegion valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2));
+    }
+
+    // Validate output
+    validate(CLAccessor(dst.first), ref_dst.first, valid_region);
+    validate(CLAccessor(dst.second), ref_dst.second, valid_region);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/tests/validation/NEON/CMakeLists.txt b/tests/validation/NEON/CMakeLists.txt
index a197f8c..86bcce7 100644
--- a/tests/validation/NEON/CMakeLists.txt
+++ b/tests/validation/NEON/CMakeLists.txt
@@ -43,6 +43,8 @@
     ${CMAKE_CURRENT_SOURCE_DIR}/PixelWiseMultiplication.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/IntegralImage.cpp
     ${CMAKE_CURRENT_SOURCE_DIR}/MeanStdDev.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/Sobel3x3.cpp
+    ${CMAKE_CURRENT_SOURCE_DIR}/Sobel5x5.cpp
 )
 
 add_library(arm_compute_test_validation_NEON OBJECT
diff --git a/tests/validation/NEON/Sobel3x3.cpp b/tests/validation/NEON/Sobel3x3.cpp
new file mode 100644
index 0000000..8324614
--- /dev/null
+++ b/tests/validation/NEON/Sobel3x3.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "Globals.h"
+#include "NEON/Helper.h"
+#include "NEON/NEAccessor.h"
+#include "TensorLibrary.h"
+#include "TypePrinter.h"
+#include "Utils.h"
+#include "validation/Datasets.h"
+#include "validation/Reference.h"
+#include "validation/Validation.h"
+#include "validation/ValidationUserConfiguration.h"
+
+#include "arm_compute/core/Helpers.h"
+#include "arm_compute/core/Types.h"
+#include "arm_compute/runtime/NEON/functions/NESobel3x3.h"
+#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/runtime/TensorAllocator.h"
+
+#include "PaddingCalculator.h"
+#include "boost_wrapper.h"
+
+#include <random>
+#include <string>
+
+using namespace arm_compute;
+using namespace arm_compute::test;
+using namespace arm_compute::test::neon;
+using namespace arm_compute::test::validation;
+
+namespace
+{
+/** Compute Neon Sobel 3x3 function.
+ *
+ * @param[in] shape                 Shape of the input and output tensors.
+ * @param[in] border_mode           BorderMode used by the input tensor
+ * @param[in] constant_border_value Constant to use if @p border_mode == CONSTANT
+ *
+ * @return Computed output tensor.
+ */
+std::pair<Tensor, Tensor> compute_sobel_3x3(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value)
+{
+    // Create tensors
+    Tensor src   = create_tensor(shape, DataType::U8);
+    Tensor dst_x = create_tensor(shape, DataType::S16);
+    Tensor dst_y = create_tensor(shape, DataType::S16);
+
+    src.info()->set_format(Format::U8);
+    dst_x.info()->set_format(Format::S16);
+    dst_y.info()->set_format(Format::S16);
+
+    // Create sobel image configure function
+    NESobel3x3 sobel_3x3;
+    sobel_3x3.configure(&src, &dst_x, &dst_y, border_mode, constant_border_value);
+
+    // Allocate tensors
+    src.allocator()->allocate();
+    dst_x.allocator()->allocate();
+    dst_y.allocator()->allocate();
+
+    BOOST_TEST(!src.info()->is_resizable());
+    BOOST_TEST(!dst_x.info()->is_resizable());
+    BOOST_TEST(!dst_y.info()->is_resizable());
+
+    // Fill tensors
+    library->fill_tensor_uniform(NEAccessor(src), 0);
+
+    // Compute function
+    sobel_3x3.run();
+
+    return std::make_pair(std::move(dst_x), std::move(dst_y));
+}
+} // namespace
+
+#ifndef DOXYGEN_SKIP_THIS
+BOOST_AUTO_TEST_SUITE(NEON)
+BOOST_AUTO_TEST_SUITE(Sobel3x3)
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly"))
+BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * BorderModes(), shape, border_mode)
+{
+    // Create tensors
+    Tensor src   = create_tensor(shape, DataType::U8);
+    Tensor dst_x = create_tensor(shape, DataType::S16);
+    Tensor dst_y = create_tensor(shape, DataType::S16);
+
+    src.info()->set_format(Format::U8);
+    dst_x.info()->set_format(Format::S16);
+    dst_y.info()->set_format(Format::S16);
+
+    BOOST_TEST(src.info()->is_resizable());
+    BOOST_TEST(dst_x.info()->is_resizable());
+    BOOST_TEST(dst_y.info()->is_resizable());
+
+    // Create sobel 3x3 configure function
+    NESobel3x3 sobel_3x3;
+    sobel_3x3.configure(&src, &dst_x, &dst_y, border_mode);
+
+    // Validate valid region
+    const ValidRegion src_valid_region = shape_to_valid_region(shape);
+    ValidRegion       dst_valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1));
+    }
+
+    validate(src.info()->valid_region(), src_valid_region);
+    validate(dst_x.info()->valid_region(), dst_valid_region);
+    validate(dst_y.info()->valid_region(), dst_valid_region);
+
+    // Validate padding
+    PaddingCalculator calculator(shape.x(), 8);
+
+    calculator.set_border_mode(border_mode);
+    calculator.set_border_size(1);
+
+    const PaddingSize dst_padding = calculator.required_padding();
+
+    calculator.set_accessed_elements(16);
+    calculator.set_access_offset(-1);
+
+    const PaddingSize src_padding = calculator.required_padding();
+
+    validate(src.info()->padding(), src_padding);
+    validate(dst_x.info()->padding(), dst_padding);
+    validate(dst_y.info()->padding(), dst_padding);
+}
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit"))
+BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * BorderModes(), shape, border_mode)
+{
+    uint8_t constant_border_value = 0;
+
+    // Generate a random constant value if border_mode is constant
+    if(border_mode == BorderMode::CONSTANT)
+    {
+        std::mt19937                           gen(user_config.seed.get());
+        std::uniform_int_distribution<uint8_t> distribution(0, 255);
+        constant_border_value = distribution(gen);
+    }
+
+    // Compute function
+    std::pair<Tensor, Tensor> dst = compute_sobel_3x3(shape, border_mode, constant_border_value);
+
+    // Compute reference
+    std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_3x3(shape, border_mode, constant_border_value);
+
+    // Calculate valid region
+    ValidRegion valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1));
+    }
+
+    // Validate output
+    validate(NEAccessor(dst.first), ref_dst.first, valid_region);
+    validate(NEAccessor(dst.second), ref_dst.second, valid_region);
+}
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly"))
+BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * BorderModes(), shape, border_mode)
+{
+    uint8_t constant_border_value = 0;
+
+    // Generate a random constant value if border_mode is constant
+    if(border_mode == BorderMode::CONSTANT)
+    {
+        std::mt19937                           gen(user_config.seed.get());
+        std::uniform_int_distribution<uint8_t> distribution(0, 255);
+        constant_border_value = distribution(gen);
+    }
+
+    // Compute function
+    std::pair<Tensor, Tensor> dst = compute_sobel_3x3(shape, border_mode, constant_border_value);
+
+    // Compute reference
+    std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_3x3(shape, border_mode, constant_border_value);
+
+    // Calculate valid region
+    ValidRegion valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(1));
+    }
+
+    // Validate output
+    validate(NEAccessor(dst.first), ref_dst.first, valid_region);
+    validate(NEAccessor(dst.second), ref_dst.second, valid_region);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/tests/validation/NEON/Sobel5x5.cpp b/tests/validation/NEON/Sobel5x5.cpp
new file mode 100644
index 0000000..bb2d7ba
--- /dev/null
+++ b/tests/validation/NEON/Sobel5x5.cpp
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2017 ARM Limited.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to
+ * deal in the Software without restriction, including without limitation the
+ * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "Globals.h"
+#include "NEON/Helper.h"
+#include "NEON/NEAccessor.h"
+#include "PaddingCalculator.h"
+#include "TensorLibrary.h"
+#include "TypePrinter.h"
+#include "Utils.h"
+#include "validation/Datasets.h"
+#include "validation/Reference.h"
+#include "validation/Validation.h"
+#include "validation/ValidationUserConfiguration.h"
+
+#include "arm_compute/core/Helpers.h"
+#include "arm_compute/core/Types.h"
+#include "arm_compute/runtime/NEON/functions/NESobel5x5.h"
+#include "arm_compute/runtime/Tensor.h"
+#include "arm_compute/runtime/TensorAllocator.h"
+
+#include "boost_wrapper.h"
+
+#include <random>
+#include <string>
+
+using namespace arm_compute;
+using namespace arm_compute::test;
+using namespace arm_compute::test::neon;
+using namespace arm_compute::test::validation;
+
+namespace
+{
+/** Compute Neon Sobel 5x5 function.
+ *
+ * @param[in] shape                 Shape of the input and output tensors.
+ * @param[in] border_mode           BorderMode used by the input tensor
+ * @param[in] constant_border_value Constant to use if @p border_mode == CONSTANT
+ *
+ * @return Computed output tensor.
+ */
+std::pair<Tensor, Tensor> compute_sobel_5x5(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value)
+{
+    // Create tensors
+    Tensor src   = create_tensor(shape, DataType::U8);
+    Tensor dst_x = create_tensor(shape, DataType::S16);
+    Tensor dst_y = create_tensor(shape, DataType::S16);
+
+    src.info()->set_format(Format::U8);
+    dst_x.info()->set_format(Format::S16);
+    dst_y.info()->set_format(Format::S16);
+
+    // Create sobel image configure function
+    NESobel5x5 sobel_5x5;
+    sobel_5x5.configure(&src, &dst_x, &dst_y, border_mode, constant_border_value);
+
+    // Allocate tensors
+    src.allocator()->allocate();
+    dst_x.allocator()->allocate();
+    dst_y.allocator()->allocate();
+
+    BOOST_TEST(!src.info()->is_resizable());
+    BOOST_TEST(!dst_x.info()->is_resizable());
+    BOOST_TEST(!dst_y.info()->is_resizable());
+
+    // Fill tensors
+    library->fill_tensor_uniform(NEAccessor(src), 0);
+
+    // Compute function
+    sobel_5x5.run();
+
+    return std::make_pair(std::move(dst_x), std::move(dst_y));
+}
+} // namespace
+
+#ifndef DOXYGEN_SKIP_THIS
+BOOST_AUTO_TEST_SUITE(NEON)
+BOOST_AUTO_TEST_SUITE(Sobel5x5)
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit") * boost::unit_test::label("nightly"))
+BOOST_DATA_TEST_CASE(Configuration, (SmallShapes() + LargeShapes()) * BorderModes(), shape, border_mode)
+{
+    // Create tensors
+    Tensor src   = create_tensor(shape, DataType::U8);
+    Tensor dst_x = create_tensor(shape, DataType::S16);
+    Tensor dst_y = create_tensor(shape, DataType::S16);
+
+    src.info()->set_format(Format::U8);
+    dst_x.info()->set_format(Format::S16);
+    dst_y.info()->set_format(Format::S16);
+
+    BOOST_TEST(src.info()->is_resizable());
+    BOOST_TEST(dst_x.info()->is_resizable());
+    BOOST_TEST(dst_y.info()->is_resizable());
+
+    // Create sobel 5x5 configure function
+    NESobel5x5 sobel_5x5;
+    sobel_5x5.configure(&src, &dst_x, &dst_y, border_mode);
+
+    // Validate valid region
+    const ValidRegion src_valid_region = shape_to_valid_region(shape);
+    ValidRegion       dst_valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        dst_valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2));
+    }
+
+    validate(src.info()->valid_region(), src_valid_region);
+    validate(dst_x.info()->valid_region(), dst_valid_region);
+    validate(dst_y.info()->valid_region(), dst_valid_region);
+
+    // Validate padding
+    PaddingCalculator calculator(shape.x(), 16);
+
+    calculator.set_border_mode(border_mode);
+    calculator.set_border_size(2);
+
+    const PaddingSize dst_padding = calculator.required_padding();
+
+    calculator.set_processed_elements(8);
+    calculator.set_access_offset(-2);
+
+    const PaddingSize src_padding = calculator.required_padding();
+
+    validate(src.info()->padding(), src_padding);
+    validate(dst_x.info()->padding(), dst_padding);
+    validate(dst_y.info()->padding(), dst_padding);
+}
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("precommit"))
+BOOST_DATA_TEST_CASE(RunSmall, SmallShapes() * BorderModes(), shape, border_mode)
+{
+    uint8_t constant_border_value = 0;
+
+    // Generate a random constant value if border_mode is constant
+    if(border_mode == BorderMode::CONSTANT)
+    {
+        std::mt19937                           gen(user_config.seed.get());
+        std::uniform_int_distribution<uint8_t> distribution(0, 255);
+        constant_border_value = distribution(gen);
+    }
+
+    // Compute function
+    std::pair<Tensor, Tensor> dst = compute_sobel_5x5(shape, border_mode, constant_border_value);
+
+    // Compute reference
+    std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_5x5(shape, border_mode, constant_border_value);
+
+    // Calculate valid region
+    ValidRegion valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2));
+    }
+
+    // Validate output
+    validate(NEAccessor(dst.first), ref_dst.first, valid_region);
+    validate(NEAccessor(dst.second), ref_dst.second, valid_region);
+}
+
+BOOST_TEST_DECORATOR(*boost::unit_test::label("nightly"))
+BOOST_DATA_TEST_CASE(RunLarge, LargeShapes() * BorderModes(), shape, border_mode)
+{
+    uint8_t constant_border_value = 0;
+
+    // Generate a random constant value if border_mode is constant
+    if(border_mode == BorderMode::CONSTANT)
+    {
+        std::mt19937                           gen(user_config.seed.get());
+        std::uniform_int_distribution<uint8_t> distribution(0, 255);
+        constant_border_value = distribution(gen);
+    }
+
+    // Compute function
+    std::pair<Tensor, Tensor> dst = compute_sobel_5x5(shape, border_mode, constant_border_value);
+
+    // Compute reference
+    std::pair<RawTensor, RawTensor> ref_dst = Reference::compute_reference_sobel_5x5(shape, border_mode, constant_border_value);
+
+    // Calculate valid region
+    ValidRegion valid_region = shape_to_valid_region(shape);
+    if(border_mode == BorderMode::UNDEFINED)
+    {
+        valid_region = shape_to_valid_region_undefined_border(shape, BorderSize(2));
+    }
+
+    // Validate output
+    validate(NEAccessor(dst.first), ref_dst.first, valid_region);
+    validate(NEAccessor(dst.second), ref_dst.second, valid_region);
+}
+
+BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE_END()
+#endif
diff --git a/tests/validation/Reference.cpp b/tests/validation/Reference.cpp
index 5f49cae..be3f28b 100644
--- a/tests/validation/Reference.cpp
+++ b/tests/validation/Reference.cpp
@@ -39,6 +39,36 @@
 {
 namespace validation
 {
+std::pair<RawTensor, RawTensor> Reference::compute_reference_sobel_3x3(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value)
+{
+    // Create reference
+    RawTensor ref_src   = library->get(shape, Format::U8);
+    RawTensor ref_dst_x = library->get(shape, Format::S16);
+    RawTensor ref_dst_y = library->get(shape, Format::S16);
+
+    // Fill reference
+    library->fill_tensor_uniform(ref_src, 0);
+
+    // Compute reference
+    ReferenceCPP::sobel_3x3(ref_src, ref_dst_x, ref_dst_y, border_mode, constant_border_value);
+
+    return std::make_pair(ref_dst_x, ref_dst_y);
+}
+std::pair<RawTensor, RawTensor> Reference::compute_reference_sobel_5x5(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value)
+{
+    // Create reference
+    RawTensor ref_src   = library->get(shape, Format::U8);
+    RawTensor ref_dst_x = library->get(shape, Format::S16);
+    RawTensor ref_dst_y = library->get(shape, Format::S16);
+
+    // Fill reference
+    library->fill_tensor_uniform(ref_src, 0);
+
+    // Compute reference
+    ReferenceCPP::sobel_5x5(ref_src, ref_dst_x, ref_dst_y, border_mode, constant_border_value);
+
+    return std::make_pair(ref_dst_x, ref_dst_y);
+}
 std::pair<float, float> Reference::compute_reference_mean_and_standard_deviation(const TensorShape &shape)
 {
     // Create reference
diff --git a/tests/validation/Reference.h b/tests/validation/Reference.h
index 06a38c4..4cfd3c2 100644
--- a/tests/validation/Reference.h
+++ b/tests/validation/Reference.h
@@ -37,6 +37,24 @@
 class Reference
 {
 public:
+    /** Compute reference sobel 3x3.
+     *
+     * @param[in] shape                 Shape of the input and output tensors.
+     * @param[in] border_mode           Border mode to use for input tensor
+     * @param[in] constant_border_value Constant value to use if @p border_mode is constant
+     *
+     * @return Computed raw tensors along x and y axis.
+     */
+    static std::pair<RawTensor, RawTensor> compute_reference_sobel_3x3(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value);
+    /** Compute reference sobel 5x5.
+     *
+     * @param[in] shape                 Shape of the input and output tensors.
+     * @param[in] border_mode           Border mode to use for input tensor
+     * @param[in] constant_border_value Constant value to use if @p border_mode is constant
+     *
+     * @return Computed raw tensors along x and y axis.
+     */
+    static std::pair<RawTensor, RawTensor> compute_reference_sobel_5x5(const TensorShape &shape, BorderMode border_mode, uint8_t constant_border_value);
     /** Compute reference mean and standard deviation.
      *
      * @param[in] shape Shape of the input tensors.
diff --git a/tests/validation/ReferenceCPP.cpp b/tests/validation/ReferenceCPP.cpp
index 5c429ea..ca6bb6c 100644
--- a/tests/validation/ReferenceCPP.cpp
+++ b/tests/validation/ReferenceCPP.cpp
@@ -48,6 +48,26 @@
 {
 namespace validation
 {
+// Sobel 3x3
+void ReferenceCPP::sobel_3x3(RawTensor &src, RawTensor &dst_x, RawTensor &dst_y, BorderMode border_mode, uint8_t constant_border_value)
+{
+    ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst_x.data_type() != DataType::S16 || dst_y.data_type() != DataType::S16);
+    Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data()));
+    Tensor<int16_t> dx(dst_x.shape(), dst_x.data_type(), dst_x.fixed_point_position(), reinterpret_cast<int16_t *>(dst_x.data()));
+    Tensor<int16_t> dy(dst_y.shape(), dst_y.data_type(), dst_y.fixed_point_position(), reinterpret_cast<int16_t *>(dst_y.data()));
+    tensor_operations::sobel_3x3(s, dx, dy, border_mode, constant_border_value);
+}
+
+// Sobel 5x5
+void ReferenceCPP::sobel_5x5(RawTensor &src, RawTensor &dst_x, RawTensor &dst_y, BorderMode border_mode, uint8_t constant_border_value)
+{
+    ARM_COMPUTE_ERROR_ON(src.data_type() != DataType::U8 || dst_x.data_type() != DataType::S16 || dst_y.data_type() != DataType::S16);
+    Tensor<uint8_t> s(src.shape(), src.data_type(), src.fixed_point_position(), reinterpret_cast<const uint8_t *>(src.data()));
+    Tensor<int16_t> dx(dst_x.shape(), dst_x.data_type(), dst_x.fixed_point_position(), reinterpret_cast<int16_t *>(dst_x.data()));
+    Tensor<int16_t> dy(dst_y.shape(), dst_y.data_type(), dst_y.fixed_point_position(), reinterpret_cast<int16_t *>(dst_y.data()));
+    tensor_operations::sobel_5x5(s, dx, dy, border_mode, constant_border_value);
+}
+
 // Absolute difference
 void ReferenceCPP::absolute_difference(const RawTensor &src1, const RawTensor &src2, RawTensor &dst)
 {
diff --git a/tests/validation/ReferenceCPP.h b/tests/validation/ReferenceCPP.h
index 4173b8b..b5e3fa0 100644
--- a/tests/validation/ReferenceCPP.h
+++ b/tests/validation/ReferenceCPP.h
@@ -42,6 +42,26 @@
 class ReferenceCPP final : public Reference
 {
 public:
+    /** Function to compute reference sobel 3x3.
+     *
+     * @param[in] src                   Input tensor.
+     * @param[in] dst_x                 Result tensor along x axis
+     * @param[in] dst_y                 Result tensor along y axis
+     * @param[in] border_mode           Border mode to use for input tensor
+     * @param[in] constant_border_value Constant value to use if @p border_mode is constant
+     *
+     */
+    static void sobel_3x3(RawTensor &src, RawTensor &dst_x, RawTensor &dst_y, BorderMode border_mode, uint8_t constant_border_value);
+    /** Function to compute reference sobel 5x5.
+     *
+     * @param[in] src                   Input tensor.
+     * @param[in] dst_x                 Result tensor along x axis
+     * @param[in] dst_y                 Result tensor along y axis
+     * @param[in] border_mode           Border mode to use for input tensor
+     * @param[in] constant_border_value Constant value to use if @p border_mode is constant
+     *
+     */
+    static void sobel_5x5(RawTensor &src, RawTensor &dst_x, RawTensor &dst_y, BorderMode border_mode, uint8_t constant_border_value);
     /** Function to compute the mean and standard deviation of a tensor.
      *
      * @param[in]  src     Input tensor.
diff --git a/tests/validation/TensorOperations.h b/tests/validation/TensorOperations.h
index 56cc657..fce2575 100644
--- a/tests/validation/TensorOperations.h
+++ b/tests/validation/TensorOperations.h
@@ -34,10 +34,12 @@
 #include "arm_compute/core/FixedPoint.h"
 #include "arm_compute/core/Types.h"
 #include "tests/validation/FixedPoint.h"
+#include "tests/validation/ValidationUserConfiguration.h"
 
 #include <algorithm>
 #include <array>
 #include <cmath>
+#include <random>
 
 namespace arm_compute
 {
@@ -199,39 +201,119 @@
     }
 }
 
+template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
+T tensor_elem_at(const Tensor<T> &in, Coordinates &coord, BorderMode border_mode, T constant_border_value)
+{
+    const int x      = coord.x();
+    const int y      = coord.y();
+    const int width  = static_cast<int>(in.shape().x());
+    const int height = static_cast<int>(in.shape().y());
+
+    // If on border
+    if(x < 0 || y < 0 || x >= width || y >= height)
+    {
+        if(border_mode == BorderMode::CONSTANT)
+        {
+            return constant_border_value;
+        }
+        else if(border_mode == BorderMode::REPLICATE)
+        {
+            coord.set(0, std::max(0, std::min(x, width - 1)));
+            coord.set(1, std::max(0, std::min(y, height - 1)));
+            return in[coord2index(in.shape(), coord)];
+        }
+        else
+        {
+            // Return a random value if on border and border_mode == UNDEFINED
+            std::mt19937                     gen(user_config.seed.get());
+            std::uniform_int_distribution<T> distribution(0, 255);
+            return distribution(gen);
+        }
+    }
+    else
+    {
+        return in[coord2index(in.shape(), coord)];
+    }
+}
+
 /** Apply 2D spatial filter on a single element of @p in at coordinates @p coord
  *
  * - filter sizes have to be odd number
- * - Valid region assumed
  * - Row major order of filter assumed
  * - TO_ZERO rounding policy assumed
  * - SATURATE convert policy assumed
  *
  */
 template <typename T1, typename T2, typename T3>
-void apply_2d_spatial_filter(Coordinates coord, const Tensor<T1> &in, Tensor<T3> &out, const TensorShape &filter_shape, const T2 *filter_itr, float scale)
+void apply_2d_spatial_filter(Coordinates coord, const Tensor<T1> &in, Tensor<T3> &out, const TensorShape &filter_shape, const T2 *filter_itr, float scale, BorderMode border_mode,
+                             T1 constant_border_value = 0)
 {
-    using intermediate_type = typename common_promoted_signed_type<T1, T2, T3>::intermediate_type;
-    intermediate_type val   = 0;
-    int               x     = coord.x();
-    int               y     = coord.y();
-    for(size_t j = y - filter_shape[1] / 2; j <= y + filter_shape[1] / 2; ++j)
+    double    val = 0;
+    const int x   = coord.x();
+    const int y   = coord.y();
+    for(int j = y - static_cast<int>(filter_shape[1] / 2); j <= y + static_cast<int>(filter_shape[1] / 2); ++j)
     {
-        for(size_t i = x - filter_shape[0] / 2; i <= x + filter_shape[0] / 2; ++i)
+        for(int i = x - static_cast<int>(filter_shape[0] / 2); i <= x + static_cast<int>(filter_shape[0] / 2); ++i)
         {
             coord.set(0, i);
             coord.set(1, j);
-            val += static_cast<intermediate_type>(*filter_itr) * static_cast<intermediate_type>(in[coord2index(in.shape(), coord)]);
+            double pixel_to_multiply = tensor_elem_at(in, coord, border_mode, constant_border_value);
+            val += static_cast<double>(*filter_itr) * pixel_to_multiply;
             ++filter_itr;
         }
     }
     coord.set(0, x);
     coord.set(1, y);
-    double rounded_val = cpp11::trunc(val * static_cast<double>(scale));
+    const double rounded_val = cpp11::trunc(val * static_cast<double>(scale));
     out[coord2index(in.shape(), coord)] = saturate_cast<T3>(rounded_val);
 }
 } // namespace
 
+// Sobel 3x3
+template <typename T1, typename T2>
+void sobel_3x3(Tensor<T1> &in, Tensor<T2> &out_x, Tensor<T2> &out_y, BorderMode border_mode, uint8_t constant_border_value)
+{
+    const std::array<int8_t, 9> sobel_x{ { -1, 0, 1, -2, 0, 2, -1, 0, 1 } };
+    const std::array<int8_t, 9> sobel_y{ { -1, -2, -1, 0, 0, 0, 1, 2, 1 } };
+
+    for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx)
+    {
+        const Coordinates id = index2coord(in.shape(), element_idx);
+
+        apply_2d_spatial_filter(id, in, out_x, TensorShape(3U, 3U), sobel_x.data(), 1.f, border_mode, constant_border_value);
+        apply_2d_spatial_filter(id, in, out_y, TensorShape(3U, 3U), sobel_y.data(), 1.f, border_mode, constant_border_value);
+    }
+}
+
+// Sobel 5x5
+template <typename T1, typename T2>
+void sobel_5x5(Tensor<T1> &in, Tensor<T2> &out_x, Tensor<T2> &out_y, BorderMode border_mode, uint8_t constant_border_value)
+{
+    const std::array<int8_t, 25> sobel_x{ {
+            -1, -2, 0, 2, 1,
+            -4, -8, 0, 8, 4,
+            -6, -12, 0, 12, 6,
+            -4, -8, 0, 8, 4,
+            -1, -2, 0, 2, 1
+        } };
+
+    const std::array<int8_t, 25> sobel_y{ {
+            -1, -4, -6, -4, -1,
+            -2, -8, -12, -8, -2,
+            0, 0, 0, 0, 0,
+            2, 8, 12, 8, 2,
+            1, 4, 6, 4, 1
+        } };
+
+    for(int element_idx = 0; element_idx < in.num_elements(); ++element_idx)
+    {
+        const Coordinates id = index2coord(in.shape(), element_idx);
+
+        apply_2d_spatial_filter(id, in, out_x, TensorShape(5U, 5U), sobel_x.data(), 1.f, border_mode, constant_border_value);
+        apply_2d_spatial_filter(id, in, out_y, TensorShape(5U, 5U), sobel_y.data(), 1.f, border_mode, constant_border_value);
+    }
+}
+
 // Mean Standard Deviation
 template <typename T1>
 void mean_and_standard_deviation(const Tensor<T1> &in, float &mean, float &std_dev)
@@ -438,7 +520,7 @@
         const Coordinates id = index2coord(in.shape(), element_idx);
         if(is_in_valid_region(valid_region, id))
         {
-            apply_2d_spatial_filter(id, in, out, TensorShape(3U, 3U), filter.data(), scale);
+            apply_2d_spatial_filter(id, in, out, TensorShape(3U, 3U), filter.data(), scale, BorderMode::UNDEFINED);
         }
     }
 }