Actual combat | OpenCV4.8 one-dimensional code detection and decoding demonstration (steps + source code)

guide

    This article mainly introduces the demo of one-dimensional code detection and decoding in OpenCV4.8 (steps + source code).

background introduction

picture

    Barcode (1D-Barcode) is the main technology for identifying goods in real life. Common barcodes consist of a pattern of parallel lines arranged with black and white bars of widely varying reflectivity. Barcode recognition is to scan the barcode in the horizontal direction to obtain a string of binary codes composed of bars of different widths and colors, that is, the code information of the barcode. By matching various barcode encoding methods, the content of the barcode can be decoded. Currently, OpenCV4.8 supports EAN-8, EAN-13, UPC-A and UPC-E barcode types.

    Reference link:

https://docs.opencv.org/4.8.0/d6/d25/tutorial_barcode_detect_and_decode.html

      

Steps for usage

    The content of this article is demonstrated with OpenCV4.8.0. The specific steps are as follows:

【1】Download and install OpenCV4.8.0 Release

    Address: https://opencv.org/releases/

picture

picture

【2】Download the super-resolution barcode detector model

picture

 download link:

https://github.com/WeChatCV/opencv_3rdparty/tree/wechat_qrcode

After decompression, put the following two files into the specified directory:

picture

【3】Test code:

// Barcode_1D_Decoder.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include "pch.h"#include <iostream>#include <opencv2/opencv.hpp>
#define Mode 1  // 0-img_test, 1-cam_test
using namespace std;using namespace cv;
static const Scalar greenColor(0, 255, 0);static const Scalar redColor(0, 0, 255);static const Scalar yellowColor(0, 255, 255);static Scalar randColor(){
   
     RNG &rng = theRNG();  return Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));}
struct TheApp{
   
     Ptr<barcode::BarcodeDetector> bardet;  //! [output]  vector<Point> corners;  vector<string> decode_info;  vector<string> decode_type;  //! [output]  bool detectOnly;
  void cleanup(){
   
       corners.clear();    decode_info.clear();    decode_type.clear();  }
  inline string modeString() const{
   
       return detectOnly ? "<detect>" : "<detectAndDecode>";  }
  void drawResults(Mat &frame) const{
   
       //! [visualize]    for (size_t i = 0; i < corners.size(); i += 4)    {
   
         const size_t idx = i / 4;      const bool isDecodable = idx < decode_info.size()        && idx < decode_type.size()        && !decode_type[idx].empty();      const Scalar lineColor = isDecodable ? greenColor : redColor;      // draw barcode rectangle      vector<Point> contour(corners.begin() + i, corners.begin() + i + 4);      const vector< vector<Point> > contours{ contour };      drawContours(frame, contours, 0, lineColor, 1);      // draw vertices      for (size_t j = 0; j < 4; j++)        circle(frame, contour[j], 2, randColor(), -1);      // write decoded text      if (isDecodable)      {
   
           ostringstream buf;        buf << "[" << decode_type[idx] << "] " << decode_info[idx];        putText(frame, buf.str(), contour[1], FONT_HERSHEY_COMPLEX, 0.8, yellowColor, 1);      }    }    //! [visualize]  }
  void drawFPS(Mat &frame, double fps) const{
   
       ostringstream buf;    buf << modeString()      << " (" << corners.size() / 4 << "/" << decode_type.size() << "/" << decode_info.size() << ") "      << cv::format("%.2f", fps) << " FPS ";    putText(frame, buf.str(), Point(25, 25), FONT_HERSHEY_COMPLEX, 0.8, redColor, 2);  }
  inline void call_decode(Mat &frame){
   
       cleanup();    if (detectOnly)    {
   
         //! [detect]      bardet->detectMulti(frame, corners);      //! [detect]    }    else    {
   
         //! [detectAndDecode]      bardet->detectAndDecodeWithType(frame, decode_info, decode_type, corners);      //! [detectAndDecode]    }  }
  int liveBarCodeDetect(){
   
       VideoCapture cap(0);    if (!cap.isOpened())    {
   
         cout << "Cannot open a camera" << endl;      return 2;    }    Mat frame;    Mat result;    cap >> frame;    cout << "Image size: " << frame.size() << endl;    cout << "Press 'd' to switch between <detect> and <detectAndDecode> modes" << endl;    cout << "Press 'ESC' to exit" << endl;    for (;;)    {
   
         cap >> frame;      if (frame.empty())      {
   
           cout << "End of video stream" << endl;        break;      }      if (frame.channels() == 1)        cvtColor(frame, frame, COLOR_GRAY2BGR);      TickMeter timer;      timer.start();      call_decode(frame);      timer.stop();      drawResults(frame);      drawFPS(frame, timer.getFPS());      imshow("barcode", frame);      const char c = (char)waitKey(1);      if (c == 'd')      {
   
           detectOnly = !detectOnly;        cout << "Mode switched to " << modeString() << endl;      }      else if (c == 27)      {
   
           cout << "'ESC' is pressed. Exiting..." << endl;        break;      }    }    return 0;  }
  int imageBarCodeDetect(const string &in_file, const string &out_file){
   
       Mat frame = imread(in_file, IMREAD_COLOR);    cout << "Image size: " << frame.size() << endl;    cout << "Mode is " << modeString() << endl;    const int count_experiments = 100;    TickMeter timer;    for (size_t i = 0; i < count_experiments; i++)    {
   
         timer.start();      call_decode(frame);      timer.stop();    }    cout << "FPS: " << timer.getFPS() << endl;    drawResults(frame);    if (!out_file.empty())    {
   
         cout << "Saving result: " << out_file << endl;      imwrite(out_file, frame);    }    imshow("barcode", frame);    cout << "Press any key to exit ..." << endl;    waitKey(0);    return 0;  }};

int main(){
   
     string sr_prototxt = "./model/sr.prototxt";  string sr_model = "./model/sr.caffemodel";
  TheApp app;  app.detectOnly = false;  //! [initialize]  try  {
   
       app.bardet = makePtr<barcode::BarcodeDetector>(sr_prototxt, sr_model);  }  catch (const std::exception& e)  {
   
       cout <<"read sr_model failed!" << endl;    cout << e.what() << endl;    return -1;  }  //! [initialize]
  if(Mode)    app.liveBarCodeDetect();  else  {
   
       string inPath = "./imgs/t6.jpg";    string outPath = "./res.jpg";    Mat img = imread(inPath);    app.call_decode(img);    app.drawResults(img);
    imshow("barcode", img);    waitKey();  }  return 0;} 

