版權聲明:本文爲博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/PinkRiverside/article/details/72062388
圖像的邊緣定義爲兩個強度明顯不同的區域之間的過渡,圖像的梯度函數即圖像灰度變化的速率將在這些過渡邊界上存在最大值。如果一個點位於邊緣點上,那麼它的灰度值會出現階躍性的變化,對應於一階導數的極值點、二階導數的過零點,如圖所示。
由圖可知,一階導數的極值點與二階導數的過零點可用來檢測邊緣。另外,一階導數與二階導數對噪聲非常敏感,尤其是二階導數。因此,在進行邊緣檢測之前,需充分考慮圖像的平滑,以減少噪聲的影響。
1)一階微分邊緣檢測
圖像灰度函數
2)二階微分邊緣檢測
通過找圖像灰度二階導數的零交叉點可以確定邊緣。圖像灰度函數f(x,y)在點f(x,y)在點(x,y)的二階微分爲
式中,由二階導數的定義可得拉普拉斯算子爲
在離散區域中,對連續的拉普拉斯算子最簡單的近似運算是計算斜面沿每一個軸線的差值:
該四鄰域的拉普拉斯算子可以通過卷積來生成:
式中
增益標準化的四鄰域拉普拉斯算子脈衝響應的定義式爲
#include "opencv2/opencv.hpp"
#include <iostream>
using namespace std;
using namespace cv;
void Gaussian(const Mat &inputImg, Mat &outputImg, double sigma) {
//計算窗口大小
int windows = ((int)(6 * sigma - 1)) / 2 * 2 + 1;
double *value = new double[windows];
double sum = 0;
//計算卷積核
int left = -windows / 2, right = windows / 2 + 1, border = windows / 2;
for (int i = left; i < right; i++){
value[i + border] = pow(2.718, -(i * i) / (2 * sigma * sigma)) / (sqrt(2 * 3.1415926) * sigma);
sum += value[i + border];
}
for (int i = left; i < right; i++){
value[i + border] = value[i + border] / sum;
cout << value[i + border] << endl;
}
//存儲中間結果圖像
Mat tempImg(inputImg.rows + border * 2, inputImg.cols + border * 2, CV_8UC1);
//填充圖像
Mat fillImg(inputImg.rows + border * 2, inputImg.cols + border * 2, CV_8UC1);
for (int i = border; i < inputImg.rows + border; i++) {
for (int j = border; j < inputImg.cols + border; j++) {
fillImg.at<uchar>(i, j) = inputImg.at<uchar>((i-border) % (inputImg.rows - 1),(j-border) % (inputImg.cols - 1));
}
}
//填充圖像上方
for (int i = 0; i < border; i++) {
for (int j = 0; j < inputImg.cols + border * 2; j++) {
fillImg.at<uchar>(i%fillImg.rows, j%fillImg.cols) = inputImg.at<uchar>(i%(inputImg.rows-1), j%(inputImg.cols-1));
}
}
//填充圖像下放
for (int i = inputImg.rows+border; i < inputImg.rows + border * 2; i++) {
for (int j = 0; j < inputImg.cols + border * 2; j++) {
fillImg.at<uchar>(i%fillImg.rows, j%fillImg.cols) = inputImg.at<uchar>((i-border*2) % (inputImg.rows - 1), j % (inputImg.cols - 1));
}
}
//填充左方
for (int i = border; i < inputImg.rows + border; i++) {
for (int j = 0; j < border; j++) {
fillImg.at<Vec3b>(i%fillImg.rows, j%fillImg.cols)[0] = inputImg.at<Vec3b>((i-border) % (inputImg.rows - 1), j % (inputImg.cols - 1))[0];
fillImg.at<Vec3b>(i%fillImg.rows, j%fillImg.cols)[1] = inputImg.at<Vec3b>((i - border) % (inputImg.rows - 1), j % (inputImg.cols - 1))[1];
fillImg.at<Vec3b>(i%fillImg.rows, j%fillImg.cols)[2] = inputImg.at<Vec3b>((i - border) % (inputImg.rows - 1), j % (inputImg.cols - 1))[2];
}
}
//填充右方
for (int i = border; i < inputImg.rows + border; i++) {
for (int j = inputImg.cols+border; j < inputImg.cols + border*2; j++) {
fillImg.at<uchar>(i%fillImg.rows, j%fillImg.cols)= inputImg.at<uchar>((i - border) % (inputImg.rows - 1), (j-border*2) % (inputImg.cols - 1));
}
}
namedWindow("填充後", WINDOW_AUTOSIZE);
imshow("填充後", fillImg);
//對每行進行一維高斯濾波
for (int i = 0; i < tempImg.rows; i++){
for (int j = border; j < tempImg.cols - border; j++){
tempImg.at<uchar>(i, j)= 0;
for (int k = left; k < right; k++){
tempImg.at<uchar>(i, j) += (fillImg.at<uchar>(i, j + k) * value[k + border]);
}
}
}
//對結果的每列進行一維高斯濾波
for (int i = 0; i < outputImg.rows; i++){
for (int j = 0; j < outputImg.cols; j++){
outputImg.at<uchar>(i, j) = 0;
for (int k = left; k < right; k++){
outputImg.at<uchar>(i, j) += (tempImg.at<uchar>(i + k+border, j+border) * value[k + border]);
}
}
}
namedWindow("高斯濾波", WINDOW_AUTOSIZE);
imshow("高斯濾波", outputImg);
}
int main(int argc, char** argv){
Mat pic=imread("d:\\test1.jpg",0);
imshow("testpic", pic);
int lap[3] = { -1, 4, 1 };
Mat outGaussian(pic.rows, pic.cols, CV_8UC1);
Mat out(pic.rows, pic.cols, CV_8UC1);
Mat temp(pic.rows, pic.cols, CV_8UC1);
for (int i = 0; i < pic.rows; i++){
for (int j = 0; j < pic.cols; j++){
outGaussian.at<uchar>(i, j) = pic.at<uchar>(i, j);
out.at<uchar>(i, j) = pic.at<uchar>(i, j);
}
}
for (int i = 1; i < pic.rows - 1; i++){
for (int j = 1; j < pic.cols - 1; j++){
out.at<uchar>(i, j) = (pic.at<uchar>(i, j) * 4 - pic.at<uchar>(i - 1, j) - pic.at<uchar>(i + 1, j) - pic.at<uchar>(i, j - 1) - pic.at<uchar>(i, j + 1)) / 4;
}
}
Gaussian(pic, temp,2);
for (int i = 1; i < pic.rows-1; i++){
for (int j = 1; j < pic.cols-1; j++){
outGaussian.at<uchar>(i, j) = (temp.at<uchar>(i, j) * 4 - temp.at<uchar>(i - 1, j) - temp.at<uchar>(i + 1, j) - temp.at<uchar>(i, j - 1) - temp.at<uchar>(i, j + 1)) / 4;
}
}
imshow("out", out);
imshow("outGaussian", outGaussian);
waitKey();
return 0;
}