Plantilla de degradado de borde de contorno de imagen que coincide con la vaca | opencvsharp

1. Antecedentes

En la actualidad, opencv tiene una variedad de soluciones integradas de coincidencia de plantillas, incluidos TemplateMatch, ShapeMatch, CompareHisr y otros algoritmos. Hay muchos defectos en el proceso de aplicación real: diferentes tamaños, diferentes ángulos y diferentes distribuciones de escala de grises provocan muchos problemas en Por lo tanto, actualmente Para mejorar la precisión y aplicabilidad de la coincidencia de plantillas, se encontró a través de la búsqueda en la red un nuevo algoritmo para hacer coincidir plantillas basado en puntos de referencia de gradiente de borde.

2.Principio

Al recopilar los gradientes de grises en las direcciones X e Y en cada posición del borde de la plantilla y hacer coincidir los gradientes de grises en la dirección XY de todos los píxeles en el área efectiva de la imagen de consulta (algoritmo NCC de gradiente), las imágenes que cumplen el umbral calificado Recoge sus ubicaciones, no más tonterías, ¡sigamos adelante! ! ! !

3. Algoritmo

3.1 Recopilación de datos

El operador Sobel se utiliza para recopilar las derivadas de primer orden de las escalas de grises X e Y de cada punto de píxel en la plantilla y la imagen de consulta respectivamente, y calcular el gradiente completo correspondiente al punto. Será más eficiente cuando se combine con la consulta de contorno . o Detección de bordes Canny y pirámide de imágenes . Al mismo tiempo, registre el punto de referencia Pb entre los puntos válidos recopilados por la plantilla y las coordenadas relativas P (col-pb.x, fila-pb.y) de cada punto válido en la plantilla en relación con el punto de referencia justo.
Insertar descripción de la imagen aquí

3.2 Coincidencia de datos

A través de los datos de la plantilla recopilados anteriormente, el valor del gradiente de cada punto en la imagen de consulta se compara con el valor del gradiente de cada punto válido en la plantilla y se registra la puntuación coincidente. El algoritmo de coincidencia se basa en la coincidencia de la plantilla NCC. están en todas partes. Solo inserto uno
Insertar descripción de la imagen aquí
. La plantilla representa los datos relevantes recopilados en la plantilla y la fuente representa los datos recopilados en la imagen que se va a consultar.

3.3 Optimización de coincidencias

