【Qt&OpenCV 图像边缘检测 Sobel/Laplace/Canny】

边缘检测是图像处理技术,用于确定图片中物体的边界(边缘)或者区域。边缘是图像中重要的特征。我们通过边缘来了解图像的结构信息。边缘的特征在于像素亮度的突然变化,为了检测边缘,我们需要能够寻找出存在于相邻像素之间的这种变化。


前言

越来越多的开发人员选择基于开源的Qt框架与OpenCV来实现界面和算法,其原因不单单是无版权问题,更多是两个社区的发展蓬勃,可用来学习的资料与例程特别丰富。以下是关于利用Qt构建GUI并使用OpenCV中的Sobel/Laplace/Canny函数进行图像边缘检测。
软件版本:Qt-5.12.0/OpenCV-4.5.3
平台:Windows10/11–64


一、函数介绍

1、Soble

函数原型
cv::Sobel( InputArray src, OutputArray dst, int ddepth,
int dx, int dy, int ksize = 3,
double scale = 1, double delta = 0,
int borderType = BORDER_DEFAULT );

参数解释
src:为输入图像;
dst:目标图像,需要和源图片有一样的尺寸和类型;
ddepth::输出图像的深度,支持如下src.depth()和ddepth的组合:

若src.depth() = CV_8U, 取ddepth =-1/CV_16S/CV_32F/CV_64F
若src.depth() = CV_16U/CV_16S, 取ddepth =-1/CV_32F/CV_64F
若src.depth() = CV_32F, 取ddepth =-1/CV_32F/CV_64F
若src.depth() = CV_64F, 取ddepth = -1/CV_64F

dx:x 方向上的差分阶数;
dy:y方向上的差分阶数;
ksize:有默认值3,表示Sobel核的大小;必须取1,3,5或7。
scale:计算导数值时可选的缩放因子,默认值是1,表示默认情况下是没有应用缩放的;
delta:表示在结果存入目标图(第二个参数dst)之前可选的delta值,有默认值0;
borderType:边界模式,默认值为BORDER_DEFAULT;

2、Laplace

函数原型
cv::Laplacian( InputArray src,
OutputArray dst,
int ddepth,
int ksize = 1,
double scale = 1,
double delta = 0,
int borderType = BORDER_DEFAULT
)

参数解释
src:待提取边缘的图像;
dst:输出图像,与输入图像src具有相同的尺寸和通道数,数据类型由第三个参数ddepth控制;
ddepth:输出图像的数据类型(深度);
ksize:表示Laplacian核的大小;
scale:对导数计算结果进行缩放的缩放因子,默认系数为1,不进行缩放;
delta:偏移值,在计算结果中加上偏移值;
borderType:边界模式,默认值为BORDER_DEFAULT;

3、Canny

函数原型
cv::Canny(InputArray src, OutputArray dst,
double threshold1, double threshold2,
int aperture_size=3);

参数解释
src:输入图像, 单通道灰度图像;
dst: 输出图像;
threshold1/threshold2:函数的两个阀值;

二、演示

1、GUI

在这里插入图片描述
如上图创建Operator的QComboBox控件进行函数选择,Action的功能按钮QPushButton,对当前窗口的图像进行边缘检测,并输出状态信息。

2、代码实现

edgeBtn的clicked()槽函数的实现代码:

