Ejemplo de aplicación de OpenCV: posicionamiento y reconocimiento de código bidimensional

prefacio

La localización de códigos QR no es solo para identificar códigos QR, también se puede utilizar para corregir imágenes horizontalmente y localizar áreas adyacentes a través de códigos QR. Localizar un código QR requiere no solo conocimientos sobre el procesamiento de imágenes, sino también las características del código QR.Este artículo comienza con las características del código QR.

Características del código QR

Al inicio del diseño del código bidimensional se consideró el problema de identificación, por lo que algunas características del código bidimensional son muy obvias.

El código QR tiene tres patrones de glifos "atrás", lo cual es muy obvio. Un punto en el medio se encuentra en la esquina superior izquierda del patrón, y si la imagen se desvía, también se puede corregir de acuerdo con el código QR.

¿Por qué tres puntos en lugar de uno, dos o cuatro?

Un punto: las características no son obvias y no es fácil de localizar. No es fácil localizar el ángulo de inclinación del código QR.

Dos puntos: el orden de los dos puntos no se puede confirmar y es difícil determinar si el código QR está colocado correctamente.

Cuatro puntos: No se puede determinar el orden de los cuatro puntos, por lo que es imposible determinar si el código QR está colocado correctamente.

Reconocer el código QR es identificar los tres puntos del código QR, y analizar paso a paso las características de estos tres puntos

 1. Cada punto tiene dos contornos. Hay dos bocas, y hay una pequeña "boca" dentro de la gran "boca", por lo que hay dos contornos.

 2. Si pones esta "espalda" sobre un fondo blanco, dibuja una línea de izquierda a derecha o de arriba a abajo. La proporción de blanco y negro del patrón por el que pasa esta línea es aproximadamente: la proporción de blanco y negro es 1:1:3:1:1.

 3. ¿Cómo encontrar el vértice en la esquina superior izquierda? El ángulo entre este vértice y los otros dos vértices es de 90 grados.

A través de los pasos anteriores, se pueden identificar los tres vértices del código QR y se puede identificar el vértice en la esquina superior izquierda.

Usa opencv para identificar códigos QR

 Encuentre el contorno y filtre tres vértices de código QR

Una función muy importante de opencv es encontrar el contorno, es decir, puede encontrar todos los contornos en una imagen. El patrón "atrás" es un contorno muy obvio, que es fácil de encontrar.

int QrParse::FindQrPoint(Mat& srcImg, vector<vector<Point>>& qrPoint)
{
     //彩色图转灰度图
     Mat src_gray;
     cvtColor(srcImg, src_gray, CV_BGR2GRAY);
     namedWindow("src_gray");
     imshow("src_gray", src_gray);
 
     //二值化
     Mat threshold_output;
     threshold(src_gray, threshold_output, 0, 255, THRESH_BINARY | THRESH_OTSU);
     Mat threshold_output_copy = threshold_output.clone();
     namedWindow("Threshold_output");
     imshow("Threshold_output", threshold_output);
 
     //调用查找轮廓函数
     vector<vector<Point> > contours;
     vector<Vec4i> hierarchy;
     findContours(threshold_output, contours, hierarchy, CV_RETR_TREE, CHAIN_APPROX_NONE, Point(0, 0));
 
     //通过黑色定位角作为父轮廓,有两个子轮廓的特点,筛选出三个定位角
     int parentIdx = -1;
     int ic = 0;
 
     for (int i = 0; i < contours.size(); i++)
     {
         if (hierarchy[i][2] != -1 && ic == 0)
         {
             parentIdx = i;
             ic++;
         }
         else if (hierarchy[i][2] != -1)
         {
             ic++;
         }
         else if (hierarchy[i][2] == -1)
         {
             ic = 0;
             parentIdx = -1;
         }
 
        
              
         //保存找到的三个黑色定位角
         if (isQr)
             qrPoint.push_back(contours[parentIdx]);
 
             ic = 0;
             parentIdx = -1;
         }
     }
 
     return 0;
}

 Encontramos dos primitivas de esquema, necesitamos analizar más a fondo si es un vértice de código QR, use la siguiente función:

