From 518e184ed1a0efed1d8eacc7543701eef7141409 Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 24 May 2023 13:43:46 +0300 Subject: [PATCH 1/4] Moved barcode module to opencv/objdetect --- modules/barcode/CMakeLists.txt | 2 - modules/barcode/README.md | 9 - modules/barcode/doc/barcode.bib | 28 -- modules/barcode/include/opencv2/barcode.hpp | 101 ----- modules/barcode/misc/java/filelist_common | 1 - modules/barcode/misc/java/gen_dict.json | 12 - .../misc/java/src/cpp/barcode_converters.cpp | 28 -- .../misc/java/src/cpp/barcode_converters.hpp | 20 - .../misc/java/test/BarcodeDetectorTest.java | 51 --- .../barcode/misc/python/pyopencv_barcode.hpp | 22 - .../misc/python/test/test_barcode_detector.py | 30 -- modules/barcode/perf/perf_barcode.cpp | 113 ----- modules/barcode/perf/perf_main.cpp | 9 - modules/barcode/perf/perf_precomp.hpp | 11 - modules/barcode/samples/barcode.cpp | 298 ------------- modules/barcode/src/barcode.cpp | 272 ------------ modules/barcode/src/decoder/abs_decoder.cpp | 118 ----- modules/barcode/src/decoder/abs_decoder.hpp | 73 ---- .../src/decoder/common/hybrid_binarizer.cpp | 195 --------- .../src/decoder/common/hybrid_binarizer.hpp | 22 - .../src/decoder/common/super_scale.cpp | 75 ---- .../src/decoder/common/super_scale.hpp | 35 -- modules/barcode/src/decoder/common/utils.cpp | 36 -- modules/barcode/src/decoder/common/utils.hpp | 25 -- modules/barcode/src/decoder/ean13_decoder.cpp | 92 ---- modules/barcode/src/decoder/ean13_decoder.hpp | 31 -- modules/barcode/src/decoder/ean8_decoder.cpp | 79 ---- modules/barcode/src/decoder/ean8_decoder.hpp | 32 -- .../barcode/src/decoder/upcean_decoder.cpp | 290 ------------- .../barcode/src/decoder/upcean_decoder.hpp | 67 --- modules/barcode/src/detector/bardetect.cpp | 410 ------------------ modules/barcode/src/detector/bardetect.hpp | 63 --- modules/barcode/src/precomp.hpp | 16 - modules/barcode/test/test_barcode.cpp | 79 ---- modules/barcode/test/test_main.cpp | 6 - modules/barcode/test/test_precomp.hpp | 11 - modules/barcode/test/utils.hpp | 59 --- .../barcode_detect_and_decode.markdown | 73 ---- .../images/4_barcodes.jpg | Bin 95340 -> 0 bytes .../images/result.jpg | Bin 100135 -> 0 bytes .../table_of_content_barcode.markdown | 6 - 41 files changed, 2900 deletions(-) delete mode 100644 modules/barcode/CMakeLists.txt delete mode 100644 modules/barcode/README.md delete mode 100644 modules/barcode/doc/barcode.bib delete mode 100644 modules/barcode/include/opencv2/barcode.hpp delete mode 100644 modules/barcode/misc/java/filelist_common delete mode 100644 modules/barcode/misc/java/gen_dict.json delete mode 100644 modules/barcode/misc/java/src/cpp/barcode_converters.cpp delete mode 100644 modules/barcode/misc/java/src/cpp/barcode_converters.hpp delete mode 100644 modules/barcode/misc/java/test/BarcodeDetectorTest.java delete mode 100644 modules/barcode/misc/python/pyopencv_barcode.hpp delete mode 100644 modules/barcode/misc/python/test/test_barcode_detector.py delete mode 100644 modules/barcode/perf/perf_barcode.cpp delete mode 100644 modules/barcode/perf/perf_main.cpp delete mode 100644 modules/barcode/perf/perf_precomp.hpp delete mode 100644 modules/barcode/samples/barcode.cpp delete mode 100644 modules/barcode/src/barcode.cpp delete mode 100644 modules/barcode/src/decoder/abs_decoder.cpp delete mode 100644 modules/barcode/src/decoder/abs_decoder.hpp delete mode 100644 modules/barcode/src/decoder/common/hybrid_binarizer.cpp delete mode 100644 modules/barcode/src/decoder/common/hybrid_binarizer.hpp delete mode 100644 modules/barcode/src/decoder/common/super_scale.cpp delete mode 100644 modules/barcode/src/decoder/common/super_scale.hpp delete mode 100644 modules/barcode/src/decoder/common/utils.cpp delete mode 100644 modules/barcode/src/decoder/common/utils.hpp delete mode 100644 modules/barcode/src/decoder/ean13_decoder.cpp delete mode 100644 modules/barcode/src/decoder/ean13_decoder.hpp delete mode 100644 modules/barcode/src/decoder/ean8_decoder.cpp delete mode 100644 modules/barcode/src/decoder/ean8_decoder.hpp delete mode 100644 modules/barcode/src/decoder/upcean_decoder.cpp delete mode 100644 modules/barcode/src/decoder/upcean_decoder.hpp delete mode 100644 modules/barcode/src/detector/bardetect.cpp delete mode 100644 modules/barcode/src/detector/bardetect.hpp delete mode 100644 modules/barcode/src/precomp.hpp delete mode 100644 modules/barcode/test/test_barcode.cpp delete mode 100644 modules/barcode/test/test_main.cpp delete mode 100644 modules/barcode/test/test_precomp.hpp delete mode 100644 modules/barcode/test/utils.hpp delete mode 100644 modules/barcode/tutorials/barcode_detect_and_decode/barcode_detect_and_decode.markdown delete mode 100644 modules/barcode/tutorials/barcode_detect_and_decode/images/4_barcodes.jpg delete mode 100644 modules/barcode/tutorials/barcode_detect_and_decode/images/result.jpg delete mode 100644 modules/barcode/tutorials/table_of_content_barcode.markdown diff --git a/modules/barcode/CMakeLists.txt b/modules/barcode/CMakeLists.txt deleted file mode 100644 index 80c00f4e4..000000000 --- a/modules/barcode/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -set(the_description "Barcode Detection and Decoding") -ocv_define_module(barcode opencv_core opencv_imgproc opencv_dnn WRAP python java) diff --git a/modules/barcode/README.md b/modules/barcode/README.md deleted file mode 100644 index efdbbf090..000000000 --- a/modules/barcode/README.md +++ /dev/null @@ -1,9 +0,0 @@ -1D Barcode Detect and Decode -====================== - -This module is focused on detecting and decoding barcode from image. It is mainly designed for scanning the images, locating barcode, decoding barcode and outputting its decoded result. - -1. Support 1-dimension bar code detection of any angle tilting. -2. Support multi-scale detection. -3. Support EAN-13, EAN-8 and UPC-A decode yet. -4. With x86 CPU, it achieves 50FPS averagely. diff --git a/modules/barcode/doc/barcode.bib b/modules/barcode/doc/barcode.bib deleted file mode 100644 index 0c1d5112b..000000000 --- a/modules/barcode/doc/barcode.bib +++ /dev/null @@ -1,28 +0,0 @@ -@mastersthesis{Xiangmin2015research, - title={Research on Barcode Recognition Technology In a Complex Background}, - author={Xiangmin, Wang}, - year={2015}, - school={Huazhong University of Science and Technology} -} - -@article{bazen2002systematic, - title={Systematic methods for the computation of the directional fields and singular points of fingerprints}, - author={Bazen, Asker M and Gerez, Sabih H}, - journal={IEEE transactions on pattern analysis and machine intelligence}, - volume={24}, - number={7}, - pages={905--919}, - year={2002}, - publisher={IEEE} -} - -@article{kass1987analyzing, - title={Analyzing oriented patterns}, - author={Kass, Michael and Witkin, Andrew}, - journal={Computer vision, graphics, and image processing}, - volume={37}, - number={3}, - pages={362--385}, - year={1987}, - publisher={Elsevier} -} diff --git a/modules/barcode/include/opencv2/barcode.hpp b/modules/barcode/include/opencv2/barcode.hpp deleted file mode 100644 index 421c41a37..000000000 --- a/modules/barcode/include/opencv2/barcode.hpp +++ /dev/null @@ -1,101 +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_HPP__ -#define __OPENCV_BARCODE_HPP__ - -#include -#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 f361bec56e9a691cb4389c5731565b14d374d3ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 95340 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 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 0a13f18edbb5ac2059b667706a638e257cb79feb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 100135 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
iORf-iEQa)pzpISCE`Fn+^ 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 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. From d3dd415746e7b83ac3323e8b21533c11afe3604d Mon Sep 17 00:00:00 2001 From: Dmitry Kurtaev Date: Sun, 4 Jun 2023 15:40:36 +0300 Subject: [PATCH 2/4] Add missed parameters descriptions --- modules/xfeatures2d/include/opencv2/xfeatures2d/cuda.hpp | 2 +- modules/ximgproc/include/opencv2/ximgproc/edge_drawing.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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() { } From 045a9b07ed7848eafb12ea9131cee5c9136f38c7 Mon Sep 17 00:00:00 2001 From: cudawarped <12133430+cudawarped@users.noreply.github.com> Date: Mon, 17 Apr 2023 12:45:51 +0300 Subject: [PATCH 3/4] cudacodec: add capacity to reconfigure decoder on resolution change --- .../cudacodec/include/opencv2/cudacodec.hpp | 3 +- modules/cudacodec/src/frame_queue.cpp | 28 +++- modules/cudacodec/src/frame_queue.hpp | 9 ++ modules/cudacodec/src/video_decoder.cpp | 57 +++++++- modules/cudacodec/src/video_decoder.hpp | 6 +- modules/cudacodec/src/video_parser.cpp | 116 ++++++++-------- modules/cudacodec/src/video_parser.hpp | 2 - modules/cudacodec/test/test_video.cpp | 129 +++++++++++++++--- 8 files changed, 269 insertions(+), 81 deletions(-) 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, From 14ac6c5c1f2520decf918afeafc5d430e1a095cd Mon Sep 17 00:00:00 2001 From: Maksim Shabunin Date: Wed, 14 Jun 2023 13:23:23 +0300 Subject: [PATCH 4/4] Removed barcode mention from README --- modules/README.md | 2 -- 1 file changed, 2 deletions(-) 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_