Notas de desarrollo de OpenCV (cuarenta y uno): el hombre gordo rojo le toma 8 minutos para aprender más sobre la detección de bordes del operador del filtro scharr (gráfico + fácil de entender + código fuente del programa)

Si el artículo es original, no se puede reproducir sin permiso La
dirección del blog original del blogger: https://blog.csdn.net/qq21497936 La
navegación del blog original del blogger: https://blog.csdn.net/qq21497936/article/details / 102478062
La dirección del blog de este artículo: https://blog.csdn.net/qq21497936/article/details/105425374
Lectores, el conocimiento es infinito y la mano de obra es pobre, cambien las necesidades o encuentren profesionales o investiguen

Directorio

Prólogo

Manifestación

Filtro Scharr y detección de bordes del operador Sobel

Resumen

Operador Sobel y filtro Scharr

Principio

Paso 1: derivación en direcciones x e y

Paso 2: encuentra el gradiente aproximado

La función Scharr es la misma que Sobel, con pasos adicionales.

Paso 1: Cree dos cv :: Mat cachés primero

Paso 2: Encuentra el gradiente en la dirección X

Paso 3: Encuentra el gradiente en la dirección Y

Paso 4: fusionar gradientes (aproximados)

Prototipo de función Scharr

Función de conversión de color

Código fuente de demostración

Plantilla de ingeniería: número de versión correspondiente v1.36.0


Columna de desarrollo de OpenCV (haga clic en el portal)

 

    Notas de desarrollo de OpenCV (cuarenta y uno): el hombre gordo rojo le toma 8 minutos para aprender más sobre la detección de bordes del operador del filtro scharr (gráfico + fácil de entender + código fuente del programa)

 

Prólogo

      Hombre gordo rojo también viene! ! !

      Este capítulo explica el filtro scharrr y compara la detección de bordes del operador sobel.

 

Manifestación

            Diagrama de ubicación del resultado de la imagen de demostración:

 

Filtro Scharr y detección de bordes del operador Sobel

Resumen

      Primero, comprenda la detección de bordes del operador Sobel. El filtro Scharr es en realidad un trasplante de la operación de programación real de Sobel, pero solo aumenta los datos de la matriz del núcleo. Su precisión es mayor que la detección de bordes del operador Sobel. Aquí comparamos el operador Sobel. Detección de sub-borde y filtro Scharr para detección de borde.

      Para obtener detalles sobre la detección de bordes del operador de Sobel, consulte "Notas de desarrollo de OpenCV (39): Red Fat Man lo lleva en 8 minutos para obtener más información sobre la detección de bordes del operador de Sobel (gráficos + fácil de entender + código fuente del programa)"

Operador Sobel y filtro Scharr

El cálculo de Sobel produce un mejor efecto de detección y tiene un efecto de supresión suave sobre el ruido, pero los bordes obtenidos son más gruesos y pueden aparecer bordes falsos.

Cuando la imagen es más complicada, el objetivo y el fondo no se pueden separar simplemente por un umbral. En este momento, solo recurra al método de detección de bordes o al método de crecimiento de área. El método de detección de bordes más utilizado es el operador Sobel .

El operador de Sobel tiene las ventajas de un método simple, una velocidad de procesamiento rápida y bordes suaves y continuos. La desventaja es que los bordes son más gruesos, debido al proceso de binarización, los bordes obtenidos tienen una gran relación con la selección del umbral.

Al llamar a Sobel (), puede configurar ksize en :: SCHARR para lograrlo, o puede usar el filtro Scharr solo, que es más preciso que el operador Sobel. Por lo tanto, cuando elige el filtro 3x3, se prefiere el filtro Scharr La velocidad es tan rápida como Sobel ().

Principio

Paso 1: derivación en direcciones x e y

Supongamos que la imagen de entrada es I.

Cambio horizontal: Convolución de I y núcleos de tamaño impar, por ejemplo, cuando el tamaño del núcleo es 3, el resultado del cálculo de Gx es:

      Cambio vertical: Convolucione I con un núcleo de tamaño impar. Por ejemplo, cuando el tamaño del núcleo es 3, el resultado del cálculo de Gy es:

Paso 2: encuentra el gradiente aproximado

En cada punto de la imagen, combine Gx y Gy para encontrar el gradiente aproximado:

Además, también puede usar un anuncio público más simple:

La función Scharr es la misma que Sobel, con pasos adicionales.

Paso 1: Cree dos cv :: Mat cachés primero

