diff --git a/modules/README.md b/modules/README.md index 28070d147..cd8ea0fbe 100644 --- a/modules/README.md +++ b/modules/README.md @@ -12,8 +12,6 @@ $ cmake -D OPENCV_EXTRA_MODULES_PATH=/modules -D BUILD_opencv_ -#include - -/** @defgroup barcode Barcode detecting and decoding methods -*/ - -namespace cv { -namespace barcode { - -//! @addtogroup barcode -//! @{ - -enum BarcodeType -{ - NONE, EAN_8, EAN_13, UPC_A, UPC_E, UPC_EAN_EXTENSION -}; - -static inline std::ostream &operator<<(std::ostream &out, const BarcodeType &barcode_type) -{ - switch (barcode_type) - { - case BarcodeType::EAN_8: - out << "EAN_8"; - break; - case BarcodeType::EAN_13: - out << "EAN_13"; - break; - case BarcodeType::UPC_E: - out << "UPC_E"; - break; - case BarcodeType::UPC_A: - out << "UPC_A"; - break; - case BarcodeType::UPC_EAN_EXTENSION: - out << "UPC_EAN_EXTENSION"; - break; - default: - out << "NONE"; - } - return out; -} - -class CV_EXPORTS_W BarcodeDetector -{ -public: - /** - * @brief Initialize the BarcodeDetector. - * @param prototxt_path prototxt file path for the super resolution model - * @param model_path model file path for the super resolution model - */ - CV_WRAP BarcodeDetector(const std::string &prototxt_path = "", const std::string &model_path = ""); - - ~BarcodeDetector(); - - /** @brief Detects Barcode in image and returns the rectangle(s) containing the code. - * - * @param img grayscale or color (BGR) image containing (or not) Barcode. - * @param points Output vector of vector of vertices of the minimum-area rotated rectangle containing the codes. - * For N detected barcodes, the dimensions of this array should be [N][4]. - * Order of four points in vector< Point2f> is bottomLeft, topLeft, topRight, bottomRight. - */ - CV_WRAP bool detect(InputArray img, OutputArray points) const; - - /** @brief Decodes barcode in image once it's found by the detect() method. - * - * @param img grayscale or color (BGR) image containing bar code. - * @param points vector of rotated rectangle vertices found by detect() method (or some other algorithm). - * For N detected barcodes, the dimensions of this array should be [N][4]. - * Order of four points in vector is bottomLeft, topLeft, topRight, bottomRight. - * @param decoded_info UTF8-encoded output vector of string or empty vector of string if the codes cannot be decoded. - * @param decoded_type vector of BarcodeType, specifies the type of these barcodes - */ - CV_WRAP bool decode(InputArray img, InputArray points, CV_OUT std::vector &decoded_info, CV_OUT - std::vector &decoded_type) const; - - /** @brief Both detects and decodes barcode - - * @param img grayscale or color (BGR) image containing barcode. - * @param decoded_info UTF8-encoded output vector of string(s) or empty vector of string if the codes cannot be decoded. - * @param decoded_type vector of BarcodeType, specifies the type of these barcodes - * @param points optional output vector of vertices of the found barcode rectangle. Will be empty if not found. - */ - CV_WRAP bool detectAndDecode(InputArray img, CV_OUT std::vector &decoded_info, CV_OUT - std::vector &decoded_type, OutputArray points = noArray()) const; - -protected: - struct Impl; - Ptr p; -}; -//! @} -} -} // cv::barcode:: -#endif //__OPENCV_BARCODE_HPP__ diff --git a/modules/barcode/misc/java/filelist_common b/modules/barcode/misc/java/filelist_common deleted file mode 100644 index 7433c26d1..000000000 --- a/modules/barcode/misc/java/filelist_common +++ /dev/null @@ -1 +0,0 @@ -misc/java/src/cpp/barcode_converters.hpp diff --git a/modules/barcode/misc/java/gen_dict.json b/modules/barcode/misc/java/gen_dict.json deleted file mode 100644 index 834d422dc..000000000 --- a/modules/barcode/misc/java/gen_dict.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "type_dict": { - "vector_BarcodeType": { - "j_type": "List", - "jn_type": "List", - "jni_type": "jobject", - "jni_var": "std::vector< cv::barcode::BarcodeType > %(n)s", - "suffix": "Ljava_util_List", - "v_type": "vector_BarcodeType" - } - } -} diff --git a/modules/barcode/misc/java/src/cpp/barcode_converters.cpp b/modules/barcode/misc/java/src/cpp/barcode_converters.cpp deleted file mode 100644 index b0dbc6600..000000000 --- a/modules/barcode/misc/java/src/cpp/barcode_converters.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// 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 - -// Author: darkliang - -#include "barcode_converters.hpp" - -#define LOG_TAG "org.opencv.barcode" - - -void Copy_vector_BarcodeType_to_List(JNIEnv* env, std::vector& vs, jobject list) -{ - static jclass juArrayList = ARRAYLIST(env); - jmethodID m_add = LIST_ADD(env, juArrayList); - jmethodID m_clear = LIST_CLEAR(env, juArrayList); - env->CallVoidMethod(list, m_clear); - - jclass jInteger = env->FindClass("java/lang/Integer"); - jmethodID m_create_Integer = CONSTRUCTOR(env, jInteger); - - for (size_t i = 0; i < vs.size(); ++i) - { - jobject element = env->NewObject(jInteger, m_create_Integer, vs[i]); - env->CallBooleanMethod(list, m_add, element); - env->DeleteLocalRef(element); - } -} diff --git a/modules/barcode/misc/java/src/cpp/barcode_converters.hpp b/modules/barcode/misc/java/src/cpp/barcode_converters.hpp deleted file mode 100644 index 127484fa1..000000000 --- a/modules/barcode/misc/java/src/cpp/barcode_converters.hpp +++ /dev/null @@ -1,20 +0,0 @@ -// 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 - -// Author: darkliang - -#ifndef BARCODE_CONVERTERS_HPP -#define BARCODE_CONVERTERS_HPP - -#include -#include "opencv_java.hpp" -#include "opencv2/core.hpp" -#include "opencv2/barcode.hpp" - - -using namespace cv::barcode; - -void Copy_vector_BarcodeType_to_List(JNIEnv* env, std::vector& vs, jobject list); - -#endif /* BARCODE_CONVERTERS_HPP */ diff --git a/modules/barcode/misc/java/test/BarcodeDetectorTest.java b/modules/barcode/misc/java/test/BarcodeDetectorTest.java deleted file mode 100644 index b07ea6fae..000000000 --- a/modules/barcode/misc/java/test/BarcodeDetectorTest.java +++ /dev/null @@ -1,51 +0,0 @@ -package org.opencv.test.barcode; - -import java.util.List; -import org.opencv.core.Mat; -import org.opencv.barcode.BarcodeDetector; -import org.opencv.imgcodecs.Imgcodecs; -import org.opencv.test.OpenCVTestCase; -import java.util.ArrayList; -import static org.opencv.barcode.Barcode.EAN_13; - -public class BarcodeDetectorTest extends OpenCVTestCase { - - private final static String ENV_OPENCV_TEST_DATA_PATH = "OPENCV_TEST_DATA_PATH"; - private String testDataPath; - - @Override - protected void setUp() throws Exception { - super.setUp(); - - testDataPath = System.getenv(ENV_OPENCV_TEST_DATA_PATH); - if (testDataPath == null) - throw new Exception(ENV_OPENCV_TEST_DATA_PATH + " has to be defined!"); - } - - public void testDetectAndDecode() { - Mat img = Imgcodecs.imread(testDataPath + "/cv/barcode/multiple/4_barcodes.jpg"); - assertFalse(img.empty()); - BarcodeDetector detector = new BarcodeDetector(); - assertNotNull(detector); - List < String > infos = new ArrayList< String >(); - List < Integer > types = new ArrayList< Integer >(); - - boolean result = detector.detectAndDecode(img, infos, types); - assertTrue(result); - assertEquals(infos.size(), 4); - assertEquals(types.size(), 4); - final String[] correctResults = {"9787122276124", "9787118081473", "9787564350840", "9783319200064"}; - for (int i = 0; i < 4; i++) { - assertEquals(types.get(i).intValue(), EAN_13); - result = false; - for (int j = 0; j < 4; j++) { - if (correctResults[j].equals(infos.get(i))) { - result = true; - break; - } - } - assertTrue(result); - } - - } -} diff --git a/modules/barcode/misc/python/pyopencv_barcode.hpp b/modules/barcode/misc/python/pyopencv_barcode.hpp deleted file mode 100644 index 9008f3909..000000000 --- a/modules/barcode/misc/python/pyopencv_barcode.hpp +++ /dev/null @@ -1,22 +0,0 @@ -#ifdef HAVE_OPENCV_BARCODE -typedef std::vector vector_BarcodeType; - -template<> struct pyopencvVecConverter -{ - static bool to(PyObject* obj, std::vector& value, const ArgInfo& info) - { - return pyopencv_to_generic_vec(obj, value, info); - } - static PyObject* from(const std::vector& value) - { - - return pyopencv_from_generic_vec(value); - } -}; - -template<> -bool pyopencv_to(PyObject *o, std::vector& types, const ArgInfo& info) -{ - return pyopencvVecConverter::to(o, types, info); -} -#endif // HAVE_OPENCV_BARCODE diff --git a/modules/barcode/misc/python/test/test_barcode_detector.py b/modules/barcode/misc/python/test/test_barcode_detector.py deleted file mode 100644 index b939ee567..000000000 --- a/modules/barcode/misc/python/test/test_barcode_detector.py +++ /dev/null @@ -1,30 +0,0 @@ -#!/usr/bin/env python -''' -=============================================================================== -Barcode detect and decode pipeline. -=============================================================================== -''' -import os -import numpy as np -import cv2 as cv - -from tests_common import NewOpenCVTests - -class barcode_detector_test(NewOpenCVTests): - - def test_detect(self): - img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/barcode/multiple/4_barcodes.jpg')) - self.assertFalse(img is None) - detector = cv.barcode_BarcodeDetector() - retval, corners = detector.detect(img) - self.assertTrue(retval) - self.assertEqual(corners.shape, (4, 4, 2)) - - def test_detect_and_decode(self): - img = cv.imread(os.path.join(self.extraTestDataPath, 'cv/barcode/single/book.jpg')) - self.assertFalse(img is None) - detector = cv.barcode_BarcodeDetector() - retval, decoded_info, decoded_type, corners = detector.detectAndDecode(img) - self.assertEqual(decoded_info[0], "9787115279460") - self.assertEqual(decoded_type[0], cv.barcode.EAN_13) - self.assertEqual(corners.shape, (1, 4, 2)) diff --git a/modules/barcode/perf/perf_barcode.cpp b/modules/barcode/perf/perf_barcode.cpp deleted file mode 100644 index 0d814a612..000000000 --- a/modules/barcode/perf/perf_barcode.cpp +++ /dev/null @@ -1,113 +0,0 @@ -// 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" - -namespace opencv_test{namespace{ - -typedef ::perf::TestBaseWithParam< tuple > Perf_Barcode_multi; -typedef ::perf::TestBaseWithParam< tuple > Perf_Barcode_single; - -PERF_TEST_P_(Perf_Barcode_multi, detect) -{ - const string root = "cv/barcode/multiple/"; - const string name_current_image = get<0>(GetParam()); - const cv::Size sz = get<1>(GetParam()); - const string image_path = findDataFile(root + name_current_image); - - Mat src = imread(image_path); - ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; - cv::resize(src, src, sz); - - vector< Point > corners; - auto bardet = barcode::BarcodeDetector(); - bool res = false; - TEST_CYCLE() - { - res = bardet.detect(src, corners); - } - SANITY_CHECK_NOTHING(); - ASSERT_TRUE(res); -} - -PERF_TEST_P_(Perf_Barcode_multi, detect_decode) -{ - const string root = "cv/barcode/multiple/"; - const string name_current_image = get<0>(GetParam()); - const cv::Size sz = get<1>(GetParam()); - const string image_path = findDataFile(root + name_current_image); - - Mat src = imread(image_path); - ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; - cv::resize(src, src, sz); - - vector decoded_info; - vector decoded_type; - vector< Point > corners; - auto bardet = barcode::BarcodeDetector(); - bool res = false; - TEST_CYCLE() - { - res = bardet.detectAndDecode(src, decoded_info, decoded_type, corners); - } - SANITY_CHECK_NOTHING(); - ASSERT_TRUE(res); -} - -PERF_TEST_P_(Perf_Barcode_single, detect) -{ - const string root = "cv/barcode/single/"; - const string name_current_image = get<0>(GetParam()); - const cv::Size sz = get<1>(GetParam()); - const string image_path = findDataFile(root + name_current_image); - - Mat src = imread(image_path); - ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; - cv::resize(src, src, sz); - - vector< Point > corners; - auto bardet = barcode::BarcodeDetector(); - bool res = false; - TEST_CYCLE() - { - res = bardet.detect(src, corners); - } - SANITY_CHECK_NOTHING(); - ASSERT_TRUE(res); -} - -PERF_TEST_P_(Perf_Barcode_single, detect_decode) -{ - const string root = "cv/barcode/single/"; - const string name_current_image = get<0>(GetParam()); - const cv::Size sz = get<1>(GetParam()); - const string image_path = findDataFile(root + name_current_image); - - Mat src = imread(image_path); - ASSERT_FALSE(src.empty()) << "Can't read image: " << image_path; - cv::resize(src, src, sz); - - vector decoded_info; - vector decoded_type; - vector< Point > corners; - auto bardet = barcode::BarcodeDetector(); - bool res = false; - TEST_CYCLE() - { - res = bardet.detectAndDecode(src, decoded_info, decoded_type, corners); - } - SANITY_CHECK_NOTHING(); - ASSERT_TRUE(res); -} - -INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Barcode_multi, - testing::Combine( - testing::Values("4_barcodes.jpg"), - testing::Values(cv::Size(2041, 2722), cv::Size(1361, 1815), cv::Size(680, 907)))); -INSTANTIATE_TEST_CASE_P(/*nothing*/, Perf_Barcode_single, - testing::Combine( - testing::Values("book.jpg", "bottle_1.jpg", "bottle_2.jpg"), - testing::Values(cv::Size(480, 360), cv::Size(640, 480), cv::Size(800, 600)))); - -}} //namespace diff --git a/modules/barcode/perf/perf_main.cpp b/modules/barcode/perf/perf_main.cpp deleted file mode 100644 index 7de147e13..000000000 --- a/modules/barcode/perf/perf_main.cpp +++ /dev/null @@ -1,9 +0,0 @@ -// 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" - -using namespace perf; - -CV_PERF_TEST_MAIN(barcode) diff --git a/modules/barcode/perf/perf_precomp.hpp b/modules/barcode/perf/perf_precomp.hpp deleted file mode 100644 index dea5503b4..000000000 --- a/modules/barcode/perf/perf_precomp.hpp +++ /dev/null @@ -1,11 +0,0 @@ -// 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/barcode.hpp" - -#endif diff --git a/modules/barcode/samples/barcode.cpp b/modules/barcode/samples/barcode.cpp deleted file mode 100644 index 00f7f6c36..000000000 --- a/modules/barcode/samples/barcode.cpp +++ /dev/null @@ -1,298 +0,0 @@ -#include -#include "opencv2/barcode.hpp" -#include "opencv2/imgproc.hpp" -#include "opencv2/highgui.hpp" - -using namespace cv; -using namespace std; - -static int liveBarCodeDetect(); - -static int imageBarCodeDetect(const string &in_file); - -static bool g_detectOnly = false; - -static string g_out_file_name, g_out_file_ext; - -static Ptr bardet; - -int main(int argc, char **argv) -{ - - - const string keys = "{h help ? | | print help messages }" - "{i in | | input image path (also switches to image detection mode) }" - "{detect | false | detect 1D barcode only (skip decoding) }" - "{o out | | path to result file (only for single image decode) }" - "{sr_prototxt| | super resolution prototxt path }" - "{sr_model | | super resolution model path }"; - - CommandLineParser cmd_parser(argc, argv, keys); - - cmd_parser.about("This program detects the 1D barcodes from camera or images using the OpenCV library."); - if (cmd_parser.has("help")) - { - cmd_parser.printMessage(); - return 0; - } - - string in_file_name = cmd_parser.get("in"); // path to input image - string sr_prototxt = cmd_parser.get("sr_prototxt"); // path to sr_prototxt - string sr_model = cmd_parser.get("sr_model"); // path to sr_model - if (cmd_parser.has("out")) - { - std::string fpath = cmd_parser.get("out"); // path to output image - std::string::size_type idx = fpath.rfind('.'); - if (idx != std::string::npos) - { - g_out_file_name = fpath.substr(0, idx); - g_out_file_ext = fpath.substr(idx); - } - else - { - g_out_file_name = fpath; - g_out_file_ext = ".png"; - } - } - if (!cmd_parser.check()) - { - cmd_parser.printErrors(); - return -1; - } - g_detectOnly = cmd_parser.has("detect") && cmd_parser.get("detect"); - //! [initialize] - try{ - bardet = makePtr(sr_prototxt, sr_model); - } catch (const std::exception& e) - { - cout << - "\n---------------------------------------------------------------\n" - "Failed to initialize super resolution.\n" - "Please, download 'sr.*' from\n" - "https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode\n" - "and put them into the current directory.\n" - "Or you can leave sr_prototxt and sr_model unspecified.\n" - "---------------------------------------------------------------\n"; - cout << e.what() << endl; - return -1; - } - //! [initialize] - int return_code; - if (in_file_name.empty()) - { - return_code = liveBarCodeDetect(); - } - else - { - return_code = imageBarCodeDetect(in_file_name); - } - return return_code; - -} - -static void drawBarcodeContour(Mat &color_image, const vector &corners, bool decodable) -{ - if (!corners.empty()) - { - double show_radius = (color_image.rows > color_image.cols) ? (2.813 * color_image.rows) / color_image.cols : - (2.813 * color_image.cols) / color_image.rows; - double contour_radius = show_radius * 0.4; - - vector > contours; - contours.push_back(corners); - - drawContours(color_image, contours, 0, decodable ? Scalar(0, 255, 0) : Scalar(0, 0, 255), - cvRound(contour_radius)); - - RNG rng(1000); - for (size_t i = 0; i < 4; i++) - { - Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); - circle(color_image, corners[i], cvRound(show_radius), color, -1); - } - } -} - -//! [visualize] -static void drawFPS(Mat &color_image, double fps) -{ - ostringstream convert; - convert << cv::format("%.2f", fps) << " FPS (" << (g_detectOnly ? " detector" : " decoder") << ")"; - putText(color_image, convert.str(), Point(25, 25), FONT_HERSHEY_DUPLEX, 1, Scalar(0, 0, 255), 2); -} - -static void drawBarcodeResults(Mat &frame, const vector &corners, const vector &decode_info, - const vector &decode_type, double fps) -{ - if (!corners.empty()) - { - for (size_t i = 0; i < corners.size(); i += 4) - { - size_t bar_idx = i / 4; - vector barcode_contour(corners.begin() + i, corners.begin() + i + 4); - drawBarcodeContour(frame, barcode_contour, g_detectOnly || decode_type[bar_idx] != barcode::NONE); - - cout << "BAR[" << bar_idx << "] @ " << Mat(barcode_contour).reshape(2, 1) << ": "; - if (decode_info.size() > bar_idx) - { - if (!decode_info[bar_idx].empty()) - { - cout << "TYPE: " << decode_type[bar_idx] << " INFO: " << decode_info[bar_idx] << endl; - } - else - { - cout << "can't decode 1D barcode" << endl; - } - } - else - { - cout << "decode information is not available (disabled)" << endl; - } - } - } - else - { - cout << "Barcode is not detected" << endl; - } - - drawFPS(frame, fps); -} -//! [visualize] - -static void -runBarcode(const Mat &input, vector &corners, vector &decode_info, - vector &decode_type -) -{ - if (!g_detectOnly) - { - //! [detectAndDecode] - bool result_detection = bardet->detectAndDecode(input, decode_info, decode_type, corners); - //! [detectAndDecode] - CV_UNUSED(result_detection); - } - else - { - //! [detect] - bool result_detection = bardet->detect(input, corners); - //! [detect] - CV_UNUSED(result_detection); - } -} - -int liveBarCodeDetect() -{ - VideoCapture cap(0); - - if (!cap.isOpened()) - { - cout << "Cannot open a camera" << endl; - return 2; - } - - cout << "Press 'd' to switch between decoder and detector" << endl; - cout << "Press 'ESC' to exit" << endl; - - for (;;) - { - Mat frame; - cap >> frame; - if (frame.empty()) - { - cout << "End of video stream" << endl; - break; - } - - - Mat result; - if (frame.channels() == 1) - { - cvtColor(frame, result, COLOR_GRAY2BGR); - } - else - { - frame.copyTo(result); - } - TickMeter timer; - //! [output] - vector decode_info; - vector decoded_type; - vector corners; - //! [output] - timer.start(); - runBarcode(frame, corners, decode_info, decoded_type); - timer.stop(); - - double fps = 1 / timer.getTimeSec(); - drawBarcodeResults(result, corners, decode_info, decoded_type, fps); - - if (!result.empty()) - { - imshow("barcode", result); - } - - int code = waitKey(1); - if (code < 0) - { - continue; - } // timeout - char c = (char) code; - - if (c == 'd') - { - g_detectOnly = !g_detectOnly; - cout << "Switching barcode decoder mode ==> " << (g_detectOnly ? "detect" : "decode") << endl; - } - if (c == 27) - { - cout << "'ESC' is pressed. Exiting..." << endl; - break; - } - } - cout << "Exit." << endl; - - return 0; -} - -int imageBarCodeDetect(const string &in_file) -{ - const int count_experiments = 10; - - Mat input = imread(in_file, IMREAD_COLOR); - cout << "Run BarCode" << (g_detectOnly ? " detector" : " decoder") << " on image: " << input.size() << " (" - << typeToString(input.type()) << ")" << endl; - - vector corners; - vector decode_info; - vector decoded_type; - TickMeter timer; - for (size_t i = 0; i < count_experiments; i++) - { - corners.clear(); - decode_info.clear(); - - timer.start(); - runBarcode(input, corners, decode_info, decoded_type); - timer.stop(); - } - double fps = count_experiments / timer.getTimeSec(); - cout << "FPS: " << fps << endl; - - Mat result; - input.copyTo(result); - drawBarcodeResults(result, corners, decode_info, decoded_type, fps); - if (!g_out_file_name.empty()) - { - string out_file = g_out_file_name + g_out_file_ext; - cout << "Saving result: " << out_file << endl; - imwrite(out_file, result); - } - imshow("barcode", result); - waitKey(1); - - cout << "Press any key to exit ..." << endl; - waitKey(0); - cout << "Exit." << endl; - - return 0; -} diff --git a/modules/barcode/src/barcode.cpp b/modules/barcode/src/barcode.cpp deleted file mode 100644 index cfcf57607..000000000 --- a/modules/barcode/src/barcode.cpp +++ /dev/null @@ -1,272 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#include "precomp.hpp" -#include -#include -#include "decoder/ean13_decoder.hpp" -#include "decoder/ean8_decoder.hpp" -#include "detector/bardetect.hpp" -#include "decoder/common/super_scale.hpp" -#include "decoder/common/utils.hpp" - -namespace cv { -namespace barcode { - -static bool checkBarInputImage(InputArray img, Mat &gray) -{ - CV_Assert(!img.empty()); - CV_CheckDepthEQ(img.depth(), CV_8U, ""); - if (img.cols() <= 40 || img.rows() <= 40) - { - return false; // image data is not enough for providing reliable results - } - int incn = img.channels(); - CV_Check(incn, incn == 1 || incn == 3 || incn == 4, ""); - if (incn == 3 || incn == 4) - { - cvtColor(img, gray, COLOR_BGR2GRAY); - } - else - { - gray = img.getMat(); - } - return true; -} - -static void updatePointsResult(OutputArray points_, const vector &points) -{ - if (points_.needed()) - { - int N = int(points.size() / 4); - if (N > 0) - { - Mat m_p(N, 4, CV_32FC2, (void *) &points[0]); - int points_type = points_.fixedType() ? points_.type() : CV_32FC2; - m_p.reshape(2, points_.rows()).convertTo(points_, points_type); // Mat layout: N x 4 x 2cn - } - else - { - points_.release(); - } - } -} - -inline const std::array, 2> &getDecoders() -{ - //indicate Decoder - static const std::array, 2> decoders{ - std::shared_ptr(new Ean13Decoder()), std::shared_ptr(new Ean8Decoder())}; - return decoders; -} - -class BarDecode -{ -public: - void init(const vector &bar_imgs_); - - const vector &getDecodeInformation() - { return result_info; } - - bool decodeMultiplyProcess(); - -private: - vector bar_imgs; - vector result_info; -}; - -void BarDecode::init(const vector &bar_imgs_) -{ - bar_imgs = bar_imgs_; -} - -bool BarDecode::decodeMultiplyProcess() -{ - static float constexpr THRESHOLD_CONF = 0.6f; - result_info.clear(); - result_info.resize(bar_imgs.size()); - parallel_for_(Range(0, int(bar_imgs.size())), [&](const Range &range) { - for (int i = range.start; i < range.end; i++) - { - Mat bin_bar; - Result max_res; - float max_conf = -1.f; - bool decoded = false; - for (const auto &decoder:getDecoders()) - { - if (decoded) - { break; } - for (const auto binary_type : binary_types) - { - binarize(bar_imgs[i], bin_bar, binary_type); - auto cur_res = decoder->decodeROI(bin_bar); - if (cur_res.second > max_conf) - { - max_res = cur_res.first; - max_conf = cur_res.second; - if (max_conf > THRESHOLD_CONF) - { - // code decoded - decoded = true; - break; - } - } - } //binary types - } //decoder types - - result_info[i] = max_res; - } - }); - return !result_info.empty(); -} - - -struct BarcodeDetector::Impl -{ -public: - Impl() = default; - - ~Impl() = default; - - vector initDecode(const Mat &src, const vector> &points) const; - - std::shared_ptr sr; - bool use_nn_sr = false; -}; - -// return cropped and scaled bar img -vector BarcodeDetector::Impl::initDecode(const Mat &src, const vector> &points) const -{ - vector bar_imgs; - for (auto &corners : points) - { - Mat bar_img; - cropROI(src, bar_img, corners); -// sharpen(bar_img, bar_img); - // empirical settings - if (bar_img.cols < 320 || bar_img.cols > 640) - { - float scale = 560.0f / static_cast(bar_img.cols); - sr->processImageScale(bar_img, bar_img, scale, use_nn_sr); - } - bar_imgs.emplace_back(bar_img); - } - return bar_imgs; - -} - -BarcodeDetector::BarcodeDetector(const string &prototxt_path, const string &model_path) : p(new Impl) -{ - if (!prototxt_path.empty() && !model_path.empty()) - { - CV_Assert(utils::fs::exists(prototxt_path)); - CV_Assert(utils::fs::exists(model_path)); - p->sr = std::make_shared(); - int res = p->sr->init(prototxt_path, model_path); - CV_Assert(res == 0); - p->use_nn_sr = true; - } -} - - -BarcodeDetector::~BarcodeDetector() = default; - -bool BarcodeDetector::detect(InputArray img, OutputArray points) const -{ - Mat inarr; - if (!checkBarInputImage(img, inarr)) - { - points.release(); - return false; - } - - Detect bardet; - bardet.init(inarr); - bardet.localization(); - if (!bardet.computeTransformationPoints()) - { return false; } - vector> pnts2f = bardet.getTransformationPoints(); - vector trans_points; - for (auto &i : pnts2f) - { - for (const auto &j : i) - { - trans_points.push_back(j); - } - } - - updatePointsResult(points, trans_points); - return true; -} - -bool BarcodeDetector::decode(InputArray img, InputArray points, vector &decoded_info, - vector &decoded_type) const -{ - Mat inarr; - if (!checkBarInputImage(img, inarr)) - { - return false; - } - CV_Assert(points.size().width > 0); - CV_Assert((points.size().width % 4) == 0); - vector> src_points; - Mat bar_points = points.getMat(); - bar_points = bar_points.reshape(2, 1); - for (int i = 0; i < bar_points.size().width; i += 4) - { - vector tempMat = bar_points.colRange(i, i + 4); - if (contourArea(tempMat) > 0.0) - { - src_points.push_back(tempMat); - } - } - CV_Assert(!src_points.empty()); - vector bar_imgs = p->initDecode(inarr, src_points); - BarDecode bardec; - bardec.init(bar_imgs); - bardec.decodeMultiplyProcess(); - const vector info = bardec.getDecodeInformation(); - decoded_info.clear(); - decoded_type.clear(); - bool ok = false; - for (const auto &res : info) - { - if (res.format != NONE) - { - ok = true; - } - - decoded_info.emplace_back(res.result); - decoded_type.emplace_back(res.format); - } - return ok; -} - -bool -BarcodeDetector::detectAndDecode(InputArray img, vector &decoded_info, vector &decoded_type, - OutputArray points_) const -{ - Mat inarr; - if (!checkBarInputImage(img, inarr)) - { - points_.release(); - return false; - } - vector points; - bool ok = this->detect(inarr, points); - if (!ok) - { - points_.release(); - return false; - } - updatePointsResult(points_, points); - decoded_info.clear(); - decoded_type.clear(); - ok = this->decode(inarr, points, decoded_info, decoded_type); - return ok; -} - -}// namespace barcode -} // namespace cv diff --git a/modules/barcode/src/decoder/abs_decoder.cpp b/modules/barcode/src/decoder/abs_decoder.cpp deleted file mode 100644 index 9eadf4bc3..000000000 --- a/modules/barcode/src/decoder/abs_decoder.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#include "../precomp.hpp" -#include "abs_decoder.hpp" - -namespace cv { -namespace barcode { - -void cropROI(const Mat &src, Mat &dst, const std::vector &rects) -{ - std::vector vertices = rects; - int height = cvRound(norm(vertices[0] - vertices[1])); - int width = cvRound(norm(vertices[1] - vertices[2])); - if (height > width) - { - std::swap(height, width); - Point2f v0 = vertices[0]; - vertices.erase(vertices.begin()); - vertices.push_back(v0); - } - std::vector dst_vertices{ - Point2f(0, (float) (height - 1)), Point2f(0, 0), Point2f((float) (width - 1), 0), - Point2f((float) (width - 1), (float) (height - 1))}; - dst.create(Size(width, height), CV_8UC1); - Mat M = getPerspectiveTransform(vertices, dst_vertices); - warpPerspective(src, dst, M, dst.size(), cv::INTER_LINEAR, BORDER_CONSTANT, Scalar(255)); -} - -void fillCounter(const std::vector &row, uint start, Counter &counter) -{ - size_t counter_length = counter.pattern.size(); - std::fill(counter.pattern.begin(), counter.pattern.end(), 0); - counter.sum = 0; - size_t end = row.size(); - uchar color = row[start]; - uint counterPosition = 0; - while (start < end) - { - if (row[start] == color) - { // that is, exactly one is true - counter.pattern[counterPosition]++; - counter.sum++; - } - else - { - counterPosition++; - if (counterPosition == counter_length) - { - break; - } - else - { - counter.pattern[counterPosition] = 1; - counter.sum++; - color = 255 - color; - } - } - ++start; - } -} - -static inline uint -patternMatchVariance(const Counter &counter, const std::vector &pattern, uint maxIndividualVariance) -{ - size_t numCounters = counter.pattern.size(); - int total = static_cast(counter.sum); - int patternLength = std::accumulate(pattern.cbegin(), pattern.cend(), 0); - if (total < patternLength) - { - // If we don't even have one pixel per unit of bar width, assume this is too small - // to reliably match, so fail: - // and use constexpr functions - return WHITE;// max - } - // We're going to fake floating-point math in integers. We just need to use more bits. - // Scale up patternLength so that intermediate values below like scaledCounter will have - // more "significant digits" - - int unitBarWidth = (total << INTEGER_MATH_SHIFT) / patternLength; - maxIndividualVariance = (maxIndividualVariance * unitBarWidth) >> INTEGER_MATH_SHIFT; - uint totalVariance = 0; - for (uint x = 0; x < numCounters; x++) - { - int cnt = counter.pattern[x] << INTEGER_MATH_SHIFT; - int scaledPattern = pattern[x] * unitBarWidth; - uint variance = std::abs(cnt - scaledPattern); - if (variance > maxIndividualVariance) - { - return WHITE; - } - totalVariance += variance; - } - return totalVariance / total; -} - -/** -* Determines how closely a set of observed counts of runs of black/white values matches a given -* target pattern. This is reported as the ratio of the total variance from the expected pattern -* proportions across all pattern elements, to the length of the pattern. -* -* @param counters observed counters -* @param pattern expected pattern -* @param maxIndividualVariance The most any counter can differ before we give up -* @return ratio of total variance between counters and pattern compared to total pattern size, -* where the ratio has been multiplied by 256. So, 0 means no variance (perfect match); 256 means -* the total variance between counters and patterns equals the pattern length, higher values mean -* even more variance -*/ -uint patternMatch(const Counter &counters, const std::vector &pattern, uint maxIndividual) -{ - CV_Assert(counters.pattern.size() == pattern.size()); - return patternMatchVariance(counters, pattern, maxIndividual); -} -} -} diff --git a/modules/barcode/src/decoder/abs_decoder.hpp b/modules/barcode/src/decoder/abs_decoder.hpp deleted file mode 100644 index cd38eed1d..000000000 --- a/modules/barcode/src/decoder/abs_decoder.hpp +++ /dev/null @@ -1,73 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#ifndef __OPENCV_BARCODE_ABS_DECODER_HPP__ -#define __OPENCV_BARCODE_ABS_DECODER_HPP__ - -#include - -namespace cv { -namespace barcode { -using std::string; -using std::vector; -constexpr static uchar BLACK = std::numeric_limits::min(); -// WHITE elemental area is 0xff -constexpr static uchar WHITE = std::numeric_limits::max(); - - -struct Result -{ - std::string result; - BarcodeType format = BarcodeType::NONE; - - Result() = default; - - Result(const std::string &_result, BarcodeType _format) - { - result = _result; - format = _format; - } -}; - -struct Counter -{ - std::vector pattern; - uint sum; - - explicit Counter(const vector &_pattern) - { - pattern = _pattern; - sum = 0; - } -}; - -class AbsDecoder -{ -public: - virtual std::pair decodeROI(const Mat &bar_img) const = 0; - - virtual ~AbsDecoder() = default; - -protected: - virtual Result decode(const vector &data) const = 0; - - virtual bool isValid(const string &result) const = 0; - - size_t bits_num{}; - size_t digit_number{}; -}; - -void cropROI(const Mat &_src, Mat &_dst, const std::vector &rect); - -void fillCounter(const std::vector &row, uint start, Counter &counter); - -constexpr static uint INTEGER_MATH_SHIFT = 8; -constexpr static uint PATTERN_MATCH_RESULT_SCALE_FACTOR = 1 << INTEGER_MATH_SHIFT; - -uint patternMatch(const Counter &counters, const std::vector &pattern, uint maxIndividual); -} -} // namespace cv - -#endif //! __OPENCV_BARCODE_ABS_DECODER_HPP__ diff --git a/modules/barcode/src/decoder/common/hybrid_binarizer.cpp b/modules/barcode/src/decoder/common/hybrid_binarizer.cpp deleted file mode 100644 index 76d63d6e4..000000000 --- a/modules/barcode/src/decoder/common/hybrid_binarizer.cpp +++ /dev/null @@ -1,195 +0,0 @@ -// 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. -// Modified from ZXing. Copyright ZXing authors. -// Licensed under the Apache License, Version 2.0 (the "License"). - -#include "../../precomp.hpp" -#include "hybrid_binarizer.hpp" - -namespace cv { -namespace barcode { - - -#define CLAMP(x, x1, x2) x < (x1) ? (x1) : ((x) > (x2) ? (x2) : (x)) - -// This class uses 5x5 blocks to compute local luminance, where each block is 8x8 pixels. -// So this is the smallest dimension in each axis we can accept. -constexpr static int BLOCK_SIZE_POWER = 3; -constexpr static int BLOCK_SIZE = 1 << BLOCK_SIZE_POWER; // ...0100...00 -constexpr static int BLOCK_SIZE_MASK = BLOCK_SIZE - 1; // ...0011...11 -constexpr static int MINIMUM_DIMENSION = BLOCK_SIZE * 5; -constexpr static int MIN_DYNAMIC_RANGE = 24; - -void -calculateThresholdForBlock(const std::vector &luminances, int sub_width, int sub_height, int width, int height, - const Mat &black_points, Mat &dst) -{ - int maxYOffset = height - BLOCK_SIZE; - int maxXOffset = width - BLOCK_SIZE; - for (int y = 0; y < sub_height; y++) - { - int yoffset = y << BLOCK_SIZE_POWER; - if (yoffset > maxYOffset) - { - yoffset = maxYOffset; - } - int top = CLAMP(y, 2, sub_height - 3); - for (int x = 0; x < sub_width; x++) - { - int xoffset = x << BLOCK_SIZE_POWER; - if (xoffset > maxXOffset) - { - xoffset = maxXOffset; - } - int left = CLAMP(x, 2, sub_width - 3); - int sum = 0; - const auto *black_row = black_points.ptr(top - 2); - for (int z = 0; z <= 4; z++) - { - sum += black_row[left - 2] + black_row[left - 1] + black_row[left] + black_row[left + 1] + - black_row[left + 2]; - black_row += black_points.cols; - } - int average = sum / 25; - int temp_y = 0; - - auto *ptr = dst.ptr(yoffset, xoffset); - for (int offset = yoffset * width + xoffset; temp_y < 8; offset += width) - { - for (int temp_x = 0; temp_x < 8; ++temp_x) - { - *(ptr + temp_x) = (luminances[offset + temp_x] & 255) <= average ? 0 : 255; - } - ++temp_y; - ptr += width; - } - } - } - -} - -Mat calculateBlackPoints(std::vector luminances, int sub_width, int sub_height, int width, int height) -{ - int maxYOffset = height - BLOCK_SIZE; - int maxXOffset = width - BLOCK_SIZE; - Mat black_points(Size(sub_width, sub_height), CV_8UC1); - for (int y = 0; y < sub_height; y++) - { - int yoffset = y << BLOCK_SIZE_POWER; - if (yoffset > maxYOffset) - { - yoffset = maxYOffset; - } - for (int x = 0; x < sub_width; x++) - { - int xoffset = x << BLOCK_SIZE_POWER; - if (xoffset > maxXOffset) - { - xoffset = maxXOffset; - } - int sum = 0; - int min = 0xFF; - int max = 0; - for (int yy = 0, offset = yoffset * width + xoffset; yy < BLOCK_SIZE; yy++, offset += width) - { - for (int xx = 0; xx < BLOCK_SIZE; xx++) - { - int pixel = luminances[offset + xx] & 0xFF; - sum += pixel; - // still looking for good contrast - if (pixel < min) - { - min = pixel; - } - if (pixel > max) - { - max = pixel; - } - } - // short-circuit min/max tests once dynamic range is met - if (max - min > MIN_DYNAMIC_RANGE) - { - // finish the rest of the rows quickly - for (yy++, offset += width; yy < BLOCK_SIZE; yy++, offset += width) - { - for (int xx = 0; xx < BLOCK_SIZE; xx++) - { - sum += luminances[offset + xx] & 0xFF; - } - } - } - } - - // The default estimate is the average of the values in the block. - int average = sum >> (BLOCK_SIZE_POWER * 2); - if (max - min <= MIN_DYNAMIC_RANGE) - { - // If variation within the block is low, assume this is a block with only light or only - // dark pixels. In that case we do not want to use the average, as it would divide this - // low contrast area into black and white pixels, essentially creating data out of noise. - // - // The default assumption is that the block is light/background. Since no estimate for - // the level of dark pixels exists locally, use half the min for the block. - average = min / 2; - - if (y > 0 && x > 0) - { - // Correct the "white background" assumption for blocks that have neighbors by comparing - // the pixels in this block to the previously calculated black points. This is based on - // the fact that dark barcode symbology is always surrounded by some amount of light - // background for which reasonable black point estimates were made. The bp estimated at - // the boundaries is used for the interior. - - // The (min < bp) is arbitrary but works better than other heuristics that were tried. - int averageNeighborBlackPoint = - (black_points.at(y - 1, x) + (2 * black_points.at(y, x - 1)) + - black_points.at(y - 1, x - 1)) / 4; - if (min < averageNeighborBlackPoint) - { - average = averageNeighborBlackPoint; - } - } - } - black_points.at(y, x) = (uchar) average; - } - } - return black_points; - -} - - -void hybridBinarization(const Mat &src, Mat &dst) -{ - int width = src.cols; - int height = src.rows; - - if (width >= MINIMUM_DIMENSION && height >= MINIMUM_DIMENSION) - { - std::vector luminances(src.begin(), src.end()); - - int sub_width = width >> BLOCK_SIZE_POWER; - if ((width & BLOCK_SIZE_MASK) != 0) - { - sub_width++; - } - - int sub_height = height >> BLOCK_SIZE_POWER; - if ((height & BLOCK_SIZE_MASK) != 0) - { - sub_height++; - } - - Mat black_points = calculateBlackPoints(luminances, sub_width, sub_height, width, height); - - dst.create(src.size(), src.type()); - calculateThresholdForBlock(luminances, sub_width, sub_height, width, height, black_points, dst); - } - else - { - threshold(src, dst, 155, 255, THRESH_OTSU + THRESH_BINARY); - } - -} -} -} diff --git a/modules/barcode/src/decoder/common/hybrid_binarizer.hpp b/modules/barcode/src/decoder/common/hybrid_binarizer.hpp deleted file mode 100644 index da9ccf033..000000000 --- a/modules/barcode/src/decoder/common/hybrid_binarizer.hpp +++ /dev/null @@ -1,22 +0,0 @@ -// 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. -// Modified from ZXing. Copyright ZXing authors. -// Licensed under the Apache License, Version 2.0 (the "License"). - -#ifndef __OPENCV_BARCODE_HYBRID_BINARIZER_HPP__ -#define __OPENCV_BARCODE_HYBRID_BINARIZER_HPP__ - -namespace cv { -namespace barcode { - -void hybridBinarization(const Mat &src, Mat &dst); - -void -calculateThresholdForBlock(const std::vector &luminances, int sub_width, int sub_height, int width, int height, - const Mat &black_points, Mat &dst); - -Mat calculateBlackPoints(std::vector luminances, int sub_width, int sub_height, int width, int height); -} -} -#endif //__OPENCV_BARCODE_HYBRID_BINARIZER_HPP__ diff --git a/modules/barcode/src/decoder/common/super_scale.cpp b/modules/barcode/src/decoder/common/super_scale.cpp deleted file mode 100644 index 8dae2b323..000000000 --- a/modules/barcode/src/decoder/common/super_scale.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// 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. -// -// Tencent is pleased to support the open source community by making WeChat QRCode available. -// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. -// Modified by darkliang wangberlinT - -#include "../../precomp.hpp" -#include "opencv2/dnn.hpp" -#include "super_scale.hpp" - - -namespace cv { -namespace barcode { -constexpr static float MAX_SCALE = 4.0f; - -int SuperScale::init(const std::string &proto_path, const std::string &model_path) -{ - srnet_ = dnn::readNetFromCaffe(proto_path, model_path); - net_loaded_ = true; - return 0; -} - -void SuperScale::processImageScale(const Mat &src, Mat &dst, float scale, const bool &use_sr, int sr_max_size) -{ - scale = min(scale, MAX_SCALE); - if (scale > .0 && scale < 1.0) - { // down sample - resize(src, dst, Size(), scale, scale, INTER_AREA); - } - else if (scale > 1.5 && scale < 2.0) - { - resize(src, dst, Size(), scale, scale, INTER_CUBIC); - } - else if (scale >= 2.0) - { - int width = src.cols; - int height = src.rows; - if (use_sr && (int) sqrt(width * height * 1.0) < sr_max_size && net_loaded_) - { - superResolutionScale(src, dst); - if (scale > 2.0) - { - processImageScale(dst, dst, scale / 2.0f, use_sr); - } - } - else - { resize(src, dst, Size(), scale, scale, INTER_CUBIC); } - } -} - -int SuperScale::superResolutionScale(const Mat &src, Mat &dst) -{ - Mat blob; - dnn::blobFromImage(src, blob, 1.0 / 255, Size(src.cols, src.rows), {0.0f}, false, false); - - srnet_.setInput(blob); - auto prob = srnet_.forward(); - - dst = Mat(prob.size[2], prob.size[3], CV_8UC1); - - for (int row = 0; row < prob.size[2]; row++) - { - const float *prob_score = prob.ptr(0, 0, row); - auto *dst_row = dst.ptr(row); - for (int col = 0; col < prob.size[3]; col++) - { - dst_row[col] = saturate_cast(prob_score[col] * 255.0f); - } - } - return 0; -} -} // namespace barcode -} // namespace cv diff --git a/modules/barcode/src/decoder/common/super_scale.hpp b/modules/barcode/src/decoder/common/super_scale.hpp deleted file mode 100644 index 75761c3fd..000000000 --- a/modules/barcode/src/decoder/common/super_scale.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/// 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. -// -// Tencent is pleased to support the open source community by making WeChat QRCode available. -// Copyright (C) 2020 THL A29 Limited, a Tencent company. All rights reserved. - -#ifndef __OPENCV_BARCODE_SUPER_SCALE_HPP__ -#define __OPENCV_BARCODE_SUPER_SCALE_HPP__ - - -namespace cv { -namespace barcode { - -class SuperScale -{ -public: - SuperScale() = default; - - ~SuperScale() = default; - - int init(const std::string &proto_path, const std::string &model_path); - - void processImageScale(const Mat &src, Mat &dst, float scale, const bool &use_sr, int sr_max_size = 160); - -private: - dnn::Net srnet_; - bool net_loaded_ = false; - - int superResolutionScale(const cv::Mat &src, cv::Mat &dst); -}; - -} // namespace barcode -} // namespace cv -#endif //__OPENCV_BARCODE_SUPER_SCALE_HPP__ diff --git a/modules/barcode/src/decoder/common/utils.cpp b/modules/barcode/src/decoder/common/utils.cpp deleted file mode 100644 index 123955c66..000000000 --- a/modules/barcode/src/decoder/common/utils.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#include "../../precomp.hpp" -#include "utils.hpp" -#include "hybrid_binarizer.hpp" - -namespace cv { -namespace barcode { - - -void sharpen(const Mat &src, const Mat &dst) -{ - Mat blur; - GaussianBlur(src, blur, Size(0, 0), 25); - addWeighted(src, 2, blur, -1, -20, dst); -} - -void binarize(const Mat &src, Mat &dst, BinaryType mode) -{ - switch (mode) - { - case OTSU: - threshold(src, dst, 155, 255, THRESH_OTSU + THRESH_BINARY); - break; - case HYBRID: - hybridBinarization(src, dst); - break; - default: - CV_Error(Error::StsNotImplemented, "This binary type is not yet implemented"); - } -} -} -} diff --git a/modules/barcode/src/decoder/common/utils.hpp b/modules/barcode/src/decoder/common/utils.hpp deleted file mode 100644 index c9c8c3138..000000000 --- a/modules/barcode/src/decoder/common/utils.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#ifndef __OPENCV_BARCODE_UTILS_HPP__ -#define __OPENCV_BARCODE_UTILS_HPP__ - - -namespace cv { -namespace barcode { - -enum BinaryType -{ - OTSU = 0, HYBRID = 1 -}; -static constexpr BinaryType binary_types[] = {OTSU, HYBRID}; - -void sharpen(const Mat &src, const Mat &dst); - -void binarize(const Mat &src, Mat &dst, BinaryType mode); - -} -} -#endif //__OPENCV_BARCODE_UTILS_HPP__ diff --git a/modules/barcode/src/decoder/ean13_decoder.cpp b/modules/barcode/src/decoder/ean13_decoder.cpp deleted file mode 100644 index dc00218f9..000000000 --- a/modules/barcode/src/decoder/ean13_decoder.cpp +++ /dev/null @@ -1,92 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#include "../precomp.hpp" -#include "ean13_decoder.hpp" - -// three digit decode method from https://baike.baidu.com/item/EAN-13 - -namespace cv { -namespace barcode { - -static constexpr size_t EAN13BITS_NUM = 95; -static constexpr size_t EAN13DIGIT_NUM = 13; -// default thought that mat is a matrix after binary-transfer. -/** -* decode EAN-13 -* @prama: data: the input array, -* @prama: start: the index of start order, begin at 0, max-value is data.size()-1 -* it scan begin at the data[start] -*/ -Result Ean13Decoder::decode(const vector &data) const -{ - string result; - char decode_result[EAN13DIGIT_NUM + 1]{'\0'}; - if (data.size() < EAN13BITS_NUM) - { - return Result("Wrong Size", BarcodeType::NONE); - } - pair pattern; - if (!findStartGuardPatterns(data, pattern)) - { - return Result("Begin Pattern Not Found", BarcodeType::NONE); - } - uint start = pattern.second; - Counter counter(vector{0, 0, 0, 0}); - size_t end = data.size(); - int first_char_bit = 0; - // [1,6] are left part of EAN, [7,12] are right part, index 0 is calculated by left part - for (int i = 1; i < 7 && start < end; ++i) - { - int bestMatch = decodeDigit(data, counter, start, get_AB_Patterns()); - if (bestMatch == -1) - { - return Result("Decode Error", BarcodeType::NONE); - } - decode_result[i] = static_cast('0' + bestMatch % 10); - start = counter.sum + start; - first_char_bit += (bestMatch >= 10) << i; - } - decode_result[0] = static_cast(FIRST_CHAR_ARRAY()[first_char_bit >> 2] + '0'); - // why there need >> 2? - // first, the i in for-cycle is begin in 1 - // second, the first i = 1 is always - Counter middle_counter(vector(MIDDLE_PATTERN().size())); - if (!findGuardPatterns(data, start, true, MIDDLE_PATTERN(), middle_counter, pattern)) - { - return Result("Middle Pattern Not Found", BarcodeType::NONE); - - } - start = pattern.second; - for (int i = 0; i < 6 && start < end; ++i) - { - int bestMatch = decodeDigit(data, counter, start, get_A_or_C_Patterns()); - if (bestMatch == -1) - { - return Result("Decode Error", BarcodeType::NONE); - } - decode_result[i + 7] = static_cast('0' + bestMatch); - start = counter.sum + start; - } - Counter end_counter(vector(BEGIN_PATTERN().size())); - if (!findGuardPatterns(data, start, false, BEGIN_PATTERN(), end_counter, pattern)) - { - return Result("End Pattern Not Found", BarcodeType::NONE); - } - result = string(decode_result); - if (!isValid(result)) - { - return Result("Wrong: " + result.append(string(EAN13DIGIT_NUM - result.size(), ' ')), BarcodeType::NONE); - } - return Result(result, BarcodeType::EAN_13); -} - -Ean13Decoder::Ean13Decoder() -{ - this->bits_num = EAN13BITS_NUM; - this->digit_number = EAN13DIGIT_NUM; -} -} -} diff --git a/modules/barcode/src/decoder/ean13_decoder.hpp b/modules/barcode/src/decoder/ean13_decoder.hpp deleted file mode 100644 index 825ab9c1c..000000000 --- a/modules/barcode/src/decoder/ean13_decoder.hpp +++ /dev/null @@ -1,31 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#ifndef __OPENCV_BARCODE_EAN13_DECODER_HPP__ -#define __OPENCV_BARCODE_EAN13_DECODER_HPP__ - -#include "upcean_decoder.hpp" - -namespace cv { -namespace barcode { -//extern struct EncodePair; -using std::string; -using std::vector; -using std::pair; - - -class Ean13Decoder : public UPCEANDecoder -{ -public: - Ean13Decoder(); - - ~Ean13Decoder() override = default; - -protected: - Result decode(const vector &data) const override; -}; -} -} // namespace cv -#endif // !__OPENCV_BARCODE_EAN13_DECODER_HPP__ diff --git a/modules/barcode/src/decoder/ean8_decoder.cpp b/modules/barcode/src/decoder/ean8_decoder.cpp deleted file mode 100644 index 74ac2fdb0..000000000 --- a/modules/barcode/src/decoder/ean8_decoder.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#include "../precomp.hpp" -#include "ean8_decoder.hpp" - -namespace cv { -namespace barcode { -static constexpr size_t EAN8BITS_NUM = 70; -static constexpr size_t EAN8DIGIT_NUM = 8; - -Result Ean8Decoder::decode(const vector &data) const -{ - std::string result; - char decode_result[EAN8DIGIT_NUM + 1]{'\0'}; - if (data.size() < EAN8BITS_NUM) - { - return Result("Wrong Size", BarcodeType::NONE); - } - pair pattern; - if (!findStartGuardPatterns(data, pattern)) - { - return Result("Begin Pattern Not Found", BarcodeType::NONE); - } - uint start = pattern.second; - Counter counter(vector{0, 0, 0, 0}); - size_t end = data.size(); - for (int i = 0; i < 4 && start < end; ++i) - { - int bestMatch = decodeDigit(data, counter, start, get_A_or_C_Patterns()); - if (bestMatch == -1) - { - return Result("Decode Error", BarcodeType::NONE); - } - decode_result[i] = static_cast('0' + bestMatch % 10); - start = counter.sum + start; - } - - Counter middle_counter(vector(MIDDLE_PATTERN().size())); - - if (!findGuardPatterns(data, start, true, MIDDLE_PATTERN(), middle_counter, pattern)) - { - return Result("Middle Pattern Not Found", BarcodeType::NONE); - } - - start = pattern.second; - for (int i = 0; i < 4 && start < end; ++i) - { - int bestMatch = decodeDigit(data, counter, start, get_A_or_C_Patterns()); - if (bestMatch == -1) - { - return Result("Decode Error", BarcodeType::NONE); - } - decode_result[i + 4] = static_cast('0' + bestMatch); - start = counter.sum + start; - } - Counter end_counter(vector(BEGIN_PATTERN().size())); - if (!findGuardPatterns(data, start, false, BEGIN_PATTERN(), end_counter, pattern)) - { - return Result("End Pattern Not Found", BarcodeType::NONE); - } - result = string(decode_result); - if (!isValid(result)) - { - return Result("Wrong: " + result.append(string(EAN8DIGIT_NUM - result.size(), ' ')), BarcodeType::NONE); - } - return Result(result, BarcodeType::EAN_8); -} - -Ean8Decoder::Ean8Decoder() -{ - this->digit_number = EAN8DIGIT_NUM; - this->bits_num = EAN8BITS_NUM; -} - -} -} diff --git a/modules/barcode/src/decoder/ean8_decoder.hpp b/modules/barcode/src/decoder/ean8_decoder.hpp deleted file mode 100644 index c5ea0a0ec..000000000 --- a/modules/barcode/src/decoder/ean8_decoder.hpp +++ /dev/null @@ -1,32 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#ifndef __OPENCV_BARCODE_EAN8_DECODER_HPP__ -#define __OPENCV_BARCODE_EAN8_DECODER_HPP__ - -#include "upcean_decoder.hpp" - -namespace cv { -namespace barcode { - -using std::string; -using std::vector; -using std::pair; - -class Ean8Decoder : public UPCEANDecoder -{ - -public: - Ean8Decoder(); - - ~Ean8Decoder() override = default; - -protected: - Result decode(const vector &data) const override; -}; -} -} - -#endif //__OPENCV_BARCODE_EAN8_DECODER_HPP__ diff --git a/modules/barcode/src/decoder/upcean_decoder.cpp b/modules/barcode/src/decoder/upcean_decoder.cpp deleted file mode 100644 index aa5a7b8f9..000000000 --- a/modules/barcode/src/decoder/upcean_decoder.cpp +++ /dev/null @@ -1,290 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#include "../precomp.hpp" -#include "upcean_decoder.hpp" -#include - -namespace cv { -namespace barcode { - -static constexpr int DIVIDE_PART = 15; -static constexpr int BIAS_PART = 2; - -#if 0 -void UPCEANDecoder::drawDebugLine(Mat &debug_img, const Point2i &begin, const Point2i &end) const -{ - Result result; - std::vector middle; - LineIterator line = LineIterator(debug_img, begin, end); - middle.reserve(line.count); - for (int cnt = 0; cnt < line.count; cnt++, line++) - { - middle.push_back(debug_img.at(line.pos())); - } - std::pair start_range; - if (findStartGuardPatterns(middle, start_range)) - { - circle(debug_img, Point2i(begin.x + start_range.second, begin.y), 2, Scalar(0), 2); - } - result = this->decode(middle); - if (result.format == BarcodeType::NONE) - { - result = this->decode(std::vector(middle.crbegin(), middle.crend())); - } - if (result.format == BarcodeType::NONE) - { - cv::line(debug_img, begin, end, Scalar(0), 2); - cv::putText(debug_img, result.result, begin, cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 0, 255), 1); - } -} -#endif - -bool UPCEANDecoder::findGuardPatterns(const std::vector &row, uint rowOffset, uchar whiteFirst, - const std::vector &pattern, Counter &counter, std::pair &result) -{ - size_t patternLength = pattern.size(); - size_t width = row.size(); - uchar color = whiteFirst ? WHITE : BLACK; - rowOffset = (int) (std::find(row.cbegin() + rowOffset, row.cend(), color) - row.cbegin()); - uint counterPosition = 0; - uint patternStart = rowOffset; - for (uint x = rowOffset; x < width; x++) - { - if (row[x] == color) - { - counter.pattern[counterPosition]++; - counter.sum++; - } - else - { - if (counterPosition == patternLength - 1) - { - if (patternMatch(counter, pattern, MAX_INDIVIDUAL_VARIANCE) < MAX_AVG_VARIANCE) - { - result.first = patternStart; - result.second = x; - return true; - } - patternStart += counter.pattern[0] + counter.pattern[1]; - counter.sum -= counter.pattern[0] + counter.pattern[1]; - - std::copy(counter.pattern.begin() + 2, counter.pattern.end(), counter.pattern.begin()); - - counter.pattern[patternLength - 2] = 0; - counter.pattern[patternLength - 1] = 0; - counterPosition--; - } - else - { - counterPosition++; - } - counter.pattern[counterPosition] = 1; - counter.sum++; - color = (std::numeric_limits::max() - color); - } - } - return false; -} - -bool UPCEANDecoder::findStartGuardPatterns(const std::vector &row, std::pair &start_range) -{ - bool is_find = false; - int next_start = 0; - while (!is_find) - { - Counter guard_counters(std::vector{0, 0, 0}); - if (!findGuardPatterns(row, next_start, BLACK, BEGIN_PATTERN(), guard_counters, start_range)) - { - return false; - } - int start = static_cast(start_range.first); - next_start = static_cast(start_range.second); - int quiet_start = max(start - (next_start - start), 0); - is_find = (quiet_start != start) && - (std::find(std::begin(row) + quiet_start, std::begin(row) + start, BLACK) == std::begin(row) + start); - } - return true; -} - -int UPCEANDecoder::decodeDigit(const std::vector &row, Counter &counters, uint rowOffset, - const std::vector> &patterns) -{ - fillCounter(row, rowOffset, counters); - int bestMatch = -1; - uint bestVariance = MAX_AVG_VARIANCE; // worst variance we'll accept - int i = 0; - for (const auto &pattern : patterns) - { - uint variance = patternMatch(counters, pattern, MAX_INDIVIDUAL_VARIANCE); - if (variance < bestVariance) - { - bestVariance = variance; - bestMatch = i; - } - i++; - } - return std::max(-1, bestMatch); - // -1 is Mismatch or means error. -} - -/*Input a ROI mat return result */ -std::pair UPCEANDecoder::decodeROI(const Mat &bar_img) const -{ - if ((size_t) bar_img.cols < this->bits_num) - { - return std::make_pair(Result{string(), BarcodeType::NONE}, 0.0F); - } - - std::map result_vote; - std::map format_vote; - int vote_cnt = 0; - int total_vote = 0; - std::string max_result; - BarcodeType max_type = BarcodeType::NONE; - - const int step = bar_img.rows / (DIVIDE_PART + BIAS_PART); - Result result; - int row_num; - for (int i = 0; i < DIVIDE_PART; ++i) - { - row_num = (i + BIAS_PART / 2) * step; - if (row_num < 0 || row_num > bar_img.rows) - { - continue; - } - const auto *ptr = bar_img.ptr(row_num); - vector line(ptr, ptr + bar_img.cols); - result = decodeLine(line); - if (result.format != BarcodeType::NONE) - { - total_vote++; - result_vote[result.result] += 1; - if (result_vote[result.result] > vote_cnt) - { - vote_cnt = result_vote[result.result]; - max_result = result.result; - max_type = result.format; - } - } - } - if (total_vote == 0 || (vote_cnt << 2) < total_vote) - { - return std::make_pair(Result(string(), BarcodeType::NONE), 0.0f); - } - - float confidence = (float) vote_cnt / (float) DIVIDE_PART; - //Check if it is UPC-A format - if (max_type == BarcodeType::EAN_13 && max_result[0] == '0') - { - max_result = max_result.substr(1, 12); //UPC-A length 12 - max_type = UPC_A; - } - return std::make_pair(Result(max_result, max_type), confidence); -} - - -Result UPCEANDecoder::decodeLine(const vector &line) const -{ - Result result = this->decode(line); - if (result.format == BarcodeType::NONE) - { - result = this->decode(std::vector(line.crbegin(), line.crend())); - } - return result; -} - -bool UPCEANDecoder::isValid(const string &result) const -{ - if (result.size() != digit_number) - { - return false; - } - int sum = 0; - for (int index = (int) result.size() - 2, i = 1; index >= 0; index--, i++) - { - int temp = result[index] - '0'; - sum += (temp + ((i & 1) != 0 ? temp << 1 : 0)); - } - return (result.back() - '0') == ((10 - (sum % 10)) % 10); -} - -// right for A -const std::vector> &get_A_or_C_Patterns() -{ - static const std::vector> A_or_C_Patterns{{3, 2, 1, 1}, // 0 - {2, 2, 2, 1}, // 1 - {2, 1, 2, 2}, // 2 - {1, 4, 1, 1}, // 3 - {1, 1, 3, 2}, // 4 - {1, 2, 3, 1}, // 5 - {1, 1, 1, 4}, // 6 - {1, 3, 1, 2}, // 7 - {1, 2, 1, 3}, // 8 - {3, 1, 1, 2} // 9 - }; - return A_or_C_Patterns; -} - -const std::vector> &get_AB_Patterns() -{ - static const std::vector> AB_Patterns = [] { - constexpr uint offset = 10; - auto AB_Patterns_inited = std::vector>(offset << 1, std::vector(PATTERN_LENGTH, 0)); - std::copy(get_A_or_C_Patterns().cbegin(), get_A_or_C_Patterns().cend(), AB_Patterns_inited.begin()); - //AB pattern is - for (uint i = 0; i < offset; ++i) - { - for (uint j = 0; j < PATTERN_LENGTH; ++j) - { - AB_Patterns_inited[i + offset][j] = AB_Patterns_inited[i][PATTERN_LENGTH - j - 1]; - } - } - return AB_Patterns_inited; - }(); - return AB_Patterns; -} - -const std::vector &BEGIN_PATTERN() -{ - // it just need it's 1:1:1(black:white:black) - static const std::vector BEGIN_PATTERN_(3, 1); - return BEGIN_PATTERN_; -} - -const std::vector &MIDDLE_PATTERN() -{ - // it just need it's 1:1:1:1:1(white:black:white:black:white) - static const std::vector MIDDLE_PATTERN_(5, 1); - return MIDDLE_PATTERN_; -} - -const std::array &FIRST_CHAR_ARRAY() -{ - // use array to simulation a Hashmap, - // because the data's size is small, - // use a hashmap or brute-force search 10 times both can not accept - static const std::array pattern{ - '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x00', '\x06', '\x00', '\x00', '\x00', '\x09', '\x00', - '\x08', '\x03', '\x00', '\x00', '\x00', '\x00', '\x05', '\x00', '\x07', '\x02', '\x00', '\x00', '\x04', - '\x01', '\x00', '\x00', '\x00', '\x00', '\x00'}; - // length is 32 to ensure the security - // 0x00000 -> 0 -> 0 - // 0x11010 -> 26 -> 1 - // 0x10110 -> 22 -> 2 - // 0x01110 -> 14 -> 3 - // 0x11001 -> 25 -> 4 - // 0x10011 -> 19 -> 5 - // 0x00111 -> 7 -> 6 - // 0x10101 -> 21 -> 7 - // 0x01101 -> 13 -> 8 - // 0x01011 -> 11 -> 9 - // delete the 1-13's 2 number's bit, - // it always be A which do not need to count. - return pattern; -} -} - -} // namespace cv diff --git a/modules/barcode/src/decoder/upcean_decoder.hpp b/modules/barcode/src/decoder/upcean_decoder.hpp deleted file mode 100644 index ecb2aec81..000000000 --- a/modules/barcode/src/decoder/upcean_decoder.hpp +++ /dev/null @@ -1,67 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#ifndef __OPENCV_BARCODE_UPCEAN_DECODER_HPP__ -#define __OPENCV_BARCODE_UPCEAN_DECODER_HPP__ - -#include "abs_decoder.hpp" - -/** - * upcean_decoder the abstract basic class for decode formats, - * it will have ean13/8,upc_a,upc_e , etc.. class extend this class -*/ -namespace cv { -namespace barcode { -using std::string; -using std::vector; - -class UPCEANDecoder : public AbsDecoder -{ - -public: - ~UPCEANDecoder() override = default; - - std::pair decodeROI(const Mat &bar_img) const override; - -protected: - static int decodeDigit(const std::vector &row, Counter &counters, uint rowOffset, - const std::vector> &patterns); - - static bool - findGuardPatterns(const std::vector &row, uint rowOffset, uchar whiteFirst, const std::vector &pattern, - Counter &counter, std::pair &result); - - static bool findStartGuardPatterns(const std::vector &row, std::pair &start_range); - - Result decodeLine(const vector &line) const; - - Result decode(const vector &bar) const override = 0; - - bool isValid(const string &result) const override; - -private: - #if 0 - void drawDebugLine(Mat &debug_img, const Point2i &begin, const Point2i &end) const; - #endif -}; - -const std::vector> &get_A_or_C_Patterns(); - -const std::vector> &get_AB_Patterns(); - -const std::vector &BEGIN_PATTERN(); - -const std::vector &MIDDLE_PATTERN(); - -const std::array &FIRST_CHAR_ARRAY(); - -constexpr static uint PATTERN_LENGTH = 4; -constexpr static uint MAX_AVG_VARIANCE = static_cast(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.48f); -constexpr static uint MAX_INDIVIDUAL_VARIANCE = static_cast(PATTERN_MATCH_RESULT_SCALE_FACTOR * 0.7f); - -} -} // namespace cv - -#endif //! __OPENCV_BARCODE_UPCEAN_DECODER_HPP__ diff --git a/modules/barcode/src/detector/bardetect.cpp b/modules/barcode/src/detector/bardetect.cpp deleted file mode 100644 index ec0806970..000000000 --- a/modules/barcode/src/detector/bardetect.cpp +++ /dev/null @@ -1,410 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#include "../precomp.hpp" -#include "bardetect.hpp" - - -namespace cv { -namespace barcode { -static constexpr float PI = static_cast(CV_PI); -static constexpr float HALF_PI = static_cast(CV_PI / 2); - -#define CALCULATE_SUM(ptr, result) \ - top_left = static_cast(*((ptr) + left_col + integral_cols * top_row));\ - top_right = static_cast(*((ptr) + integral_cols * top_row + right_col));\ - bottom_right = static_cast(*((ptr) + right_col + bottom_row * integral_cols));\ - bottom_left = static_cast(*((ptr) + bottom_row * integral_cols + left_col));\ - (result) = (bottom_right - bottom_left - top_right + top_left); - - -inline bool Detect::isValidCoord(const Point &coord, const Size &limit) -{ - if ((coord.x < 0) || (coord.y < 0)) - { - return false; - } - - if ((unsigned) coord.x > (unsigned) (limit.width - 1) || ((unsigned) coord.y > (unsigned) (limit.height - 1))) - { - return false; - } - - return true; -} - - -void Detect::init(const Mat &src) -{ - const double min_side = std::min(src.size().width, src.size().height); - if (min_side > 512.0) - { - purpose = SHRINKING; - coeff_expansion = min_side / 512.0; - width = cvRound(src.size().width / coeff_expansion); - height = cvRound(src.size().height / coeff_expansion); - Size new_size(width, height); - resize(src, resized_barcode, new_size, 0, 0, INTER_AREA); - } -// else if (min_side < 512.0) -// { -// purpose = ZOOMING; -// coeff_expansion = 512.0 / min_side; -// width = cvRound(src.size().width * coeff_expansion); -// height = cvRound(src.size().height * coeff_expansion); -// Size new_size(width, height); -// resize(src, resized_barcode, new_size, 0, 0, INTER_CUBIC); -// } - else - { - purpose = UNCHANGED; - coeff_expansion = 1.0; - width = src.size().width; - height = src.size().height; - resized_barcode = src.clone(); - } - // median blur: sometimes it reduces the noise, but also reduces the recall - // medianBlur(resized_barcode, resized_barcode, 3); - -} - - -void Detect::localization() -{ - - localization_bbox.clear(); - bbox_scores.clear(); - - // get integral image - preprocess(); - // empirical setting - static constexpr float SCALE_LIST[] = {0.01f, 0.03f, 0.06f, 0.08f}; - const auto min_side = static_cast(std::min(width, height)); - int window_size; - for (const float scale:SCALE_LIST) - { - window_size = cvRound(min_side * scale); - if(window_size == 0) { - window_size = 1; - } - calCoherence(window_size); - barcodeErode(); - regionGrowing(window_size); - } - -} - - -bool Detect::computeTransformationPoints() -{ - - bbox_indices.clear(); - transformation_points.clear(); - transformation_points.reserve(bbox_indices.size()); - RotatedRect rect; - Point2f temp[4]; - const float THRESHOLD_SCORE = float(width * height) / 300.f; - dnn::NMSBoxes(localization_bbox, bbox_scores, THRESHOLD_SCORE, 0.1f, bbox_indices); - - for (const auto &bbox_index : bbox_indices) - { - rect = localization_bbox[bbox_index]; - if (purpose == ZOOMING) - { - rect.center /= coeff_expansion; - rect.size.height /= static_cast(coeff_expansion); - rect.size.width /= static_cast(coeff_expansion); - } - else if (purpose == SHRINKING) - { - rect.center *= coeff_expansion; - rect.size.height *= static_cast(coeff_expansion); - rect.size.width *= static_cast(coeff_expansion); - } - rect.points(temp); - transformation_points.emplace_back(vector{temp[0], temp[1], temp[2], temp[3]}); - } - - return !transformation_points.empty(); -} - - -void Detect::preprocess() -{ - Mat scharr_x, scharr_y, temp; - static constexpr double THRESHOLD_MAGNITUDE = 64.; - Scharr(resized_barcode, scharr_x, CV_32F, 1, 0); - Scharr(resized_barcode, scharr_y, CV_32F, 0, 1); - // calculate magnitude of gradient and truncate - magnitude(scharr_x, scharr_y, temp); - threshold(temp, temp, THRESHOLD_MAGNITUDE, 1, THRESH_BINARY); - temp.convertTo(gradient_magnitude, CV_8U); - integral(gradient_magnitude, integral_edges, CV_32F); - - - for (int y = 0; y < height; y++) - { - auto *const x_row = scharr_x.ptr(y); - auto *const y_row = scharr_y.ptr(y); - auto *const magnitude_row = gradient_magnitude.ptr(y); - for (int pos = 0; pos < width; pos++) - { - if (magnitude_row[pos] == 0) - { - x_row[pos] = 0; - y_row[pos] = 0; - continue; - } - if (x_row[pos] < 0) - { - x_row[pos] *= -1; - y_row[pos] *= -1; - } - } - } - integral(scharr_x, temp, integral_x_sq, CV_32F, CV_32F); - integral(scharr_y, temp, integral_y_sq, CV_32F, CV_32F); - integral(scharr_x.mul(scharr_y), integral_xy, temp, CV_32F, CV_32F); -} - - -// Change coherence orientation edge_nums -// depend on width height integral_edges integral_x_sq integral_y_sq integral_xy -void Detect::calCoherence(int window_size) -{ - static constexpr float THRESHOLD_COHERENCE = 0.9f; - int right_col, left_col, top_row, bottom_row; - float xy, x_sq, y_sq, d, rect_area; - const float THRESHOLD_AREA = float(window_size * window_size) * 0.42f; - Size new_size(width / window_size, height / window_size); - coherence = Mat(new_size, CV_8U), orientation = Mat(new_size, CV_32F), edge_nums = Mat(new_size, CV_32F); - - float top_left, top_right, bottom_left, bottom_right; - int integral_cols = width + 1; - const auto *edges_ptr = integral_edges.ptr(), *x_sq_ptr = integral_x_sq.ptr(), *y_sq_ptr = integral_y_sq.ptr(), *xy_ptr = integral_xy.ptr(); - for (int y = 0; y < new_size.height; y++) - { - auto *coherence_row = coherence.ptr(y); - auto *orientation_row = orientation.ptr(y); - auto *edge_nums_row = edge_nums.ptr(y); - if (y * window_size >= height) - { - continue; - } - top_row = y * window_size; - bottom_row = min(height, (y + 1) * window_size); - - for (int pos = 0; pos < new_size.width; pos++) - { - - // then calculate the column locations of the rectangle and set them to -1 - // if they are outside the matrix bounds - if (pos * window_size >= width) - { - continue; - } - left_col = pos * window_size; - right_col = min(width, (pos + 1) * window_size); - - //we had an integral image to count non-zero elements - CALCULATE_SUM(edges_ptr, rect_area) - if (rect_area < THRESHOLD_AREA) - { - // smooth region - coherence_row[pos] = 0; - continue; - } - - CALCULATE_SUM(x_sq_ptr, x_sq) - CALCULATE_SUM(y_sq_ptr, y_sq) - CALCULATE_SUM(xy_ptr, xy) - - // get the values of the rectangle corners from the integral image - 0 if outside bounds - d = sqrt((x_sq - y_sq) * (x_sq - y_sq) + 4 * xy * xy) / (x_sq + y_sq); - if (d > THRESHOLD_COHERENCE) - { - coherence_row[pos] = 255; - orientation_row[pos] = atan2(x_sq - y_sq, 2 * xy) / 2.0f; - edge_nums_row[pos] = rect_area; - } - else - { - coherence_row[pos] = 0; - } - - } - - } -} - -// will change localization_bbox bbox_scores -// will change coherence, -// depend on coherence orientation edge_nums -void Detect::regionGrowing(int window_size) -{ - static constexpr float LOCAL_THRESHOLD_COHERENCE = 0.95f, THRESHOLD_RADIAN = - PI / 30, LOCAL_RATIO = 0.5f, EXPANSION_FACTOR = 1.2f; - static constexpr uint THRESHOLD_BLOCK_NUM = 35; - Point pt_to_grow, pt; //point to grow - - float src_value; - float cur_value; - float edge_num; - float rect_orientation; - float sin_sum, cos_sum; - uint counter; - //grow direction - static constexpr int DIR[8][2] = {{-1, -1}, - {0, -1}, - {1, -1}, - {1, 0}, - {1, 1}, - {0, 1}, - {-1, 1}, - {-1, 0}}; - vector growingPoints, growingImgPoints; - for (int y = 0; y < coherence.rows; y++) - { - auto *coherence_row = coherence.ptr(y); - - for (int x = 0; x < coherence.cols; x++) - { - if (coherence_row[x] == 0) - { - continue; - } - // flag - coherence_row[x] = 0; - growingPoints.clear(); - growingImgPoints.clear(); - - pt = Point(x, y); - cur_value = orientation.at(pt); - sin_sum = sin(2 * cur_value); - cos_sum = cos(2 * cur_value); - counter = 1; - edge_num = edge_nums.at(pt); - growingPoints.push_back(pt); - growingImgPoints.push_back(Point(pt)); - while (!growingPoints.empty()) - { - pt = growingPoints.back(); - growingPoints.pop_back(); - src_value = orientation.at(pt); - - //growing in eight directions - for (auto i : DIR) - { - pt_to_grow = Point(pt.x + i[0], pt.y + i[1]); - - //check if out of boundary - if (!isValidCoord(pt_to_grow, coherence.size())) - { - continue; - } - - if (coherence.at(pt_to_grow) == 0) - { - continue; - } - cur_value = orientation.at(pt_to_grow); - if (abs(cur_value - src_value) < THRESHOLD_RADIAN || - abs(cur_value - src_value) > PI - THRESHOLD_RADIAN) - { - coherence.at(pt_to_grow) = 0; - sin_sum += sin(2 * cur_value); - cos_sum += cos(2 * cur_value); - counter += 1; - edge_num += edge_nums.at(pt_to_grow); - growingPoints.push_back(pt_to_grow); //push next point to grow back to stack - growingImgPoints.push_back(pt_to_grow); - } - } - } - //minimum block num - if (counter < THRESHOLD_BLOCK_NUM) - { - continue; - } - float local_coherence = (sin_sum * sin_sum + cos_sum * cos_sum) / static_cast(counter * counter); - // minimum local gradient orientation_arg coherence_arg - if (local_coherence < LOCAL_THRESHOLD_COHERENCE) - { - continue; - } - RotatedRect minRect = minAreaRect(growingImgPoints); - if (edge_num < minRect.size.area() * float(window_size * window_size) * LOCAL_RATIO || - static_cast(counter) < minRect.size.area() * LOCAL_RATIO) - { - continue; - } - const float local_orientation = atan2(cos_sum, sin_sum) / 2.0f; - // only orientation_arg is approximately equal to the rectangle orientation_arg - rect_orientation = (minRect.angle) * PI / 180.f; - if (minRect.size.width < minRect.size.height) - { - rect_orientation += (rect_orientation <= 0.f ? HALF_PI : -HALF_PI); - std::swap(minRect.size.width, minRect.size.height); - } - if (abs(local_orientation - rect_orientation) > THRESHOLD_RADIAN && - abs(local_orientation - rect_orientation) < PI - THRESHOLD_RADIAN) - { - continue; - } - minRect.angle = local_orientation * 180.f / PI; - minRect.size.width *= static_cast(window_size) * EXPANSION_FACTOR; - minRect.size.height *= static_cast(window_size); - minRect.center.x = (minRect.center.x + 0.5f) * static_cast(window_size); - minRect.center.y = (minRect.center.y + 0.5f) * static_cast(window_size); - localization_bbox.push_back(minRect); - bbox_scores.push_back(edge_num); - - } - } -} - -inline const std::array &getStructuringElement() -{ - static const std::array structuringElement{ - Mat_{{3, 3}, - {255, 0, 0, 0, 0, 0, 0, 0, 255}}, Mat_{{3, 3}, - {0, 0, 255, 0, 0, 0, 255, 0, 0}}, - Mat_{{3, 3}, - {0, 0, 0, 255, 0, 255, 0, 0, 0}}, Mat_{{3, 3}, - {0, 255, 0, 0, 0, 0, 0, 255, 0}}}; - return structuringElement; -} - -// Change mat -void Detect::barcodeErode() -{ - static const std::array &structuringElement = getStructuringElement(); - Mat m0, m1, m2, m3; - dilate(coherence, m0, structuringElement[0]); - dilate(coherence, m1, structuringElement[1]); - dilate(coherence, m2, structuringElement[2]); - dilate(coherence, m3, structuringElement[3]); - int sum; - for (int y = 0; y < coherence.rows; y++) - { - auto coherence_row = coherence.ptr(y); - auto m0_row = m0.ptr(y); - auto m1_row = m1.ptr(y); - auto m2_row = m2.ptr(y); - auto m3_row = m3.ptr(y); - - for (int pos = 0; pos < coherence.cols; pos++) - { - if (coherence_row[pos] != 0) - { - sum = m0_row[pos] + m1_row[pos] + m2_row[pos] + m3_row[pos]; - //more than 2 group - coherence_row[pos] = sum > 600 ? 255 : 0; - } - } - } -} -} -} diff --git a/modules/barcode/src/detector/bardetect.hpp b/modules/barcode/src/detector/bardetect.hpp deleted file mode 100644 index 42a501ad6..000000000 --- a/modules/barcode/src/detector/bardetect.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#ifndef __OPENCV_BARCODE_BARDETECT_HPP__ -#define __OPENCV_BARCODE_BARDETECT_HPP__ - - -#include -#include - -namespace cv { -namespace barcode { -using std::vector; - -class Detect -{ -private: - vector localization_rects; - vector localization_bbox; - vector bbox_scores; - vector bbox_indices; - vector> transformation_points; - - -public: - void init(const Mat &src); - - void localization(); - - vector> getTransformationPoints() - { return transformation_points; } - - bool computeTransformationPoints(); - -protected: - enum resize_direction - { - ZOOMING, SHRINKING, UNCHANGED - } purpose = UNCHANGED; - - - double coeff_expansion = 1.0; - int height, width; - Mat resized_barcode, gradient_magnitude, coherence, orientation, edge_nums, integral_x_sq, integral_y_sq, integral_xy, integral_edges; - - void preprocess(); - - void calCoherence(int window_size); - - static inline bool isValidCoord(const Point &coord, const Size &limit); - - void regionGrowing(int window_size); - - void barcodeErode(); - - -}; -} -} - -#endif //__OPENCV_BARCODE_BARDETECT_HPP__ diff --git a/modules/barcode/src/precomp.hpp b/modules/barcode/src/precomp.hpp deleted file mode 100644 index 92db1c1cb..000000000 --- a/modules/barcode/src/precomp.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// This file is part of OpenCV project. -// It is subject to the license terms in the LICENSE file found in the top-level directory -// of this distribution and at http://opencv.org/license.html. -// Copyright (c) 2020-2021 darkliang wangberlinT Certseeds - -#ifndef __OPENCV_PRECOMP_H__ -#define __OPENCV_PRECOMP_H__ - -#include -#include -#include - -#ifdef _MSC_VER -#pragma warning(disable: 4244) -#endif -#endif diff --git a/modules/barcode/test/test_barcode.cpp b/modules/barcode/test/test_barcode.cpp deleted file mode 100644 index 85c8d28e6..000000000 --- a/modules/barcode/test/test_barcode.cpp +++ /dev/null @@ -1,79 +0,0 @@ -// 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{ -//init validation map -datasetType initValidation(std::string path) -{ - const std::string valid_path = findDataFile(path); - return buildDataSet(valid_path); -} - -TEST(Barcode_BarcodeDetector_single, regression) -{ - const std::string root = "barcode/single/"; - datasetType validation = initValidation(root + "result.csv"); - auto bardet = barcode::BarcodeDetector(); - datasetType::iterator iterator = validation.begin(); - while (iterator != validation.end()) - { - std::string img_name = iterator->first; - std::string result = iterator->second; - std::string image_path = findDataFile(root + img_name); - Mat img = imread(image_path); - EXPECT_FALSE(img.empty()) << "Can't read image: " << image_path; - std::vector points; - std::vector infos; - std::vector formats; - bardet.detectAndDecode(img, infos, formats, points); - EXPECT_FALSE(points.empty()) << "Nothing detected: " << image_path; - bool is_correct = false; - for (const auto &ans : infos) - { - if (ans == result) - { - is_correct = true; - break; - } - } - EXPECT_TRUE(is_correct) << "No results for " << img_name; - iterator++; - } -} - -TEST(Barcode_BarcodeDetector_detect_multi, detect_regression) -{ - const std::string root = "barcode/multiple/"; - datasetType validation = initValidation(root + "result.csv"); - auto bardet = barcode::BarcodeDetector(); - datasetType::iterator iterator = validation.begin(); - while (iterator != validation.end()) - { - std::string img = iterator->first; - size_t expect_corners_size = std::stoi(iterator->second); - std::string image_path = findDataFile(root + img); - Mat src = imread(image_path); - EXPECT_FALSE(src.empty()) << "Can't read image: " << image_path; - - std::vector corners; - bardet.detect(src, corners); - EXPECT_EQ(corners.size(), expect_corners_size) << "Can't detect all barcodes: " << img; - iterator++; - } -} - -TEST(Barcode_BarcodeDetector_basic, not_found_barcode) -{ - auto bardet = barcode::BarcodeDetector(); - std::vector corners; - vector decoded_info; - vector decoded_type; - Mat zero_image = Mat::zeros(256, 256, CV_8UC1); - EXPECT_FALSE(bardet.detect(zero_image, corners)); - corners = std::vector(4); - EXPECT_ANY_THROW(bardet.decode(zero_image, corners, decoded_info, decoded_type)); -} -}} // namespace diff --git a/modules/barcode/test/test_main.cpp b/modules/barcode/test/test_main.cpp deleted file mode 100644 index 0e51ddfd0..000000000 --- a/modules/barcode/test/test_main.cpp +++ /dev/null @@ -1,6 +0,0 @@ -// 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" - -CV_TEST_MAIN("cv") diff --git a/modules/barcode/test/test_precomp.hpp b/modules/barcode/test/test_precomp.hpp deleted file mode 100644 index 11dc8a103..000000000 --- a/modules/barcode/test/test_precomp.hpp +++ /dev/null @@ -1,11 +0,0 @@ -// 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/barcode.hpp" -#include "utils.hpp" - -#endif diff --git a/modules/barcode/test/utils.hpp b/modules/barcode/test/utils.hpp deleted file mode 100644 index 0a7007882..000000000 --- a/modules/barcode/test/utils.hpp +++ /dev/null @@ -1,59 +0,0 @@ -// 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_CONTRIB_UTILS_HPP -#define OPENCV_CONTRIB_UTILS_HPP - -#include -#include -#include -#include - -typedef std::vector stringvec; -typedef std::map datasetType; - -namespace opencv_test{namespace { -inline stringvec explode(const std::string &s, const char &c) -{ - std::string buff; - stringvec v; - - for (auto n:s) - { - if (n != c) { buff += n; } - else if (n == c && !buff.empty()) - { - v.push_back(buff); - buff = ""; - } - } - if (!buff.empty()) { v.push_back(buff); } - - return v; -} - -inline datasetType buildDataSet(std::string result_file_path) -{ - std::ifstream result_file; - datasetType dataset; - result_file.open(result_file_path); - std::string line; - if (result_file.is_open()) - { - while (std::getline(result_file, line)) - { - stringvec result = explode(line, ','); - std::string filename = result[0]; - if (dataset.find(filename) == dataset.end()) - { - dataset[filename] = result[1]; - } - } - } - - result_file.close(); - return dataset; -} -}} -#endif //OPENCV_CONTRIB_UTILS_HPP diff --git a/modules/barcode/tutorials/barcode_detect_and_decode/barcode_detect_and_decode.markdown b/modules/barcode/tutorials/barcode_detect_and_decode/barcode_detect_and_decode.markdown deleted file mode 100644 index c43d8b8f8..000000000 --- a/modules/barcode/tutorials/barcode_detect_and_decode/barcode_detect_and_decode.markdown +++ /dev/null @@ -1,73 +0,0 @@ -Bar code Recognition{#tutorial_barcode_detect_and_decode} -====================== - -Goal ----- - -In this chapter, - -- We will familiarize with the bar code detection and decoding methods available in OpenCV. - -Basics ----- - -Bar code is major technique to identify commodity in real life. A common bar code is a pattern of parallel lines arranged by black bars and white bars with vastly different reflectivity. Bar code recognition is to scan the bar code in the horizontal direction to get a string of binary codes composed of bars of different widths and colors, that is, the code information of the bar code. The content of bar code can be decoded by matching with various bar code encoding methods. For current work, we only support EAN13 encoding method. - -### EAN 13 - -The EAN-13 bar code is based on the UPC-A standard, which was first implemented in Europe by the International Item Coding Association and later gradually spread worldwide. Most of the common goods in life use EAN-13 barcode. - -for more detail see [EAN - Wikipedia](https://en.wikipedia.org/wiki/International_Article_Number) - -### BarcodeDetector -Several algorithms were introduced for bar code recognition. - -While coding, we firstly need to create a **cv::barcode::BarcodeDetector** object. It has mainly three member functions, which will be introduced in the following. - -#### Initilization - -User can construct BarcodeDetector with super resolution model which should be downloaded automatically to `/downloads/barcode`. If not, please download them from `https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode` - -or choose not to use super resolution. - -@snippet ./samples/barcode.cpp initialize -We need create variables to store the outputs. -@snippet ./samples/barcode.cpp output - -#### detect - -It is a algorithm based on directional coherence. First of all, we compute the average squared gradients of every pixels. It was proposed in the paper "Systematic methods for the computation of the directional fields and singular points of fingerprints" by A.M. Bazen and S.H. Gerez in 2002. Then we divide the image into some square patches and compute the **gradient orientation coherence** and **mean gradient direction** of each patch. At last we connected the patches that have **high gradient orientation coherence** and **similar gradient direction**. In this stage, we use multi-scale patches to capture the gradient distribution of multi-size bar codes, and apply non-maximum suppression to filter duplicate proposals. A last, we use minAreaRect() to bound the ROI, and output the corners of the rectangles. - -Detect codes in the input image, and output the corners of detected rectangles: - -@snippet ./samples/barcode.cpp detect - -#### decode - -This function first super-scales the image if it is smaller than threshold, sharpens the image and then binaries it by OTSU or local-binarization. At last reads the contents of the barcode by matching the similarity of the specified barcode pattern. Only EAN-13 barcode currently supported. - -You can find more information in **cv::barcode::BarcodeDetector::decode()**. - -#### detectAndDecode - -This function combines `detect` and `decode`. A simple example below to use this function showing recognized bar codes. - -@snippet ./samples/barcode.cpp detectAndDecode - -Visualize the results: -@snippet ./samples/barcode.cpp visualize - -Results -------- - -**Original Image** - -Below image shows four EAN 13 bar codes photoed by a smart phone. - -![image](images/4_barcodes.jpg) - -**Result of detectAndDecode** - -Bar codes are bounded by green box, and decoded numbers are lying on the boxes. - -![image](images/result.jpg) diff --git a/modules/barcode/tutorials/barcode_detect_and_decode/images/4_barcodes.jpg b/modules/barcode/tutorials/barcode_detect_and_decode/images/4_barcodes.jpg deleted file mode 100644 index f361bec56..000000000 Binary files a/modules/barcode/tutorials/barcode_detect_and_decode/images/4_barcodes.jpg and /dev/null differ diff --git a/modules/barcode/tutorials/barcode_detect_and_decode/images/result.jpg b/modules/barcode/tutorials/barcode_detect_and_decode/images/result.jpg deleted file mode 100644 index 0a13f18ed..000000000 Binary files a/modules/barcode/tutorials/barcode_detect_and_decode/images/result.jpg and /dev/null differ diff --git a/modules/barcode/tutorials/table_of_content_barcode.markdown b/modules/barcode/tutorials/table_of_content_barcode.markdown deleted file mode 100644 index eae18e6db..000000000 --- a/modules/barcode/tutorials/table_of_content_barcode.markdown +++ /dev/null @@ -1,6 +0,0 @@ -Tutorials for barcode module {#tutorial_table_of_content_barcode} -=============================================================== - -- @subpage tutorial_barcode_detect_and_decode - - Bar code is a widely used technology in real goods, we often need to detect and decode the bar code. It can be easy to implement via `barcode` module. diff --git a/modules/cudacodec/include/opencv2/cudacodec.hpp b/modules/cudacodec/include/opencv2/cudacodec.hpp index 602f9ff17..af8f169c1 100644 --- a/modules/cudacodec/include/opencv2/cudacodec.hpp +++ b/modules/cudacodec/include/opencv2/cudacodec.hpp @@ -309,12 +309,13 @@ enum DeinterlaceMode */ struct CV_EXPORTS_W_SIMPLE FormatInfo { - CV_WRAP FormatInfo() : nBitDepthMinus8(-1), ulWidth(0), ulHeight(0), width(0), height(0), ulMaxWidth(0), ulMaxHeight(0), valid(false), + CV_WRAP FormatInfo() : nBitDepthMinus8(-1), nBitDepthChromaMinus8(-1), ulWidth(0), ulHeight(0), width(0), height(0), ulMaxWidth(0), ulMaxHeight(0), valid(false), fps(0), ulNumDecodeSurfaces(0), videoFullRangeFlag(false) {}; CV_PROP_RW Codec codec; CV_PROP_RW ChromaFormat chromaFormat; CV_PROP_RW int nBitDepthMinus8; + CV_PROP_RW int nBitDepthChromaMinus8; CV_PROP_RW int ulWidth;//!< Coded sequence width in pixels. CV_PROP_RW int ulHeight;//!< Coded sequence height in pixels. CV_PROP_RW int width;//!< Width of the decoded frame returned by nextFrame(frame). diff --git a/modules/cudacodec/src/frame_queue.cpp b/modules/cudacodec/src/frame_queue.cpp index d73e04eb1..dafe5bb29 100644 --- a/modules/cudacodec/src/frame_queue.cpp +++ b/modules/cudacodec/src/frame_queue.cpp @@ -55,14 +55,29 @@ cv::cudacodec::detail::FrameQueue::~FrameQueue() { void cv::cudacodec::detail::FrameQueue::init(const int _maxSz) { AutoLock autoLock(mtx_); - if (isFrameInUse_) - return; + if (isFrameInUse_) return; maxSz = _maxSz; displayQueue_ = std::vector(maxSz, CUVIDPARSERDISPINFO()); isFrameInUse_ = new volatile int[maxSz]; std::memset((void*)isFrameInUse_, 0, sizeof(*isFrameInUse_) * maxSz); } +void cv::cudacodec::detail::FrameQueue::resize(const int newSz) { + if (newSz == maxSz) return; + if (!isFrameInUse_) return init(newSz); + AutoLock autoLock(mtx_); + const int maxSzOld = maxSz; maxSz = newSz; + const auto displayQueueOld = displayQueue_; + displayQueue_ = std::vector(maxSz, CUVIDPARSERDISPINFO()); + for (int i = readPosition_; i < readPosition_ + framesInQueue_; i++) + displayQueue_.at(i % displayQueue_.size()) = displayQueueOld.at(i % displayQueueOld.size()); + const volatile int* const isFrameInUseOld = isFrameInUse_; + isFrameInUse_ = new volatile int[maxSz]; + std::memset((void*)isFrameInUse_, 0, sizeof(*isFrameInUse_) * maxSz); + std::memcpy((void*)isFrameInUse_, (void*)isFrameInUseOld, sizeof(*isFrameInUseOld) * min(maxSz,maxSzOld)); + delete[] isFrameInUseOld; +} + bool cv::cudacodec::detail::FrameQueue::waitUntilFrameAvailable(int pictureIndex, const bool allowFrameDrop) { while (isInUse(pictureIndex)) @@ -79,6 +94,15 @@ bool cv::cudacodec::detail::FrameQueue::waitUntilFrameAvailable(int pictureIndex return true; } +bool cv::cudacodec::detail::FrameQueue::waitUntilEmpty() { + while (framesInQueue_) { + Thread::sleep(1); + if (isEndOfDecode()) + return false; + } + return true; +} + void cv::cudacodec::detail::FrameQueue::enqueue(const CUVIDPARSERDISPINFO* picParams, const std::vector rawPackets) { // Mark the frame as 'in-use' so we don't re-use it for decoding until it is no longer needed diff --git a/modules/cudacodec/src/frame_queue.hpp b/modules/cudacodec/src/frame_queue.hpp index 840b23c5d..4eb17809e 100644 --- a/modules/cudacodec/src/frame_queue.hpp +++ b/modules/cudacodec/src/frame_queue.hpp @@ -66,6 +66,12 @@ public: ~FrameQueue(); void init(const int _maxSz); + // Resize the current frame queue keeping any existing queued values - must only + // be called in the same thread as enqueue. + // Parameters: + // newSz - new size of the frame queue. + void resize(const int newSz); + void endDecode() { endOfDecode_ = true; } bool isEndOfDecode() const { return endOfDecode_ != 0;} @@ -77,6 +83,8 @@ public: // to ensure a frame is available. bool waitUntilFrameAvailable(int pictureIndex, const bool allowFrameDrop = false); + bool waitUntilEmpty(); + void enqueue(const CUVIDPARSERDISPINFO* picParams, const std::vector rawPackets); // Deque the next frame. @@ -97,6 +105,7 @@ public: bool dequeueUntil(const int pictureIndex); void releaseFrame(const CUVIDPARSERDISPINFO& picParams) { isFrameInUse_[picParams.picture_index] = 0; } + int getMaxSz() { return maxSz; } private: bool isInUse(int pictureIndex) const { return isFrameInUse_[pictureIndex] != 0; } diff --git a/modules/cudacodec/src/video_decoder.cpp b/modules/cudacodec/src/video_decoder.cpp index f828b08c1..e776f9334 100644 --- a/modules/cudacodec/src/video_decoder.cpp +++ b/modules/cudacodec/src/video_decoder.cpp @@ -124,8 +124,8 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat) cuSafeCall(cuvidGetDecoderCaps(&decodeCaps)); cuSafeCall(cuCtxPopCurrent(NULL)); if (!(decodeCaps.bIsSupported && (decodeCaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_NV12)))){ - CV_Error(Error::StsUnsupportedFormat, "Video source is not supported by hardware video decoder"); CV_LOG_ERROR(NULL, "Video source is not supported by hardware video decoder."); + CV_Error(Error::StsUnsupportedFormat, "Video source is not supported by hardware video decoder"); } CV_Assert(videoFormat.ulWidth >= decodeCaps.nMinWidth && videoFormat.ulHeight >= decodeCaps.nMinHeight && @@ -162,6 +162,61 @@ void cv::cudacodec::detail::VideoDecoder::create(const FormatInfo& videoFormat) cuSafeCall(cuCtxPushCurrent(ctx_)); cuSafeCall(cuvidCreateDecoder(&decoder_, &createInfo_)); cuSafeCall(cuCtxPopCurrent(NULL)); + inited_ = true; +} + +int cv::cudacodec::detail::VideoDecoder::reconfigure(const FormatInfo& videoFormat) { + if (videoFormat.nBitDepthMinus8 != videoFormat_.nBitDepthMinus8 || videoFormat.nBitDepthChromaMinus8 != videoFormat_.nBitDepthChromaMinus8) { + CV_LOG_ERROR(NULL, "Reconfigure Not supported for bit depth change"); + CV_Error(Error::StsUnsupportedFormat, "Reconfigure Not supported for bit depth change"); + } + + if (videoFormat.chromaFormat != videoFormat_.chromaFormat) { + CV_LOG_ERROR(NULL, "Reconfigure Not supported for chroma format change"); + CV_Error(Error::StsUnsupportedFormat, "Reconfigure Not supported for chroma format change"); + } + + const bool decodeResChange = !(videoFormat.ulWidth == videoFormat_.ulWidth && videoFormat.ulHeight == videoFormat_.ulHeight); + + if ((videoFormat.ulWidth > videoFormat_.ulMaxWidth) || (videoFormat.ulHeight > videoFormat_.ulMaxHeight)) { + // For VP9, let driver handle the change if new width/height > maxwidth/maxheight + if (videoFormat.codec != Codec::VP9) { + CV_LOG_ERROR(NULL, "Reconfigure Not supported when width/height > maxwidth/maxheight"); + CV_Error(Error::StsUnsupportedFormat, "Reconfigure Not supported when width/height > maxwidth/maxheight"); + } + } + + if (!decodeResChange) + return 1; + + { + AutoLock autoLock(mtx_); + videoFormat_.ulNumDecodeSurfaces = videoFormat.ulNumDecodeSurfaces; + videoFormat_.ulWidth = videoFormat.ulWidth; + videoFormat_.ulHeight = videoFormat.ulHeight; + videoFormat_.targetRoi = videoFormat.targetRoi; + } + + CUVIDRECONFIGUREDECODERINFO reconfigParams = { 0 }; + reconfigParams.ulWidth = videoFormat_.ulWidth; + reconfigParams.ulHeight = videoFormat_.ulHeight; + reconfigParams.display_area.left = videoFormat_.displayArea.x; + reconfigParams.display_area.right = videoFormat_.displayArea.x + videoFormat_.displayArea.width; + reconfigParams.display_area.top = videoFormat_.displayArea.y; + reconfigParams.display_area.bottom = videoFormat_.displayArea.y + videoFormat_.displayArea.height; + reconfigParams.ulTargetWidth = videoFormat_.width; + reconfigParams.ulTargetHeight = videoFormat_.height; + reconfigParams.target_rect.left = videoFormat_.targetRoi.x; + reconfigParams.target_rect.right = videoFormat_.targetRoi.x + videoFormat_.targetRoi.width; + reconfigParams.target_rect.top = videoFormat_.targetRoi.y; + reconfigParams.target_rect.bottom = videoFormat_.targetRoi.y + videoFormat_.targetRoi.height; + reconfigParams.ulNumDecodeSurfaces = videoFormat_.ulNumDecodeSurfaces; + + cuSafeCall(cuCtxPushCurrent(ctx_)); + cuSafeCall(cuvidReconfigureDecoder(decoder_, &reconfigParams)); + cuSafeCall(cuCtxPopCurrent(NULL)); + CV_LOG_INFO(NULL, "Reconfiguring Decoder"); + return videoFormat_.ulNumDecodeSurfaces; } void cv::cudacodec::detail::VideoDecoder::release() diff --git a/modules/cudacodec/src/video_decoder.hpp b/modules/cudacodec/src/video_decoder.hpp index 76d731f20..c2e8d7efd 100644 --- a/modules/cudacodec/src/video_decoder.hpp +++ b/modules/cudacodec/src/video_decoder.hpp @@ -68,9 +68,10 @@ public: } void create(const FormatInfo& videoFormat); + int reconfigure(const FormatInfo& videoFormat); void release(); - // Get the code-type currently used. + // Get the codec-type currently used. cudaVideoCodec codec() const { return static_cast(videoFormat_.codec); } int nDecodeSurfaces() const { return videoFormat_.ulNumDecodeSurfaces; } cv::Size getTargetSz() const { return videoFormat_.targetSz; } @@ -84,6 +85,8 @@ public: unsigned long targetWidth() { return videoFormat_.width; } unsigned long targetHeight() { return videoFormat_.height; } + bool inited() { return inited_; } + cudaVideoChromaFormat chromaFormat() const { return static_cast(videoFormat_.chromaFormat); } int nBitDepthMinus8() const { return videoFormat_.nBitDepthMinus8; } @@ -114,6 +117,7 @@ private: CUvideodecoder decoder_ = 0; FormatInfo videoFormat_ = {}; Mutex mtx_; + bool inited_ = false; }; }}} diff --git a/modules/cudacodec/src/video_parser.cpp b/modules/cudacodec/src/video_parser.cpp index ce8b80fe1..459db17da 100644 --- a/modules/cudacodec/src/video_parser.cpp +++ b/modules/cudacodec/src/video_parser.cpp @@ -68,6 +68,7 @@ bool cv::cudacodec::detail::VideoParser::parseVideoData(const unsigned char* dat CUVIDSOURCEDATAPACKET packet; std::memset(&packet, 0, sizeof(CUVIDSOURCEDATAPACKET)); + packet.flags = CUVID_PKT_TIMESTAMP; if (endOfStream) packet.flags |= CUVID_PKT_ENDOFSTREAM; @@ -107,68 +108,69 @@ int CUDAAPI cv::cudacodec::detail::VideoParser::HandleVideoSequence(void* userDa thiz->unparsedPackets_ = 0; - if (format->codec != thiz->videoDecoder_->codec() || - format->coded_width != thiz->videoDecoder_->frameWidth() || - format->coded_height != thiz->videoDecoder_->frameHeight() || - format->chroma_format != thiz->videoDecoder_->chromaFormat()|| - format->bit_depth_luma_minus8 != thiz->videoDecoder_->nBitDepthMinus8() || - format->min_num_decode_surfaces != thiz->videoDecoder_->nDecodeSurfaces()) + FormatInfo newFormat; + newFormat.videoFullRangeFlag = format->video_signal_description.video_full_range_flag; + newFormat.codec = static_cast(format->codec); + newFormat.chromaFormat = static_cast(format->chroma_format); + newFormat.nBitDepthMinus8 = format->bit_depth_luma_minus8; + newFormat.nBitDepthChromaMinus8 = format->bit_depth_chroma_minus8; + newFormat.ulWidth = format->coded_width; + newFormat.ulHeight = format->coded_height; + newFormat.fps = format->frame_rate.numerator / static_cast(format->frame_rate.denominator); + newFormat.targetSz = thiz->videoDecoder_->getTargetSz(); + newFormat.srcRoi = thiz->videoDecoder_->getSrcRoi(); + if (newFormat.srcRoi.empty()) { + newFormat.displayArea = Rect(Point(format->display_area.left, format->display_area.top), Point(format->display_area.right, format->display_area.bottom)); + if (newFormat.targetSz.empty()) + newFormat.targetSz = Size((format->display_area.right - format->display_area.left), (format->display_area.bottom - format->display_area.top)); + } + else + newFormat.displayArea = newFormat.srcRoi; + newFormat.width = newFormat.targetSz.width ? newFormat.targetSz.width : format->coded_width; + newFormat.height = newFormat.targetSz.height ? newFormat.targetSz.height : format->coded_height; + newFormat.targetRoi = thiz->videoDecoder_->getTargetRoi(); + newFormat.ulNumDecodeSurfaces = min(!thiz->allowFrameDrop_ ? max(thiz->videoDecoder_->nDecodeSurfaces(), static_cast(format->min_num_decode_surfaces)) : + format->min_num_decode_surfaces * 2, 32); + if (format->progressive_sequence) + newFormat.deinterlaceMode = Weave; + else + newFormat.deinterlaceMode = Adaptive; + int maxW = 0, maxH = 0; + // AV1 has max width/height of sequence in sequence header + if (format->codec == cudaVideoCodec_AV1 && format->seqhdr_data_length > 0) { - FormatInfo newFormat; - newFormat.videoFullRangeFlag = format->video_signal_description.video_full_range_flag; - newFormat.codec = static_cast(format->codec); - newFormat.chromaFormat = static_cast(format->chroma_format); - newFormat.nBitDepthMinus8 = format->bit_depth_luma_minus8; - newFormat.ulWidth = format->coded_width; - newFormat.ulHeight = format->coded_height; - newFormat.fps = format->frame_rate.numerator / static_cast(format->frame_rate.denominator); - newFormat.targetSz = thiz->videoDecoder_->getTargetSz(); - newFormat.srcRoi = thiz->videoDecoder_->getSrcRoi(); - if (newFormat.srcRoi.empty()) { - newFormat.displayArea = Rect(Point(format->display_area.left, format->display_area.top), Point(format->display_area.right, format->display_area.bottom)); - if (newFormat.targetSz.empty()) - newFormat.targetSz = Size((format->display_area.right - format->display_area.left), (format->display_area.bottom - format->display_area.top)); - } - else - newFormat.displayArea = newFormat.srcRoi; - newFormat.width = newFormat.targetSz.width ? newFormat.targetSz.width : format->coded_width; - newFormat.height = newFormat.targetSz.height ? newFormat.targetSz.height : format->coded_height; - newFormat.targetRoi = thiz->videoDecoder_->getTargetRoi(); - newFormat.ulNumDecodeSurfaces = min(!thiz->allowFrameDrop_ ? max(thiz->videoDecoder_->nDecodeSurfaces(), static_cast(format->min_num_decode_surfaces)) : - format->min_num_decode_surfaces * 2, 32); - if (format->progressive_sequence) - newFormat.deinterlaceMode = Weave; - else - newFormat.deinterlaceMode = Adaptive; - int maxW = 0, maxH = 0; - // AV1 has max width/height of sequence in sequence header - if (format->codec == cudaVideoCodec_AV1 && format->seqhdr_data_length > 0) - { - CUVIDEOFORMATEX* vidFormatEx = (CUVIDEOFORMATEX*)format; - maxW = vidFormatEx->av1.max_width; - maxH = vidFormatEx->av1.max_height; + CUVIDEOFORMATEX* vidFormatEx = (CUVIDEOFORMATEX*)format; + maxW = vidFormatEx->av1.max_width; + maxH = vidFormatEx->av1.max_height; + } + if (maxW < (int)format->coded_width) + maxW = format->coded_width; + if (maxH < (int)format->coded_height) + maxH = format->coded_height; + newFormat.ulMaxWidth = maxW; + newFormat.ulMaxHeight = maxH; + + thiz->frameQueue_->waitUntilEmpty(); + int retVal = newFormat.ulNumDecodeSurfaces; + try + { + if (thiz->videoDecoder_->inited()) { + retVal = thiz->videoDecoder_->reconfigure(newFormat); + if (retVal > 1 && newFormat.ulNumDecodeSurfaces != thiz->frameQueue_->getMaxSz()) + thiz->frameQueue_->resize(newFormat.ulNumDecodeSurfaces); } - if (maxW < (int)format->coded_width) - maxW = format->coded_width; - if (maxH < (int)format->coded_height) - maxH = format->coded_height; - newFormat.ulMaxWidth = maxW; - newFormat.ulMaxHeight = maxH; - thiz->frameQueue_->init(newFormat.ulNumDecodeSurfaces); - try - { - thiz->videoDecoder_->release(); + else { + thiz->frameQueue_->init(newFormat.ulNumDecodeSurfaces); thiz->videoDecoder_->create(newFormat); } - catch (const cv::Exception&) - { - CV_LOG_ERROR(NULL, "Attempt to reconfigure Nvidia decoder failed!"); - thiz->hasError_ = true; - return false; - } } - - return thiz->videoDecoder_->nDecodeSurfaces(); + catch (const cv::Exception&) + { + CV_LOG_ERROR(NULL, "Attempt to configure Nvidia decoder failed!"); + thiz->hasError_ = true; + retVal = 0; + } + return retVal; } int CUDAAPI cv::cudacodec::detail::VideoParser::HandlePictureDecode(void* userData, CUVIDPICPARAMS* picParams) diff --git a/modules/cudacodec/src/video_parser.hpp b/modules/cudacodec/src/video_parser.hpp index 28159a9b9..2bc3831c2 100644 --- a/modules/cudacodec/src/video_parser.hpp +++ b/modules/cudacodec/src/video_parser.hpp @@ -78,8 +78,6 @@ private: bool allowFrameDrop_ = false; // Called when the decoder encounters a video format change (or initial sequence header) - // This particular implementation of the callback returns 0 in case the video format changes - // to something different than the original format. Returning 0 causes a stop of the app. static int CUDAAPI HandleVideoSequence(void* pUserData, CUVIDEOFORMAT* pFormat); // Called by the video parser to decode a single picture diff --git a/modules/cudacodec/test/test_video.cpp b/modules/cudacodec/test/test_video.cpp index a92512304..842bb3ed6 100644 --- a/modules/cudacodec/test/test_video.cpp +++ b/modules/cudacodec/test/test_video.cpp @@ -45,6 +45,15 @@ namespace opencv_test { namespace { #if defined(HAVE_NVCUVID) || defined(HAVE_NVCUVENC) +struct SetDevice : testing::TestWithParam +{ + cv::cuda::DeviceInfo devInfo; + virtual void SetUp(){ + devInfo = GetParam(); + cv::cuda::setDevice(devInfo.deviceID()); + } +}; + PARAM_TEST_CASE(CheckSet, cv::cuda::DeviceInfo, std::string) { }; @@ -71,6 +80,14 @@ PARAM_TEST_CASE(ColorConversion, cv::cuda::DeviceInfo, cv::cudacodec::ColorForma { }; +struct ReconfigureDecoderWithScaling : SetDevice +{ +}; + +PARAM_TEST_CASE(ReconfigureDecoder, cv::cuda::DeviceInfo, int) +{ +}; + PARAM_TEST_CASE(VideoReadRaw, cv::cuda::DeviceInfo, std::string) { }; @@ -87,16 +104,8 @@ PARAM_TEST_CASE(CheckInitParams, cv::cuda::DeviceInfo, std::string, bool, bool, { }; -struct CheckParams : testing::TestWithParam +struct CheckParams : SetDevice { - cv::cuda::DeviceInfo devInfo; - - virtual void SetUp() - { - devInfo = GetParam(); - - cv::cuda::setDevice(devInfo.deviceID()); - } }; #if defined(HAVE_NVCUVID) @@ -190,6 +199,14 @@ CUDA_TEST_P(CheckKeyFrame, Reader) } } +void ForceAlignment(Rect& srcRoi, Rect& targetRoi, Size& targetSz) { + targetSz.width = targetSz.width - targetSz.width % 2; targetSz.height = targetSz.height - targetSz.height % 2; + srcRoi.x = srcRoi.x - srcRoi.x % 4; srcRoi.width = srcRoi.width - srcRoi.width % 4; + srcRoi.y = srcRoi.y - srcRoi.y % 2; srcRoi.height = srcRoi.height - srcRoi.height % 2; + targetRoi.x = targetRoi.x - targetRoi.x % 4; targetRoi.width = targetRoi.width - targetRoi.width % 4; + targetRoi.y = targetRoi.y - targetRoi.y % 2; targetRoi.height = targetRoi.height - targetRoi.height % 2; +} + CUDA_TEST_P(Scaling, Reader) { cv::cuda::setDevice(GET_PARAM(0).deviceID()); @@ -211,18 +228,15 @@ CUDA_TEST_P(Scaling, Reader) static_cast(frameOr.rows * srcRoiIn.height)); params.targetRoi = Rect(static_cast(params.targetSz.width * targetRoiIn.x), static_cast(params.targetSz.height * targetRoiIn.y), static_cast(params.targetSz.width * targetRoiIn.width), static_cast(params.targetSz.height * targetRoiIn.height)); + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, {}, params); ASSERT_TRUE(reader->set(cudacodec::ColorFormat::GRAY)); GpuMat frame; ASSERT_TRUE(reader->nextFrame(frame)); const cudacodec::FormatInfo format = reader->format(); - Size targetSzOut; - targetSzOut.width = params.targetSz.width - params.targetSz.width % 2; targetSzOut.height = params.targetSz.height - params.targetSz.height % 2; - Rect srcRoiOut, targetRoiOut; - srcRoiOut.x = params.srcRoi.x - params.srcRoi.x % 4; srcRoiOut.width = params.srcRoi.width - params.srcRoi.width % 4; - srcRoiOut.y = params.srcRoi.y - params.srcRoi.y % 2; srcRoiOut.height = params.srcRoi.height - params.srcRoi.height % 2; - targetRoiOut.x = params.targetRoi.x - params.targetRoi.x % 4; targetRoiOut.width = params.targetRoi.width - params.targetRoi.width % 4; - targetRoiOut.y = params.targetRoi.y - params.targetRoi.y % 2; targetRoiOut.height = params.targetRoi.height - params.targetRoi.height % 2; + Size targetSzOut = params.targetSz; + Rect srcRoiOut = params.srcRoi, targetRoiOut = params.targetRoi; + ForceAlignment(srcRoiOut, targetRoiOut, targetSzOut); ASSERT_TRUE(format.valid && format.targetSz == targetSzOut && format.srcRoi == srcRoiOut && format.targetRoi == targetRoiOut); ASSERT_TRUE(frame.size() == targetSzOut); GpuMat frameGs; @@ -237,8 +251,8 @@ CUDA_TEST_P(DisplayResolution, Reader) cv::cuda::setDevice(GetParam().deviceID()); std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../cv/video/1920x1080.avi"; const Rect displayArea(0, 0, 1920, 1080); - GpuMat frame; + GpuMat frame; { // verify the output frame is the diplay size (1920x1080) and not the coded size (1920x1088) cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile); @@ -332,6 +346,82 @@ CUDA_TEST_P(ColorConversion, Reader) } } +CUDA_TEST_P(ReconfigureDecoderWithScaling, Reader) +{ + const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny_multi_res.h264"; + + GpuMat frameOr; + { + cv::Ptr readerGs = cv::cudacodec::createVideoReader(inputFile); + ASSERT_TRUE(readerGs->nextFrame(frameOr)); + } + + cv::cudacodec::VideoReaderInitParams params; + const Size2f targetSzNew(0.8f, 0.9f); + const Rect2f srcRoiNew(0.25f, 0.25f, 0.5f, 0.5f); + const Rect2f targetRoiNew(0.2f, 0.3f, 0.6f, 0.7f); + params.targetSz = Size(static_cast(frameOr.cols * targetSzNew.width), static_cast(frameOr.rows * targetSzNew.height)); + params.srcRoi = Rect(static_cast(frameOr.cols * srcRoiNew.x), static_cast(frameOr.rows * srcRoiNew.y), static_cast(frameOr.cols * srcRoiNew.width), + static_cast(frameOr.rows * srcRoiNew.height)); + params.targetRoi = Rect(static_cast(params.targetSz.width * targetRoiNew.x), static_cast(params.targetSz.height * targetRoiNew.y), + static_cast(params.targetSz.width * targetRoiNew.width), static_cast(params.targetSz.height * targetRoiNew.height)); + + Size targetSzOut = params.targetSz; + Rect srcRoiOut = params.srcRoi, targetRoiOut = params.targetRoi; + ForceAlignment(srcRoiOut, targetRoiOut, targetSzOut); + GpuMat mask(targetSzOut, CV_8U, Scalar(255)); + mask(targetRoiOut).setTo(0); + + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, {}, params); + reader->set(cudacodec::ColorFormat::GRAY); + cv::cudacodec::FormatInfo fmt; + cv::cuda::GpuMat frame; + int nFrames = 0; + Size initialSize; + while (reader->nextFrame(frame)) + { + ASSERT_TRUE(!frame.empty()); + if (nFrames++ == 0) + initialSize = frame.size(); + fmt = reader->format(); + ASSERT_TRUE(fmt.valid && (frame.size() == initialSize)); + ASSERT_TRUE((frame.size() == targetSzOut) && (fmt.targetSz == targetSzOut) && (fmt.srcRoi == srcRoiOut) && (fmt.targetRoi == targetRoiOut)); + // simple check - zero borders, non zero contents + ASSERT_TRUE(!cuda::absSum(frame, mask)[0] && cuda::sum(frame)[0]); + } + ASSERT_TRUE(nFrames == 40); +} + +CUDA_TEST_P(ReconfigureDecoder, Reader) +{ + const std::string inputFile = std::string(cvtest::TS::ptr()->get_data_path()) + "../highgui/video/big_buck_bunny_multi_res.h264"; + cv::cuda::setDevice(GET_PARAM(0).deviceID()); + const int minNumDecodeSurfaces = GET_PARAM(1); + cv::cudacodec::VideoReaderInitParams params; + params.minNumDecodeSurfaces = minNumDecodeSurfaces; + cv::Ptr reader = cv::cudacodec::createVideoReader(inputFile, {}, params); + reader->set(cudacodec::ColorFormat::GRAY); + cv::cudacodec::FormatInfo fmt; + cv::cuda::GpuMat frame, mask; + int nFrames = 0; + Size initialSize, initialCodedSize; + while(reader->nextFrame(frame)) + { + ASSERT_TRUE(!frame.empty()); + fmt = reader->format(); + if (nFrames++ == 0) { + initialSize = frame.size(); + initialCodedSize = Size(fmt.ulWidth, fmt.ulHeight); + } + ASSERT_TRUE(fmt.valid && (frame.size() == initialSize)); + ASSERT_TRUE(fmt.srcRoi.empty()); + const bool resChanged = (initialCodedSize.width != fmt.ulWidth) || (initialCodedSize.height != fmt.ulHeight); + if (resChanged) + ASSERT_TRUE(fmt.targetRoi.empty()); + } + ASSERT_TRUE(nFrames == 40); +} + CUDA_TEST_P(VideoReadRaw, Reader) { cv::cuda::setDevice(GET_PARAM(0).deviceID()); @@ -767,6 +857,11 @@ INSTANTIATE_TEST_CASE_P(CUDA_Codec, ColorConversion, testing::Combine( testing::Values(VIDEO_COLOR_OUTPUTS), testing::ValuesIn(color_conversion_params))); +INSTANTIATE_TEST_CASE_P(CUDA_Codec, ReconfigureDecoderWithScaling, ALL_DEVICES); + +#define N_DECODE_SURFACES testing::Values(0, 10) +INSTANTIATE_TEST_CASE_P(CUDA_Codec, ReconfigureDecoder, testing::Combine(ALL_DEVICES, N_DECODE_SURFACES)); + #define VIDEO_SRC_RW "highgui/video/big_buck_bunny.h264", "highgui/video/big_buck_bunny.h265" INSTANTIATE_TEST_CASE_P(CUDA_Codec, VideoReadRaw, testing::Combine( ALL_DEVICES, diff --git a/modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp b/modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp index ea4a32381..611869e3a 100644 --- a/modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp +++ b/modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp @@ -110,7 +110,7 @@ public: @param _nOctaveLayers Number of octave layers within each octave. @param _extended Extended descriptor flag (true - use extended 128-element descriptors; false - use 64-element descriptors). - @param _keypointsRatio + @param _keypointsRatio Limits a maximum number of features @param _upright Up-right or rotated features flag (true - do not compute orientation of features; false - compute orientation). */ diff --git a/modules/ximgproc/include/opencv2/ximgproc/edge_drawing.hpp b/modules/ximgproc/include/opencv2/ximgproc/edge_drawing.hpp index 6f97dd2ae..33741e900 100644 --- a/modules/ximgproc/include/opencv2/ximgproc/edge_drawing.hpp +++ b/modules/ximgproc/include/opencv2/ximgproc/edge_drawing.hpp @@ -115,7 +115,7 @@ public: /** @brief sets parameters. this function is meant to be used for parameter setting in other languages than c++ like python. - @param parameters + @param parameters Parameters of the algorithm */ CV_WRAP void setParams(const EdgeDrawing::Params& parameters); virtual ~EdgeDrawing() { }