void MainWindow::on_edgeBtn_clicked()
{
    std::size_t numView = ui->tabWidget->currentIndex() % 3;
    if (dispMat[numView]->empty())
    {
        outputInfo(2, tr("Please make sure the Mat exist!"));
        return;
    }

    if (dispMat[numView]->channels() == 3)
    {
        cv::cvtColor(*dispMat[numView], *dispMat[numView], cv::COLOR_RGB2GRAY);
    }

    tmpMat->zeros(dispMat[numView]->size(), dispMat[numView]->type());
    cv::GaussianBlur(*dispMat[numView], *dispMat[numView], cv::Size(3, 3), \	// 图像去噪
                      0, 0, cv::BORDER_DEFAULT);
    int operatorType = ui->edgeCombo->currentIndex();

    switch (operatorType)
    {
        case 0:		// Sobel
        {
            cv::Mat gradX, gradY, absGradX, absGradY;
            cv::Sobel(*dispMat[numView], gradX, CV_16S, 1, 0, 3, 1, \
                  0, cv::BORDER_DEFAULT);
            cv::convertScaleAbs(gradX, absGradX);
            cv::Sobel(*dispMat[numView], gradY, CV_16S, 0, 1, 3, 1, \
                      0, cv::BORDER_DEFAULT);

            cv::convertScaleAbs(gradY, absGradY);
            cv::addWeighted(absGradX, 0.5, absGradY, 0.5, 0, *tmpMat);

            outputInfo(1, tr("Sobel done."));

            break;
        }
        case 1:		// Laplace
        {
            cv::Laplacian(*dispMat[numView], *tmpMat, CV_16S, 3, 1, 0, \
                      cv::BORDER_DEFAULT);
            cv::convertScaleAbs(*tmpMat, *tmpMat);

            outputInfo(1, tr("Laplace done."));

            break;
        }
        case 2:		// Canny
        {
            cv::Mat cannyMat;
            int cannyThresh = ui->cannySlider->value();
            cv::Canny(*dispMat[numView], cannyMat, cannyThresh, cannyThresh * 3, \
                      3);
            *tmpMat = cv::Scalar::all(0);
            dispMat[numView]->copyTo(*tmpMat, cannyMat);
            outputInfo(1, tr("Canny done."));
            break;
        }
    }

    if (ui->edgeChkBox->isChecked())
    {
        *dispMat[numView] = tmpMat->clone();
        cvtMatPixmap(dispMat, dispPixmap, numView);
    }
    else
    {
        if (tmpMat->channels() == 3)
        {
            QImage tmpImage = QImage(tmpMat->data, tmpMat->cols,tmpMat->rows, \
                         static_cast<int>(tmpMat->step), \
                         QImage::Format_RGB888);
            dispPixmap[numView]->setPixmap(QPixmap::fromImage(tmpImage.rgbSwapped()));
        }
        else
        {
            QImage tmpImage = QImage(tmpMat->data, tmpMat->cols,tmpMat->rows, \
                         static_cast<int>(tmpMat->step), \
                         QImage::Format_Grayscale8);
            dispPixmap[numView]->setPixmap(QPixmap::fromImage(tmpImage.rgbSwapped()));
        }

    }
}

总结

以上是关于利用Qt进行GUI构建并使用OpenCV中的Sobel/Laplace/Canny函数进行图像边缘检测。
参考:
链接:前期https://blog.csdn.net/richard_yuu/article/details/128014922

其中疑问或错误,欢迎联系交流

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt中进行二维码边缘轮廓检测,可以使用OpenCV库提供的函数来实现。具体步骤如下: 1. 加载图像:使用Qt提供的QImage类或OpenCV库提供的cv::imread函数加载图像。 2. 灰度化:使用OpenCV库提供的cv::cvtColor函数将图像转换为灰度图像。 3. 二值化:使用OpenCV库提供的cv::threshold函数将灰度图像转换为二值图像。 4. 边缘检测:使用OpenCV库提供的cv::Canny函数进行边缘检测。 5. 轮廓检测:使用OpenCV库提供的cv::findContours函数进行轮廓检测。 6. 绘制轮廓:使用Qt提供的QPainter类或OpenCV库提供的cv::drawContours函数将轮廓绘制在图像上。 完整代码示例: ```cpp #include <opencv2/opencv.hpp> #include <QImage> #include <QPainter> QImage cvMat2QImage(const cv::Mat& mat) { QImage image(mat.cols, mat.rows, QImage::Format_RGB32); for (int y = 0; y < mat.rows; ++y) { QRgb* scanLine = reinterpret_cast<QRgb*>(image.scanLine(y)); for (int x = 0; x < mat.cols; ++x) { const cv::Vec3b& bgr = mat.at<cv::Vec3b>(y, x); scanLine[x] = qRgb(bgr[2], bgr[1], bgr[0]); } } return image; } void detectQRCode(const QImage& inputImage, QImage& outputImage) { cv::Mat inputMat(inputImage.height(), inputImage.width(), CV_8UC4, const_cast<uchar*>(inputImage.bits()), inputImage.bytesPerLine()); cv::Mat grayMat, binaryMat; cv::cvtColor(inputMat, grayMat, cv::COLOR_BGRA2GRAY); cv::threshold(grayMat, binaryMat, 128, 255, cv::THRESH_BINARY); cv::Mat edgesMat; cv::Canny(binaryMat, edgesMat, 100, 200); std::vector<std::vector<cv::Point>> contours; cv::findContours(edgesMat, contours, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); cv::drawContours(inputMat, contours, -1, cv::Scalar(255, 0, 0), 1); outputImage = cvMat2QImage(inputMat); } int main(int argc, char* argv[]) { QImage inputImage(":/qrcode.png"); QImage outputImage; detectQRCode(inputImage, outputImage); outputImage.save("output.png"); return 0; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值