// 使用Scharr滤波器
// 步骤一:先分别创建2个矩阵,共4个
cv::Mat gradXMat, absGradXMat;
cv::Mat gradYMat, absGradYMat;
// copy
mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                cv::Range(srcMat.cols * 0, srcMat.cols * 1));

Paso 2: Encuentra el gradiente en la dirección X

// 步骤二:求X方向的梯度
//cv::Sobel(grayMat3Channels, gradXMat, CV_16S, 1, 0, 3, 1, 1);
cv::Scharr(grayMat3Channels, gradXMat, CV_16S, 1, 0, 1, 0,);
cv::convertScaleAbs(gradXMat, absGradXMat);

Paso 3: Encuentra el gradiente en la dirección Y

// 步骤三:求Y方向的梯度
//cv::Sobel(grayMat3Channels, gradYMat, CV_16S, 0, 1, 3, 1, 1);
cv::Scharr(grayMat3Channels, gradXMat, CV_16S, 0, 1, 1, 0,);
cv::convertScaleAbs(gradYMat, absGradYMat);

Paso 4: fusionar gradientes (aproximados)

// 步骤四:合并梯度(近似)
cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);

Prototipo de función Scharr

void Scharr( InputArray src,
           OutputArray dst,
           int ddepth,
           int dx,
           int dy, 
           double scale = 1,
           double delta = 0,
           int borderType = BORDER_DEFAULT );
  • Parámetro uno: imagen de tipo InputArray, generalmente cv :: Mat;
  • Parámetro 2: dst de tipo OutputArray, el mismo tamaño que la imagen de entrada;
  • Tres parámetros: tipo int ddepth, la profundidad de la imagen de salida , 8-bit resultado de imagen de entrada causa se trunca, tiene la siguiente composición:

  • Parámetro cuatro: int tipo dx, orden diferencial en dirección x, 1 o 0;
  • Parámetro cinco: int tipo dy, orden diferencial en la dirección y, 1 o 0;
  • Parámetro seis: escala de tipo doble, factor de escala opcional para el valor derivado del cálculo de escala; el valor predeterminado es 1, lo que indica que no se realiza escala;
  • Parámetro 7: los detalles de tipo doble y delta se agregan al resultado antes de almacenar el resultado en dst;
  • Parámetro ocho: borderType de tipo int, generalmente no necesita prestar atención;

Función de conversión de color

void convertScaleAbs(InputArray src,
                   OutputArray dst,
                   double alpha = 1,
                   double beta = 0);
  • Parámetro uno: imagen de tipo InputArray, generalmente cv :: Mat;
  • Parámetro 2: dst de tipo OutputArray, imagen de salida;
  • Parámetro tres: doble tipo appha;
  • Parámetro 4: profundidad de tipo doble;

(Suplemento: de hecho, esto es similar al ajuste de contraste y brillo, pero no lo es, porque cuando el resultado del cálculo es menor que 0, no se procesará, lo que provocará que el valor se endeude hacia adelante, sino que se vuelva blanco)

 

Código fuente de demostración

