边缘检测是图像处理技术,用于确定图片中物体的边界(边缘)或者区域。边缘是图像中重要的特征。我们通过边缘来了解图像的结构信息。边缘的特征在于像素亮度的突然变化,为了检测边缘,我们需要能够寻找出存在于相邻像素之间的这种变化。
前言
越来越多的开发人员选择基于开源的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函数进行图像边缘检测。