#if !defined LAPLACEZC
#define LAPLACEZC
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
class LaplacianZC {
private:
// original image
cv::Mat img;
// 32-bit float image containing the Laplacian
cv::Mat laplace;
// Aperture size of the laplacian kernel
int aperture;
public:
LaplacianZC() : aperture(3) {}
// Set the aperture size of the kernel
void setAperture(int a) {
aperture = a;
}
// Get the aperture size of the kernel
int getAperture() const {
return aperture;
}
// Compute the floating point Laplacian
cv::Mat computeLaplacian(const cv::Mat& image) {
// Compute Laplacian
cv::Laplacian(image, laplace, CV_32F, aperture);
// Keep local copy of the image
// (used for zero-crossings)
img = image.clone();
return laplace;
}
// Get the Laplacian result in 8-bit image
// zero corresponds to gray level 128
// if no scale is provided, then the max value will be
// scaled to intensity 255
// You must call computeLaplacian before calling this method
cv::Mat getLaplacianImage(double scale = -1.0) {
if (scale<0) {
double lapmin, lapmax;
cv::minMaxLoc(laplace, &lapmin, &lapmax);
scale = 127 / std::max(-lapmin, lapmax);
}
cv::Mat laplaceImage;
laplace.convertTo(laplaceImage, CV_8U, scale, 128);
return laplaceImage;
}
// Get a binary image of the zero-crossings
// if the product of the two adjascent pixels is
// less than threshold then this zero-crossing will be ignored
cv::Mat getZeroCrossings(float threshold = 1.0) {
// Create the iterators
cv::Mat_<float>::const_iterator it = laplace.begin<float>() + laplace.step1();
cv::Mat_<float>::const_iterator itend = laplace.end<float>();
cv::Mat_<float>::const_iterator itup = laplace.begin<float>();
// Binary image initialize to white
cv::Mat binary(laplace.size(), CV_8U, cv::Scalar(255));
cv::Mat_<uchar>::iterator itout = binary.begin<uchar>() + binary.step1();
// negate the input threshold value
threshold *= -1.0;
for (; it != itend; ++it, ++itup, ++itout) {
// if the product of two adjascent pixel is negative
// then there is a sign change
if (*it * *(it - 1) < threshold)
*itout = 0; // horizontal zero-crossing
else if (*it * *itup < threshold)
*itout = 0; // vertical zero-crossing
}
return binary;
}
// Get a binary image of the zero-crossings
// if the product of the two adjacent pixels is
// less than threshold then this zero-crossing will be ignored
cv::Mat getZeroCrossingsWithSobel(float threshold) {
cv::Mat sx;
cv::Sobel(img, sx, CV_32F, 1, 0, 1);
cv::Mat sy;
cv::Sobel(img, sy, CV_32F, 0, 1, 1);
// Create the iterators
cv::Mat_<float>::const_iterator it = laplace.begin<float>() + laplace.step1();
cv::Mat_<float>::const_iterator itend = laplace.end<float>();
cv::Mat_<float>::const_iterator itup = laplace.begin<float>();
cv::Mat_<float>::const_iterator itx = sx.begin<float>() + sx.step1();
cv::Mat_<float>::const_iterator ity = sy.begin<float>() + sy.step1();
// Binary image initialize to white
cv::Mat binary(laplace.size(), CV_8U, cv::Scalar(255));
cv::Mat_<uchar>::iterator itout = binary.begin<uchar>() + binary.step1();
for (; it != itend; ++it, ++itup, ++itout, ++itx, ++ity) {
// if the product of two adjacent pixel is negative
// then there is a sign change
if (*it * *(it - 1) < 0.0 && fabs(*ity) > threshold)
*itout = 0; // horizontal zero-crossing
else if (*it * *itup < 0.0 && fabs(*ity) > threshold)
*itout = 0; // vertical zero-crossing
}
return binary;
}
};
#endif
//这种边缘检测与canny变换类似,效果不如canny变换
#include <iostream>
#include <iomanip>
#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "laplacianZC.h"
int main()
{
// Read input image
cv::Mat image = cv::imread("bfg.jpg", 0);
if (!image.data)
return 0;
// Display the image
cv::namedWindow("Original Image");
cv::imshow("Original Image", image);
// Compute Sobel X derivative 默认图核为3X3 高通滤波 ,方向滤波,X水平方向表示为 1,0
cv::Mat sobelX;
cv::Sobel(image, sobelX, CV_8U, 1, 0, 3, 0.4, 128);
// Display the image
cv::namedWindow("Sobel X Image");
cv::imshow("Sobel X Image", sobelX);
// Compute Sobel Y derivative
cv::Mat sobelY;
cv::Sobel(image, sobelY, CV_8U, 0, 1, 3, 0.4, 128);
// Display the image
cv::namedWindow("Sobel Y Image");
cv::imshow("Sobel Y Image", sobelY);
// Compute norm of Sobel
cv::Sobel(image, sobelX, CV_16S, 1, 0);
cv::Sobel(image, sobelY, CV_16S, 0, 1);
cv::Mat sobel;
//compute the L1 norm 水平和垂直滤波相加获得Sobel标准型
sobel = abs(sobelX) + abs(sobelY);
double sobmin, sobmax;
cv::minMaxLoc(sobel, &sobmin, &sobmax);//获得最大最小值double形
std::cout << "sobel value range: " << sobmin << " " << sobmax << std::endl;
// Print window pixel values
for (int i = 0; i<12; i++) {
for (int j = 0; j<12; j++)
std::cout << std::setw(5) << static_cast<int>(sobel.at<short>(i + 135, j + 362)) << " ";
std::cout << std::endl;
}
std::cout << std::endl;
std::cout << std::endl;
std::cout << std::endl;
// Conversion to 8-bit image
// sobelImage = -alpha*sobel + 255
cv::Mat sobelImage;
sobel.convertTo(sobelImage, CV_8U, -255. / sobmax, 255);//将图片CV_16S转化为CV_8U格式,且设置颜色范围,线性变换
// Display the image
cv::namedWindow("Sobel Image");
cv::imshow("Sobel Image", sobelImage);
// Apply threshold to Sobel norm (low threshold value)
cv::Mat sobelThresholded;
cv::threshold(sobelImage, sobelThresholded, 225, 255, cv::THRESH_BINARY);//根据图像偏白设置大的限定值225能够保证一个具体的边缘线条,设置低的190可能导致边缘缺失
// Display the image
cv::namedWindow("Binary Sobel Image (low)");
cv::imshow("Binary Sobel Image (low)", sobelThresholded);
// Apply threshold to Sobel norm (high threshold value)
cv::threshold(sobelImage, sobelThresholded, 190, 255, cv::THRESH_BINARY);
// Display the image
cv::namedWindow("Binary Sobel Image (high)");
cv::imshow("Binary Sobel Image (high)", sobelThresholded);
/
// Compute Laplacian 3x3
cv::Mat laplace;
cv::Laplacian(image, laplace, CV_8U, 1, 1, 128);
// Display the image
cv::namedWindow("Laplacian Image");
cv::imshow("Laplacian Image", laplace);
// Print window pixel values
for (int i = 0; i<12; i++) {
for (int j = 0; j<12; j++)
std::cout << std::setw(5) << static_cast<int>(laplace.at<uchar>(i + 135, j + 362)) - 128 << " ";
std::cout << std::endl;
}
std::cout << std::endl;
std::cout << std::endl;
std::cout << std::endl;
// Compute Laplacian 7x7
cv::Laplacian(image, laplace, CV_8U, 7, 0.01, 128);
// Display the image
cv::namedWindow("Laplacian Image");
cv::imshow("Laplacian Image", laplace);
// Print window pixel values
for (int i = 0; i<12; i++) {
for (int j = 0; j<12; j++)
std::cout << std::setw(5) << static_cast<int>(laplace.at<uchar>(i + 135, j + 362)) - 128 << " ";
std::cout << std::endl;
}
// Extract small window
cv::Mat window(image, cv::Rect(362, 135, 12, 12));
cv::namedWindow("Image window");
cv::imshow("Image window", window);
cv::imwrite("window.bmp", window);
// Compute Laplacian using LaplacianZC class
LaplacianZC laplacian;
laplacian.setAperture(7);
cv::Mat flap = laplacian.computeLaplacian(image);
double lapmin, lapmax;
cv::minMaxLoc(flap, &lapmin, &lapmax);
std::cout << "Laplacian value range=[" << lapmin << "," << lapmax << "]\n";
laplace = laplacian.getLaplacianImage();
cv::namedWindow("Laplacian Image (7x7)");
cv::imshow("Laplacian Image (7x7)", laplace);
// Print Laplacian values
std::cout << std::endl;
for (int i = 0; i<12; i++) {
for (int j = 0; j<12; j++)
std::cout << std::setw(5) << static_cast<int>(flap.at<float>(i + 135, j + 362) / 100) << " ";
std::cout << std::endl;
}
std::cout << std::endl;
// Compute and display the zero-crossing points
cv::Mat zeros;
zeros = laplacian.getZeroCrossings(lapmax);
cv::namedWindow("Zero-crossings");
cv::imshow("Zero-crossings", zeros);
// Compute and display the zero-crossing points (Sobel version)
zeros = laplacian.getZeroCrossings();
zeros = laplacian.getZeroCrossingsWithSobel(20);
cv::namedWindow("Zero-crossings (2)");
cv::imshow("Zero-crossings (2)", zeros);
// Print window pixel values
for (int i = 0; i<12; i++) {
for (int j = 0; j<12; j++)
std::cout << std::setw(2) << static_cast<int>(zeros.at<uchar>(i + 135, j + 362)) << " ";
std::cout << std::endl;
}
// Display the image with window
cv::rectangle(image, cv::Point(362, 135), cv::Point(374, 147), cv::Scalar(255, 255, 255));
cv::namedWindow("Original Image with window");
cv::imshow("Original Image with window", image);
cv::waitKey();
return 0;
}