void OpenCVManager::testScharr()
{
    QString fileName1 = "E:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/11.jpg";
    cv::Mat srcMat = cv::imread(fileName1.toStdString());

    if(!srcMat.data)
    {
        qDebug() << __FILE__ << __LINE__
                 << "Failed to load image:" << fileName1;
        return;
    }

    int width = 300;
    int height = 200;
    cv::resize(srcMat, srcMat, cv::Size(width, height));

    cv::String windowName = _windowTitle.toStdString();
    cvui::init(windowName);


    cv::Mat dstMat;
    dstMat = cv::Mat::zeros(srcMat.size(), srcMat.type());
    cv::Mat windowMat = cv::Mat(cv::Size(dstMat.cols * 3, dstMat.rows * 4),
                                srcMat.type());
    int ksize = 1;   // 核心大小
    int sigmaX = 0;  // x方向的标准偏差
    int sigmaY = 0;  // y方向的标准偏差

    int ksize2 = 1;

    int sigmaS = 160;
    int sigmaR = 2;

    cvui::window(windowMat, dstMat.cols, 0, dstMat.cols, dstMat.rows, "settings");
    cv::Mat grayMat;
    cv::Mat grayMat3Channels;
    cv::Mat mat;
    cv::cvtColor(srcMat, grayMat, CV_BGR2GRAY);
    cv::cvtColor(grayMat, grayMat3Channels, CV_GRAY2BGR);
    while(true)
    {
        windowMat = cv::Scalar(0, 0, 0);
        // 原图先copy到左边
        cv::Mat leftMat = windowMat(cv::Range(0, srcMat.rows),
                                    cv::Range(0, srcMat.cols));
        cv::addWeighted(leftMat, 1.0f, srcMat, 1.0f, 0.0f, leftMat);
        // 中间为调整滤波参数的相关设置
        cvui::printf(windowMat, width * 2 + 75, 20, "ksize = size *  2 + 1");
        cvui::trackbar(windowMat, width * 2 + 75, 30, 165, &ksize, 0, 10);

        cvui::printf(windowMat, width * 2 + 75, 80, "sigmaX");
        cvui::trackbar(windowMat, width * 2 + 75, 90, 165, &sigmaX, 0, 100);

        cvui::printf(windowMat, width * 2 + 75, 140, "sigmaY");
        cvui::trackbar(windowMat, width * 2 + 75, 150, 165, &sigmaY, 0, 100);

        // 复制灰度图像
        {
            cv::Mat rightMat = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                                         cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(rightMat, 0.0f, grayMat3Channels, 1.0f, 0.0f, rightMat);
        }

        {
            // 高斯滤波
            cv::Mat mat;
            cv::GaussianBlur(grayMat3Channels, mat, cv::Size(ksize * 2 + 1, ksize * 2 + 1), sigmaX / 10.f, sigmaY / 10.f);

            // 高斯滤波后进行边缘检测
            // 使用Sobel边缘检测
            // 步骤一:先分别创建2个矩阵,共4个
            cv::Mat gradXMat, absGradXMat;
            cv::Mat gradYMat, absGradYMat;
            // 步骤二:求X方向的梯度
            cv::Sobel(mat, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
            cv::convertScaleAbs(gradXMat, absGradXMat);
            // 步骤三:求Y方向的梯度
            cv::Sobel(mat, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
            cv::convertScaleAbs(gradYMat, absGradYMat);
            // 步骤四:合并梯度(近似)
            cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
            cv::Mat rightMat2 = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                                          cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(rightMat2, 0.0f, dstMat, 1.0f, 0.0f, rightMat2);

        }
        {
            // 使用Sobel边缘检测
            // 步骤一:先分别创建2个矩阵,共4个
            cv::Mat gradXMat, absGradXMat;
            cv::Mat gradYMat, absGradYMat;
            // 步骤二:求X方向的梯度
            cv::Sobel(grayMat3Channels, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
            cv::convertScaleAbs(gradXMat, absGradXMat);
            // 步骤三:求Y方向的梯度
            cv::Sobel(grayMat3Channels, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
            cv::convertScaleAbs(gradYMat, absGradYMat);
            // 步骤四:合并梯度(近似)
            cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        cvui::printf(windowMat, 75 + width * 2, height * 2 + 20, "sigmaS");
        cvui::trackbar(windowMat, 75 + width * 2, height * 2 + 50, 165, &sigmaS, 101, 10000);
        cvui::printf(windowMat, 75 + width * 2, height * 2 + 90, "sigmaR");
        cvui::trackbar(windowMat, 75 + width * 2, height * 2 + 120, 165, &sigmaR, 1, 100);
        cvui::printf(windowMat,
                     srcMat.cols * 2 + 75,
                     srcMat.rows * 1 + 100, "ksize");
        cvui::trackbar(windowMat,
                       srcMat.cols * 2 + 75,
                       srcMat.rows * 1 + 130,
                       165,
                       &ksize2,
                       0,
                       3);
        {
            // 使用自适应流形应用高维滤波。
            cv::Mat tempMat;
            cv::Ptr<cv::ximgproc::AdaptiveManifoldFilter> pAdaptiveManifoldFilter
                    = cv::ximgproc::createAMFilter(sigmaS/100.0f, sigmaR/100.0f, true);
            pAdaptiveManifoldFilter->filter(grayMat, tempMat);
            cv::cvtColor(tempMat, tempMat, CV_GRAY2BGR);
            // 使用Sobel边缘检测
            // 步骤一:先分别创建2个矩阵,共4个
            cv::Mat gradXMat, absGradXMat;
            cv::Mat gradYMat, absGradYMat;
            // 步骤二:求X方向的梯度
            cv::Sobel(tempMat, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
            cv::convertScaleAbs(gradXMat, absGradXMat);
            // 步骤三:求Y方向的梯度
            cv::Sobel(tempMat, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
            cv::convertScaleAbs(gradYMat, absGradYMat);
            // 步骤四:合并梯度(近似)
            cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                            cv::Range(srcMat.cols * 0, srcMat.cols * 1));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }

        {
            // 高斯滤波
            cv::Mat mat;
            cv::GaussianBlur(grayMat3Channels, mat, cv::Size(ksize * 2 + 1, ksize * 2 + 1), sigmaX / 10.f, sigmaY / 10.f);

            // 高斯滤波后进行边缘检测
            // 使用Sobel边缘检测
            // 步骤一:先分别创建2个矩阵,共4个
            cv::Mat gradXMat, absGradXMat;
            cv::Mat gradYMat, absGradYMat;
            // 步骤二:求X方向的梯度
//            cv::Sobel(mat, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
            cv::Scharr(mat, gradXMat, CV_16S, 1, 0, 1, 1);
            cv::convertScaleAbs(gradXMat, absGradXMat);
            // 步骤三:求Y方向的梯度
//            cv::Sobel(mat, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
            cv::Scharr(mat, gradYMat, CV_16S, 1, 0, 1, 1);
            cv::convertScaleAbs(gradYMat, absGradYMat);
            // 步骤四:合并梯度(近似)
            cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
            cv::Mat rightMat2 = windowMat(cv::Range(srcMat.rows * 1, srcMat.rows * 2),
                                          cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(rightMat2, 0.0f, dstMat, 1.0f, 0.0f, rightMat2);
        }

        {
            // 使用Sobel边缘检测
            // 步骤一:先分别创建2个矩阵,共4个
            cv::Mat gradXMat, absGradXMat;
            cv::Mat gradYMat, absGradYMat;
            // 步骤二:求X方向的梯度
//            cv::Sobel(grayMat3Channels, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
            cv::Scharr(grayMat3Channels, gradXMat, CV_16S, 1, 0, 1, 1);
            cv::convertScaleAbs(gradXMat, absGradXMat);
            // 步骤三:求Y方向的梯度
//            cv::Sobel(grayMat3Channels, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
            cv::Scharr(grayMat3Channels, gradYMat, CV_16S, 1, 0, 1, 1);
            cv::convertScaleAbs(gradYMat, absGradYMat);
            // 步骤四:合并梯度(近似)
            cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 2, srcMat.rows * 3),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        {
            // 使用自适应流形应用高维滤波。
            cv::Mat tempMat;
            cv::Ptr<cv::ximgproc::AdaptiveManifoldFilter> pAdaptiveManifoldFilter
                    = cv::ximgproc::createAMFilter(sigmaS/100.0f, sigmaR/100.0f, true);
            pAdaptiveManifoldFilter->filter(grayMat, tempMat);
            cv::cvtColor(tempMat, tempMat, CV_GRAY2BGR);
            // 使用Sobel边缘检测
            // 步骤一:先分别创建2个矩阵,共4个
            cv::Mat gradXMat, absGradXMat;
            cv::Mat gradYMat, absGradYMat;
            // 步骤二:求X方向的梯度
//            cv::Sobel(tempMat, gradXMat, CV_16S, 1, 0, ksize2 * 2 + 1, 1, 1);
            cv::Scharr(tempMat, gradXMat, CV_16S, 1, 0, 1, 1);
            cv::convertScaleAbs(gradXMat, absGradXMat);
            // 步骤三:求Y方向的梯度
//            cv::Sobel(tempMat, gradYMat, CV_16S, 0, 1, ksize2 * 2 + 1, 1, 1);
            cv::Scharr(tempMat, gradYMat, CV_16S, 1, 0, 1, 1);
            cv::convertScaleAbs(gradYMat, absGradYMat);
            // 步骤四:合并梯度(近似)
            cv::addWeighted(absGradXMat, 0.5, absGradYMat, 0.5, 0, dstMat);
            // copy
            mat = windowMat(cv::Range(srcMat.rows * 3, srcMat.rows * 4),
                            cv::Range(srcMat.cols * 1, srcMat.cols * 2));
            cv::addWeighted(mat, 0.0f, dstMat, 1.0f, 0.0f, mat);
        }
        // 更新
        cvui::update();
        // 显示
        cv::imshow(windowName, windowMat);
        // esc键退出
        if(cv::waitKey(25) == 27)
        {
            break;
        }
    }
}

.

Plantilla de ingeniería: número de versión correspondiente v1.36.0

      Número de versión correspondiente v1.36.0

 

La dirección del blog del blogger original: https://blog.csdn.net/qq21497936 La
navegación del blog del blogger original: https://blog.csdn.net/qq21497936/article/details/102478062
La dirección del blog de este artículo: https: // blog .csdn.net / qq21497936 / article / details / 105425374

Publicado 260 artículos originales · elogiado 411 · 520,000 visitas

Supongo que te gusta

Origin blog.csdn.net/qq21497936/article/details/105425374
Recomendado
Clasificación