Durante el proceso de comparación, dado que el número de píxeles en la imagen que se va a consultar puede ser demasiado, la puntuación de coincidencia obtenida por cada píxel dado en el proceso de comparación se puede establecer en un umbral Smin para mejorar la eficiencia. La optimización y el establecimiento de umbrales convencionales pueden llevar a una temprana Parte de los datos se ignora cuando se produce una coincidencia de puntos en la etapa posterior de la falta de coincidencia de puntos. Por lo tanto, se realiza un procesamiento de parámetros codiciosos durante el procesamiento del número de umbral para garantizar que se mejore la eficiencia de la coincidencia.
Insertar descripción de la imagen aquí![Inserte descripción de la imagen aquí](https://img-blog.csdnimg.cn/851707ccebe241bd99a1639107d3a2b8.png]

4.Código

4.1 Plantilla de recopilación efectiva de datos de puntos

La cantidad de información relacionada con el contorno preciso de la imagen se obtiene a través del siguiente código. Encontrar puntos efectivos a través del contorno es mucho más eficiente que consultar el gradiente de todos los puntos de píxeles en la plantilla y luego filtrar . A través de la comparación, el valor del gradiente es relativamente obvio;
Insertar descripción de la imagen aquíInsertar descripción de la imagen aquí

static public List<ImageEdgePtInform> ImageContourEdgeInfomGet(Mat img,int margin,ref Size validSize,bool show=false)
        {
    
    
            Mat _uImg = img.Clone();
            if (_uImg.Type() != MatType.CV_8UC1)
            {
    
    
                Cv2.CvtColor(_uImg, _uImg, ColorConversionCodes.BGR2GRAY);
            }
            int edgePointCount = 0;
            Point relaOrgPt = new Point(int.MaxValue,int.MaxValue);
            List<ImageEdgePtInform> resultEdgeInforms = new List<ImageEdgePtInform>();
            Mat _blur = _uImg.GaussianBlur(new Size(3, 3), 0);
            Mat _threImg= _blur.Threshold(120, 255, ThresholdTypes.BinaryInv);
            //图像前期处理
            _threImg.FindContours(out Point[][] cnts, out HierarchyIndex[] hids, RetrievalModes.List, ContourApproximationModes.ApproxNone);
            cnts = cnts.Where(cnt => Cv2.ContourArea(cnt) > 3 && Cv2.ArcLength(cnt, false) > 3 && cnt.Length > 3).ToArray();
            cnts=cnts.ToList().OrderByDescending(cnt => Cv2.ContourArea(cnt)).ToArray();
            //找轮廓,并获取最大轮廓
            foreach (var cnt in cnts) edgePointCount += cnt.Length;
            Mat xDerivative = _uImg.Sobel(MatType.CV_64FC1, 1, 0, 3);
            Mat yDerivative = _uImg.Sobel(MatType.CV_64FC1, 0, 1, 3);
            //获取模板图像X方向Y方向梯度
         
            unsafe
            {
    
    
                foreach(var cnt in cnts)
                {
    
    
                    foreach(var pt in cnt)
                    {
    
    
                        ImageEdgePtInform ptInform = new ImageEdgePtInform();
                        int row = pt.Y, col = pt.X;
                        //获取轮廓基准点(轮廓边缘矩形左下角)
                        relaOrgPt.X = Math.Min(relaOrgPt.X, col);
                        relaOrgPt.Y = Math.Min(relaOrgPt.Y, row);
                        //获取轮廓边缘矩形右下角点
                        validSize.Width = Math.Max(validSize.Width, col);
                        validSize.Height = Math.Max(validSize.Height, row);
                        double dx = ((double*)xDerivative.Ptr(row))[col];
                        double dy = ((double*)yDerivative.Ptr(row))[col];
                        double mag = Math.Sqrt(dx * dx + dy * dy);
                        double barycentOrient = ContourBaryCenterOrientationGet(cnts[0]);
                        ptInform.DerivativeX = dx;
                        ptInform.DerivativeY = dy;
                        //获取当前点xy方向梯度
                        ptInform.Magnitude = (double)(1.0 / mag);
                        ptInform.RelativePos = pt;
                        ptInform.BarycentOrient = barycentOrient;
                        resultEdgeInforms.Add(ptInform);
                    }
                }
                foreach (var inf in resultEdgeInforms)
                {
    
    
                    inf.RelativePos = new Point(inf.RelativePos.X - relaOrgPt.X, inf.RelativePos.Y - relaOrgPt.Y);
                }
                validSize = new Size(validSize.Width - relaOrgPt.X, validSize.Height - relaOrgPt.Y);
                if (show)
                {
    
    
                    Mat _sImg = img.Clone();
                    _sImg.DrawContours(cnts, -1, Scalar.Green, 1);
                    _sImg.Circle(relaOrgPt, 2, Scalar.Red);
                    _sImg.Rectangle(new Rect(relaOrgPt, validSize), Scalar.Blue, 1);
                    _sImg = ImageBasicLineDrawing(_sImg, bcenter, orientation: ENUMS.IMAGE_PERMUTATION_TYPE.HORIZONTAL);
                    _sImg = ImageBasicLineDrawing(_sImg, bcenter, orientation: ENUMS.IMAGE_PERMUTATION_TYPE.VERTICAL);
                    _sImg.Circle(bcenter, 5, Scalar.Blue, -1);
                    ImageShow("asdsad", _sImg);
                }
            }
            return resultEdgeInforms;
        }

4.2 Coincidencia de datos de plantilla con imagen de consulta

Durante el proceso de coincidencia, la eficiencia es relativamente lenta porque el área de la imagen de consulta es grande, pero el efecto sigue siendo bueno, pero parece que solo coincide la imagen de muestra cuya posición relativa es exactamente la misma que la plantilla.
Insertar descripción de la imagen aquíInsertar descripción de la imagen aquí

static public double ImageEdgeMatch(Mat img,List<ImageEdgePtInform> queryEdgeInforms,
            double minScore,double greediness, Size validSize, out Point conformPoints)
        {
    
    
            Mat _uImg = img.Clone();
            if (_uImg.Type() == MatType.CV_8UC3)
            {
    
    
                Cv2.CvtColor(_uImg, _uImg, ColorConversionCodes.BGR2GRAY);
            }
            Cv2.GaussianBlur(_uImg, _uImg, new Size(3, 3), 0);
            //查询图像前期处理
            int Width = _uImg.Width;
            int Height = _uImg.Height;
            int queryCount = queryEdgeInforms.Count;
            double partialScore = 0;
            double resultScore = 0;
            conformPoints =new Point();
            unsafe
            {
    
    
            //获取查询图像X方向和Y方向梯度,并计算筛选成绩相关参数
                Mat txMagnitude = _uImg.Sobel(MatType.CV_64FC1, 1, 0, 3);
                Mat tyMagnitude = _uImg.Sobel(MatType.CV_64FC1, 0, 1, 3);
                Mat torgMagnitude = Mat.Zeros(_uImg.Size(), MatType.CV_64FC1);
                double normMinScore = minScore / (double)queryCount;
                double normGreediness = ((1 - greediness * minScore) / (1 - greediness)) / queryCount;
                for(int row = 0; row < Height; row++)
                {
    
    
                    double* xMag = (double*)txMagnitude.Ptr(row);
                    double* yMag = (double*)tyMagnitude.Ptr(row);
                    double* oMag = (double*)torgMagnitude.Ptr(row);
                    for (int col = 0; col < Width; col++)
                    {
    
    
                        double dx = xMag[col], dy = yMag[col];
                        double _mag = Math.Sqrt(dx * dx + dy * dy);
                        oMag[col] = _mag;
                    }
                }
//开始匹配
                for(int row = 0; row < Height; row++)
                {
    
    
                    for (int col = 0; col < Width; col++)
                    {
    
    
                        double sum = 0;
                        double corSum = 0;
                        bool flag = false;
                        for (int cn = 0; cn < queryCount; cn++)
                        {
    
    
                            int xoff = queryEdgeInforms[cn].RelativePos.X;
                            int yoff = queryEdgeInforms[cn].RelativePos.Y;
                            //回去相对基准点的查询图像位置
                            int relaX = xoff + col;
                            int relaY = yoff + row;
                            if (relaY >= Height || relaX >= Width)
                            {
    
    
                                continue;
                            }
                            double txD = ((double*)txMagnitude.Ptr(relaY))[relaX];
                            double tyD = ((double*)tyMagnitude.Ptr(relaY))[relaX];
                            double tMag = ((double*)torgMagnitude.Ptr(relaY))[relaX];
                            double qxD = queryEdgeInforms[cn].DerivativeX;
                            double qyD = queryEdgeInforms[cn].DerivativeY;
                            double qMag = queryEdgeInforms[cn].Magnitude;
                            if((txD!=0 || tyD != 0) && (qxD != 0 || qyD != 0))
                            {
    
    
                                sum += (txD * qxD + tyD * qyD) * qMag / tMag;
                            }
                            corSum += 1;
                            partialScore = sum / corSum;
                            double curJudge = Math.Min((minScore - 1) + normGreediness * normMinScore, normMinScore * corSum);
                            //匹配分数不满足则进行下一个点匹配
                            if (partialScore < curJudge)
                            {
    
    
                                break;
                            }
                        }
                        if (partialScore > resultScore)
                        {
    
    
                            resultScore = partialScore;
                            conformPoints = new Point(col, row);
                        }

                    }
                }

                //二次筛选
                if (resultScore > 0.5)
                {
    
    
                    if(conformPoints.X+validSize.Width>Width||
                        conformPoints.Y + validSize.Height > Height)
                    {
    
    
                        resultScore = 0;
                    }
                }

                return resultScore;
            }


        }

4.3 Optimización de procesos

A través de la Parte 4.2, podemos entender claramente que el efecto de coincidencia de gradiente gris del borde sigue siendo bueno, pero todavía hay algunos defectos: solo se pueden obtener posiciones de imagen con posiciones relativas consistentes y la eficiencia de la consulta es baja ; para estos dos problemas, usted Primero puede consultar La imagen se segmenta y luego se hace coincidir en el mismo ángulo , y luego se realizan la recopilación de datos y las operaciones relacionadas para hacer coincidir los datos de la plantilla, de modo que el efecto mejora significativamente.
Insertar descripción de la imagen aquí

Insertar descripción de la imagen aquíInsertar descripción de la imagen aquí

{
    
    //图像轮廓边缘匹配
                Mat _sImg = detectImg.Clone();
                Mat queryImg = patternImg.Clone();
                Mat trainImg = detectImg.Clone();
                Mat queryGray = ImageGrayDetect(queryImg);
                Mat trainGray = ImageGrayDetect(trainImg);
                Mat queBlur = queryGray.GaussianBlur(new Size(3, 3), 0);
                Mat trainBlur = trainGray.GaussianBlur(new Size(3, 3), 0);
                List<Size> templateValidSize = new List<Size>();
                List<List<ImageEdgePtInform>> templatesEdgeInforms = new List<List<ImageEdgePtInform>>();
                ImageGeometricData queryGeoInform = new ImageGeometricData();
                ImageGeometricData trainGeoInform = new ImageGeometricData();
                Rect[] querySubBoundRects = BbProcesser.ImageConnectedFieldSegment(queBlur, 120, true, show: false).ConnectedFieldDatas.FieldRects.ToArray();
                Rect[] trainSubBoundRects = BbProcesser.ImageConnectedFieldSegment(trainBlur, 120, true, show: false).ConnectedFieldDatas.FieldRects.ToArray();
                Mat[] querySubImgs = new Mat[querySubBoundRects.Length];
                Mat[] trianSubImgs = new Mat[trainSubBoundRects.Length];
                List<Rect> matchedConformRegions = new List<Rect>();
                int margin = 5;
                for (int i =0;i< querySubImgs.Length;i++)
                {
    
    
                    Rect selectRegion = querySubBoundRects[i];
                    if(selectRegion.X-margin>=0 
                        && selectRegion.Width+margin<queBlur.Width
                        && selectRegion.Y-margin>=0
                        && selectRegion.Height + margin < queBlur.Height)
                    {
    
    
                        selectRegion.X -= margin;
                        selectRegion.Y -= margin;
                        selectRegion.Width += 2 * margin;
                        selectRegion.Height += 2 * margin;
                    }
                    querySubImgs[i] = new Mat(patternImg.Clone(), selectRegion);
                }
                for(int i = 0; i < trianSubImgs.Length; i++)
                {
    
    
                    Rect selectRegion = trainSubBoundRects[i];
                    if (selectRegion.X - margin >= 0
                        && selectRegion.Width + 2*margin < trainBlur.Width
                        && selectRegion.Y - margin >= 0
                        && selectRegion.Height + 2*margin < trainBlur.Height)
                    {
    
    
                        selectRegion.X -= margin;
                        selectRegion.Y -= margin;
                        selectRegion.Width += 2 * margin;
                        selectRegion.Height += 2 * margin;
                    }
                    trianSubImgs[i] = new Mat(detectImg.Clone(), selectRegion);
                }
                unsafe
                {
    
    
                    double[] querySubOrients = new double[querySubImgs.Length];
                    for(int i = 0; i < querySubImgs.Length; i++)
                    {
    
    
                        Size validSize = new Size();
                        List<ImageEdgePtInform> subEdgeInforms = GeoProcesser.ImageContourEdgeInfomGet(querySubImgs[i].Clone(), 3,ref validSize,show:true);
                        templateValidSize.Add(validSize);
                        templatesEdgeInforms.Add(subEdgeInforms);
                    }
                    for(int i = 0; i < trianSubImgs.Length; i++)
                    {
    
    
                        for(int j = 0; j < templateValidSize.Count; j++)
                        {
    
    
                            Point outPt = new Point();
                            //检测图像与模板角度不匹配时旋转
                            Mat _tImg = trianSubImgs[i].Clone();
                            {
    
    
                                _tImg = ImageGrayDetect(_tImg);
                                Cv2.GaussianBlur(_tImg, _tImg, new Size(3, 3), 0);
                                _tImg = _tImg.Threshold(120, 255, ThresholdTypes.BinaryInv);
                                Cv2.FindContours(_tImg, out Point[][] tCnts, out HierarchyIndex[] hidxs, RetrievalModes.External, ContourApproximationModes.ApproxSimple);
                                tCnts = tCnts.ToList().OrderByDescending(cnt => Cv2.ContourArea(cnt)).ToArray();
                                double tOrient = GeometricProcessor.ContourBaryCenterOrientationGet(tCnts[0]);
                                if (tOrient != templatesEdgeInforms[j][0].BarycentOrient)
                                {
    
    
                                    double rOrient = templatesEdgeInforms[j][0].BarycentOrient - tOrient;
                                    _tImg = ImageRotate(trianSubImgs[i].Clone(), new Point2f(_tImg.Width / 2, _tImg.Height / 2), rOrient, show: false);
                                }
                            }
                            
                            double score1 = GeometricProcessor.ImageEdgeMatch(trianSubImgs[i].Clone(), templatesEdgeInforms[j],
                                0.9, 1, templateValidSize[j], out outPt);
                            double score2 =GeometricProcessor.ImageEdgeMatch(_tImg, templatesEdgeInforms[j], 
                                0.9, 1, templateValidSize[j],out outPt);
                            if (Math.Max(score1,score2) > 0.9)
                            {
    
    
                                matchedConformRegions.Add(trainSubBoundRects[i]);
                                _sImg.Rectangle(trainSubBoundRects[i], Scalar.Red, 1) ;
                            }
                        }
                    }
                    if (matchedConformRegions.Count != 0)
                    {
    
    
                        Mat _attachedImg = ImagesMerge(querySubImgs, new Mat());
                        _sImg = ImagesMerge(new Mat[] {
    
     _attachedImg }, _sImg);
                        ImageShow("GradientDetectResult", _sImg);
                    }
                }
            }

5. Resumen

Durante el proceso de coincidencia, todavía se encuentran muchos problemas, por ejemplo, ¿cómo lidiar con la inconsistencia entre el tamaño de la plantilla y el objeto de consulta? Cómo hacer coincidir cuando el área bloqueada es demasiado grande, etc. . . tómalo con calma

6.Referencia

El algoritmo y la referencia de la imagen son los siguientes:
Función relacionada

Supongo que te gusta

Origin blog.csdn.net/JAYLEE900/article/details/131475498
Recomendado
Clasificación