bool QrParse::IsQrPoint(vector<Point>& contour, Mat& img)
{
    //最小大小限定
    RotatedRect rotatedRect = minAreaRect(contour);
    if (rotatedRect.size.height < 10 || rotatedRect.size.width < 10)
        return false;

    //将二维码从整个图上抠出来
    cv::Mat cropImg = CropImage(img, rotatedRect);
    int flag = i++;

    //横向黑白比例1:1:3:1:1
    bool result = IsQrColorRate(cropImg, flag);
    return result;
}

 Función de juicio de proporción en blanco y negro:

  //横向和纵向黑白比例判断
  bool QrParse::IsQrColorRate(cv::Mat& image, int flag)
  {
       bool x = IsQrColorRateX(image, flag);
       if (!x)
           return false;
       bool y = IsQrColorRateY(image, flag);
       return y;
  }

  //横向黑白比例判断
  bool QrParse::IsQrColorRateX(cv::Mat& image, int flag)
  {
      int nr = image.rows / 2;
      int nc = image.cols * image.channels();
  
      vector<int> vValueCount;
      vector<uchar> vColor;
      int count = 0;
      uchar lastColor = 0;
  
      uchar* data = image.ptr<uchar>(nr);
      for (int i = 0; i < nc; i++)
      {
          vColor.push_back(data[i]);
          uchar color = data[i]; 28 
          if (i == 0)
          {
              lastColor = color;
              count++;
          }
          else
          {
              if (lastColor != color)
              {
                  vValueCount.push_back(count);
                  count = 0;
              }
              count++;
              lastColor = color;
          }
      }
  
      if (count != 0)
          vValueCount.push_back(count);
  
      if (vValueCount.size() < 5)
          return false;
  
      //横向黑白比例1:1:3:1:1
      int index = -1;
      int maxCount = -1;
      for (int i = 0; i < vValueCount.size(); i++)
      {
          if (i == 0)
          {
              index = i;
              maxCount = vValueCount[i];
          }
          else
          {
              if (vValueCount[i] > maxCount)
              {
                  index = i;
                  maxCount = vValueCount[i];
              }
          }
      }
  
      //黑白比例1:1:3:1:1
      float rate = ((float)maxCount) / 3.00;
  
      cout << "flag:" << flag << " ";
  
      float rate2 = vValueCount[index - 2] / rate;
      cout << rate2 << " ";
      if (!IsQrRate(rate2))
          return false;
  
      rate2 = vValueCount[index - 1] / rate;
      cout << rate2 << " ";
      if (!IsQrRate(rate2))
          return false;
  
      rate2 = vValueCount[index + 1] / rate;
      cout << rate2 << " ";
      if (!IsQrRate(rate2))
          return false;
  
      rate2 = vValueCount[index + 2] / rate;
      cout << rate2 << " ";
      if (!IsQrRate(rate2))
         return false;
 
      return true;
 }
//纵向黑白比例判断 省略
bool QrParse::IsQrColorRateY(cv::Mat& image, int flag)
bool QrParse::IsQrRate(float rate)
{
     //大概比例 不能太严格
    return rate > 0.6 && rate < 1.9;
}

Determinar el orden de los vértices de los tres códigos QR

 El vértice de la esquina superior izquierda está determinado por el siguiente principio: el ángulo entre el vértice de la esquina superior izquierda del código QR y los otros dos vértices es de 90 grados.

 // pointDest存放调整后的三个点,三个点的顺序如下
 // pt0----pt1
 // 
 // pt2
 bool QrParse::AdjustQrPoint(Point* pointSrc, Point* pointDest)
 {
     bool clockwise;
     int index1[3] = { 2,1,0 };
     int index2[3] = { 0,2,1 };
     int index3[3] = { 0,1,2 };
 
     for (int i = 0; i < 3; i++)
     {
         int *n = index1;
         if(i==0)
             n = index1;
         else if (i == 1)
             n = index2;
         else 
             n = index3;
 
         if (angle > 80 && angle < 99)
         {
             pointDest[0] = pointSrc[n[2]];
             if (clockwise)
             {
                 pointDest[1] = pointSrc[n[0]];
                 pointDest[2] = pointSrc[n[1]];
             }
             else
             {
                 pointDest[1] = pointSrc[n[1]];
                 pointDest[2] = pointSrc[n[0]];
             }
             return true;
         }
     }
     return true;
 }

 

Corrección de imagen por código QR

La imagen se puede inclinar y el ángulo de inclinación se puede determinar por el ángulo entre la línea pt0 y pt1 y la línea horizontal. El ángulo de inclinación del código bidimensional es el ángulo de inclinación de toda la imagen, de modo que toda la imagen puede corregirse horizontalmente.

//二维码倾斜角度
Point hor(pointAdjust[0].x+300,pointAdjust[0].y); //水平线
double qrAngle = QrParse::Angle(pointAdjust[1], hor, pointAdjust[0], clockwise);

//以二维码左上角点为中心 旋转
Mat drawingRotation = Mat::zeros(Size(src.cols,src.rows), CV_8UC3);
double rotationAngle = clockwise? -qrAngle:qrAngle;
Mat affine_matrix = getRotationMatrix2D(pointAdjust[0], rotationAngle, 1.0);//求得旋转矩阵
warpAffine(src, drawingRotation, affine_matrix, drawingRotation.size());

Posicionamiento del área adyacente del código QR

Generalmente, se determina la posición del código QR en la imagen completa. Una vez que se reconoce el código bidimensional, se pueden localizar fácilmente otros elementos gráficos de acuerdo con la relación posicional entre el código bidimensional y otras imágenes.

 

Resumir

Buscando una gran cantidad de información y estudiando cuidadosamente las características del código bidimensional, encontramos un método para identificar el código bidimensional. También existen muchos métodos para identificar códigos QR en Internet, pero no son lo suficientemente rigurosos. Este artículo combina múltiples características del código QR para identificarlo, lo que es más preciso. Este método de reconocimiento se ha aplicado a los productos de la empresa y el efecto de reconocimiento sigue siendo muy bueno.

Supongo que te gusta

Origin blog.csdn.net/qq_39312146/article/details/130138588
Recomendado
Clasificación