【4】Picture test:

picture

picture

picture

picture

picture

【5】Video test:

, duration 00:25

      

A brief description

    The module introduces a variety of barcode recognition algorithms.

    When encoding, we first need to create a cv::barcode::BarcodeDetector object. It mainly has three member functions, which are introduced separately below.

【1】Initialization

try {
   
    app.bardet = makePtr<barcode::BarcodeDetector>(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; }

    We need to create variables to store the output. ​​​​​​​​

 vector<Point> corners; vector<string> decode_info; vector<string> decode_type;

【2】Barcode detection

    The cv::barcode::BarcodeDetector::detect method uses an algorithm based on directional coherence. First, we compute the average squared gradient for each pixel. Then, we divide the image into square blocks and compute the gradient direction consistency and mean gradient direction for each block. Then, we concatenate all patches with high gradient direction consistency and similar gradient directions. In this stage, we use multi-scale patches to capture the gradient distribution of multi-size barcodes and apply non-maximum suppression to filter duplicate proposals. Finally, we use cv::minAreaRect to limit the ROI and output the corners of the rectangle.

    Code that detects an input image, and outputs the corners of the detected rectangle:

bardet->detectMulti(frame, corners);

[3] decoding

    The cv::barcode::BarcodeDetector::decode method first superscales ( optionally ) the image (if the image is smaller than the threshold), sharpens the image, and then binarizes it via OTSU or local binarization. It then reads the content of the barcode by matching the similarity of the specified barcode pattern.

    cv::barcode::BarcodeDetector::detectAndDecodedetect will combine decode and decode in one call. The following simple example shows how to use this function:

bardet->detectAndDecodeWithType(frame,decode_info,decode_type,corners);

Visualization results:

for (size_t i = 0; i < corners.size(); i += 4) {
   
    const size_t idx = i / 4; const bool isDecodable = idx < decode_info.size() && idx < decode_type.size() && !decode_type[idx].empty(); const Scalar lineColor = isDecodable ? greenColor : redColor; // draw barcode rectangle vector<Point> contour(corners.begin() + i, corners.begin() + i + 4); const vector< vector<Point> > contours {contour}; drawContours(frame, contours, 0, lineColor, 1); // draw vertices for (size_t j = 0; j < 4; j++) circle(frame, contour[j], 2, randColor(), -1); // write decoded text if (isDecodable) {
   
    ostringstream buf; buf << "[" << decode_type[idx] << "] " << decode_info[idx]; putText(frame, buf.str(), contour[1], FONT_HERSHEY_COMPLEX, 0.8, yellowColor, 1); } }

picture

Kind tips:

The barcode detection is successful, but the decoding fails: the barcode frame is red;

The barcode detection is successful, and the decoding is successful: the barcode frame is green, and the decoded content is yellow.

      

Use summary

[1] The detection effect of one-dimensional code is better, and most barcodes can be detected;

[2] One-dimensional code decoding effect is better for commodity barcodes, and it is more suitable for scanning codes on mobile phones.

【3】Support for similar barcodes to be added;

【4】Video detection and decoding speed is fast, 640*480 resolution FPS is between 40~200.

Guess you like

Origin blog.csdn.net/stq054188/article/details/132257212