基本原理
Prewitt算子是一种用于边缘检测的经典算子,它通过计算图像中像素值的(一阶导数)梯度来检测边缘。Prewitt算子通常包括两个3x3的卷积核,一个用于检测水平方向上的边缘,另一个用于检测垂直方向上的边缘。
示例代码1
使用OpenCV C++实现Prewitt算子
以下是一个使用OpenCV C++实现Prewitt算子边缘检测的示例代码:
#include <opencv2/opencv.hpp>
#include <iostream>
int main(int argc, char** argv)
{
if (argc != 2) {
std::cout << "Usage: " << argv[0] << " <Image Path>" << std::endl;
return -1;
}
// 读取图像
cv::Mat src = cv::imread(argv[1], cv::IMREAD_GRAYSCALE);
if (!src.data) {
std::cout << "Error: Image cannot be loaded!" << std::endl;
return -1;
}
// 创建输出图像
cv::Mat grad_x, grad_y, abs_grad_x, abs_grad_y, combined_grad;
// 定义Prewitt算子的卷积核
cv::Mat kernel_x = (cv::Mat_<double>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
cv::Mat kernel_y = (cv::Mat_<double>(3, 3) << 1, 1, 1, 0, 0, 0, -1, -1, -1);
// 对图像应用Prewitt算子
cv::filter2D(src, grad_x, CV_64F, kernel_x);
cv::filter2D(src, grad_y, CV_64F, kernel_y);
// 转换为8位无符号整型
cv::convertScaleAbs(grad_x, abs_grad_x);
cv::convertScaleAbs(grad_y, abs_grad_y);
// 合并梯度图像
cv::addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, combined_grad);
// 显示原始图像和边缘检测后的图像
cv::namedWindow("Original Image", cv::WINDOW_AUTOSIZE);
cv::imshow("Original Image", src);
cv::namedWindow("Prewitt X Gradient", cv::WINDOW_AUTOSIZE);
cv::imshow("Prewitt X Gradient", abs_grad_x);
cv::namedWindow("Prewitt Y Gradient", cv::WINDOW_AUTOSIZE);
cv::imshow("Prewitt Y Gradient", abs_grad_y);
cv::namedWindow("Combined Gradient", cv::WINDOW_AUTOSIZE);
cv::imshow("Combined Gradient", combined_grad);
cv::waitKey();
return 0;
}
代码解释
1.读取图像:从命令行参数读取图像路径,并将其加载为灰度图像。
2.定义卷积核:定义Prewitt算子的两个3x3卷积核,一个用于水平方向,一个用于垂直方向。
3.应用卷积核:使用cv::filter2D函数对图像应用Prewitt算子的卷积核,分别计算水平方向和垂直方向上的梯度。
4.转换为8位图像:由于卷积操作可能会产生负值,因此需要使用cv::convertScaleAbs函数将梯度图像转换为8位无符号整型图像。
5.合并梯度图像:使用cv::addWeighted函数将水平方向和垂直方向上的梯度图像合并成一个图像。
6.显示结果:使用cv::imshow函数分别显示原始图像、水平方向梯度图像、垂直方向梯度图像以及合并后的梯度图像。
注意事项
数据类型:在进行卷积操作时,通常选择CV_64F作为中间结果的数据类型,以避免溢出。但在显示图像之前,需要将其转换为CV_8U。
边界处理:卷积操作可能会导致边界上的像素值不准确。在实际应用中,可以通过填充边界来解决这个问题。
参数调整:可以根据实际需求调整卷积核的大小和权重,以优化边缘检测的效果。
运行结果1
示例代码2
以下是使用OpenCV C++实现Prewitt算子进行边缘检测的示例代码:
#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
// 定义Prewitt算子掩膜
static const float prewittHorizontal[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1};
static const float prewittVertical[] = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
void detectEdgesWithPrewitt(const Mat &src, Mat &horizontal, Mat &vertical, Mat &magnitude) {
Mat kernelHorizontal = Mat(3, 3, CV_32F, prewittHorizontal);
Mat kernelVertical = Mat(3, 3, CV_32F, prewittVertical);
// 水平方向的边缘检测
filter2D(src, horizontal, CV_32F, kernelHorizontal);
// 垂直方向的边缘检测
filter2D(src, vertical, CV_32F, kernelVertical);
// 计算边缘强度
magnitude = sqrt(horizontal.mul(horizontal) + vertical.mul(vertical));
// 将结果转换为8位无符号整数
convertScaleAbs(magnitude, magnitude);
}
int main(int argc, char** argv) {
if (argc != 2) {
cout << "Usage: ./PrewittEdgeDetection <Image Path>" << endl;
return -1;
}
// 加载图像
Mat img = imread(argv[1], IMREAD_GRAYSCALE);
if (!img.data) {
cout << "Error opening image" << endl;
return -1;
}
// 初始化输出矩阵
Mat horizontal, vertical, magnitude;
// 执行Prewitt边缘检测
detectEdgesWithPrewitt(img, horizontal, vertical, magnitude);
// 显示结果
imshow("Original Image", img);
imshow("Prewitt Horizontal", horizontal);
imshow("Prewitt Vertical", vertical);
imshow("Magnitude", magnitude);
waitKey(0);
destroyAllWindows();
return 0;
}
代码解释
1. 定义Prewitt算子掩膜: 使用静态数组来定义Prewitt算子的两个掩膜。
2. 边缘检测: 使用 filter2D 函数分别计算水平方向和垂直方向上的边缘。
3. 计算边缘强度: 通过计算水平方向和垂直方向边缘的平方和的平方根来得到边缘强度。
4. 转换数据类型: 使用 convertScaleAbs 函数将浮点型数据转换为8位无符号整数,以便于显示。
5. 显示结果: 使用 imshow 函数显示原始图像、水平方向边缘、垂直方向边缘以及边缘强度。
注意事项
•数据类型: 在计算过程中,我们使用了 CV_32F 类型来保持精度,但在最后为了显示结果,需要将数据转换为 CV_8U 类型。
•噪声处理: Prewitt算子对噪声比较敏感,因此在实际应用中,通常会对图像进行适当的预处理(如高斯滤波)来减少噪声的影响。
运行结果2
实验代码3
#include "pch.h"
#include <iostream>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/types_c.h>
#include <opencv2/highgui/highgui_c.h>
using namespace cv;
using namespace std;
//#pragma comment(lib,"opencv_world450d.lib")
void getPrewitt_oper(cv::Mat& getPrewitt_horizontal, cv::Mat& getPrewitt_vertical, cv::Mat& getPrewitt_Diagonal1, cv::Mat& getPrewitt_Diagonal2) {
//水平方向
getPrewitt_horizontal = (cv::Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);
//垂直方向
getPrewitt_vertical = (cv::Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);
//对角135°
getPrewitt_Diagonal1 = (cv::Mat_<float>(3, 3) << 0, 1, 1, -1, 0, 1, -1, -1, 0);
//对角45°
getPrewitt_Diagonal2 = (cv::Mat_<float>(3, 3) << -1, -1, 0, -1, 0, 1, 0, 1, 1);
//逆时针反转180°得到卷积核
cv::flip(getPrewitt_horizontal, getPrewitt_horizontal, -1);
cv::flip(getPrewitt_vertical, getPrewitt_vertical, -1);
cv::flip(getPrewitt_Diagonal1, getPrewitt_Diagonal1, -1);
cv::flip(getPrewitt_Diagonal2, getPrewitt_Diagonal2, -1);
}
void edge_Prewitt(cv::Mat& src, cv::Mat& dst1, cv::Mat& dst2, cv::Mat& dst3, cv::Mat& dst4, cv::Mat& dst, int ddepth, double delta = 0, int borderType = cv::BORDER_DEFAULT) {
//获取Prewitt算子
cv::Mat getPrewitt_horizontal;
cv::Mat getPrewitt_vertical;
cv::Mat getPrewitt_Diagonal1;
cv::Mat getPrewitt_Diagonal2;
getPrewitt_oper(getPrewitt_horizontal, getPrewitt_vertical, getPrewitt_Diagonal1, getPrewitt_Diagonal2);
//卷积得到水平方向边缘
cv::filter2D(src, dst1, ddepth, getPrewitt_horizontal, cv::Point(-1, -1), delta, borderType);
//卷积得到4垂直方向边缘
cv::filter2D(src, dst2, ddepth, getPrewitt_vertical, cv::Point(-1, -1), delta, borderType);
//卷积得到45°方向边缘
cv::filter2D(src, dst3, ddepth, getPrewitt_Diagonal1, cv::Point(-1, -1), delta, borderType);
//卷积得到135°方向边缘
cv::filter2D(src, dst4, ddepth, getPrewitt_Diagonal2, cv::Point(-1, -1), delta, borderType);
//边缘强度(近似)
cv::convertScaleAbs(dst1, dst1); //求绝对值并转为无符号8位图
cv::convertScaleAbs(dst2, dst2);
cv::convertScaleAbs(dst3, dst3); //求绝对值并转为无符号8位图
cv::convertScaleAbs(dst4, dst4);
dst = dst1 + dst2;
}
int main() {
cv::Mat src = cv::imread("2.jpeg");
if (src.empty())
{
return -1;
}
if (src.channels() > 1) cv::cvtColor(src, src, CV_RGB2GRAY);
cv::Mat dst, dst1, dst2, dst3, dst4;
//注意:要采用CV_32F,因为有些地方卷积后为负数,若用8位无符号,则会导致这些地方为0
edge_Prewitt(src, dst1, dst2, dst3, dst4, dst, CV_32F);
cv::namedWindow("src", CV_WINDOW_NORMAL);
imshow("src", src);
cv::namedWindow("水平边缘", CV_WINDOW_NORMAL);
imshow("水平边缘", dst1);
cv::namedWindow("垂直边缘", CV_WINDOW_NORMAL);
imshow("垂直边缘", dst2);
cv::namedWindow("45°边缘", CV_WINDOW_NORMAL);
imshow("45°边缘", dst3);
cv::namedWindow("135°边缘", CV_WINDOW_NORMAL);
imshow("135°边缘", dst4);
cv::namedWindow("边缘强度", CV_WINDOW_NORMAL);
imshow("边缘强度", dst);
cv::waitKey(0);
return 0;
}