特征值检测
梯度计算
梯度可以反应图像的像素差异:对于图像边缘部分,梯度值会比较大;对于图像的平坦区域,梯度值比较小、OpenCv 提供了两个非常有用的计算函数Sobel与Scharr
Sobel梯度算子分为X方向与Y方向,可以分别计算X与Y方向的梯度成像
Sobel(Mat src,Mat dst,int ddpeth,int x,int y)
ddpeth:表示输入图像的深度,常见为CV_32SC或者CV_32F
x:x方向的梯度1为是,0为否‘
y:y方向的梯度同上
Mat gradx = new Mat();
Imgproc.Sobel(src, gradx, CvType.CV_32F, 1, 0);
Core.convertScaleAbs(gradx, gradx);
Log.i("OpenCV", "XGradient....");
// Y方向梯度
Mat grady = new Mat();
Imgproc.Sobel(src, grady, CvType.CV_32F, 0, 1);
Core.convertScaleAbs(grady, grady);
Log.i("OpenCV", "YGradient....");
Core.addWeighted(gradx,0.5, grady, 0.5, 0, dst);
gradx.release();
grady.release();
Log.i("OpenCV", "Gradient.....");
Scharr梯度
Scharr梯度是sobel算子的升级加强版,只是更强的计算算子
Scharr(Mat src,Mat dst,int ddpeth,int x,int y)
Mat gradx = new Mat();
Imgproc.Scharr(src, gradx, CvType.CV_32F, 1, 0);
Core.convertScaleAbs(gradx, gradx);
// Y方向梯度
Mat grady = new Mat();
Imgproc.Scharr(src, grady, CvType.CV_32F, 0, 1);
Core.convertScaleAbs(grady, grady);
Core.addWeighted(gradx,0.5, grady, 0.5, 0, dst);
拉普拉斯算子
拉普拉斯可以增强图像,也可以用于边缘检测
Imgproc.Laplacian(src, dst,int ddepth,int kszie,double scale,double delta);
ddepth:图像深度,常见为CV_32fF
kszie:3x3
scale;是否伸缩默认为1
delta:是否调整像素默认为0
Canny边缘检测
Canny边缘检测是对一种噪点比较敏感点边缘检测方法,通常在使用Canny边缘检测之前,首先对图像进行降噪。
完整对Canny检测步骤
- 高数模糊:完成噪点抑制
- 灰度转化:在灰度图像上计算梯度值
- 计算梯度:使用Sobel/Scharr
- 非最大信号抑制:在梯度图像上寻找局部最大值
- 高低阀直连接:把边缘像素连接成线段,形成完整边缘轮廓
Imgproc.Canny(gradx, grady, edges, 50, 150);
private void houghLinePDemo(Mat src, Mat dst) {
Mat edges = new Mat();
Imgproc.Canny(src, edges, 50, 150, 3, true);
Mat lines = new Mat();
Imgproc.HoughLinesP(edges, lines, 1, Math.PI/180.0, 100, 50, 10);
Mat out = Mat.zeros(src.size(), src.type());
for(int i=0; i<lines.rows(); i++) {
int[] oneline = new int[4];
lines.get(i, 0, oneline);
Imgproc.line(out, new Point(oneline[0], oneline[1]),
new Point(oneline[2], oneline[3]),
new Scalar(0, 0, 255), 2, 8, 0);
}
out.copyTo(dst);
// 释放内存
out.release();
edges.release();
}
private void houghLinesDemo(Mat src, Mat dst) {
Mat edges = new Mat();
Imgproc.Canny(src, edges, 50, 150, 3, true);
Mat lines = new Mat();
Imgproc.HoughLines(edges, lines, 1,Math.PI/180.0, 200);
Mat out = Mat.zeros(src.size(), src.type());
float[] data = new float[2];
for(int i=0; i<lines.rows(); i++) {
lines.get(i, 0, data);
float rho = data[0], theta = data[1];
double a = Math.cos(theta), b = Math.sin(theta);
double x0 = a*rho, y0 = b*rho;
Point pt1 = new Point();
Point pt2 = new Point();
pt1.x = Math.round(x0 + 1000*(-b));
pt1.y = Math.round(y0 + 1000*(a));
pt2.x = Math.round(x0 - 1000*(-b));
pt2.y = Math.round(y0 - 1000*(a));
Imgproc.line(out, pt1, pt2, new Scalar(0,0,255), 3, Imgproc.LINE_AA, 0);
}
out.copyTo(dst);
out.release();
edges.release();
}
霍夫圆检测原理
霍夫圆变换的基本原理和霍夫线变换类似, 只是点对应的二维极径极角空间被三维的圆心点x, y还有半径r空间取代.
private void houghCircleDemo(Mat src, Mat dst) {
Mat gray = new Mat();
Imgproc.pyrMeanShiftFiltering(src, gray, 15, 80);
Imgproc.cvtColor(gray, gray, Imgproc.COLOR_BGR2GRAY);
Imgproc.GaussianBlur(gray, gray, new Size(3, 3), 0);
// detect circles
Mat circles = new Mat();
dst.create(src.size(), src.type());
Imgproc.HoughCircles(gray, circles, Imgproc.HOUGH_GRADIENT, 1, 20, 100, 30, 10, 200);
for(int i=0; i<circles.cols(); i++) {
float[] info = new float[3];
circles.get(0, i, info);
Imgproc.circle(dst, new Point((int)info[0], (int)info[1]), (int)info[2],
new Scalar(0, 255, 0), 2, 8, 0);
}
circles.release();
gray.release();
}
轮廓发现与绘制
private void findContoursDemo(Mat src, Mat dst) {
Mat gray= new Mat();
Mat binary = new Mat();
// 二值
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(gray, binary, 0, 255, Imgproc.THRESH_BINARY | Imgproc.THRESH_OTSU);
// 轮廓发现
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
Mat hierarchy = new Mat();
Imgproc.findContours(binary, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE, new Point(0, 0));
// 绘制轮廓
dst.create(src.size(), src.type());
for(int i=0; i<contours.size(); i++) {
Imgproc.drawContours(dst, contours, i, new Scalar(0, 0, 255), 2);
}
// 释放内存
gray.release();
binary.release();
}
图像直方图
private void displayHistogram(Mat src, Mat dst) {
Mat gray = new Mat();
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
// 计算直方图数据并归一化
List<Mat> images = new ArrayList<>();
images.add(gray);
Mat mask = Mat.ones(src.size(), CvType.CV_8UC1);
Mat hist = new Mat();
Imgproc.calcHist(images, new MatOfInt(0), mask, hist, new MatOfInt(256), new MatOfFloat(0, 255));
Core.normalize(hist, hist, 0, 255, Core.NORM_MINMAX);
int height = hist.rows();
dst.create(400, 400, src.type());
dst.setTo(new Scalar(200, 200, 200));
float[] histdata = new float[256];
hist.get(0, 0, histdata);
int offsetx = 50;
int offsety = 350;
// 绘制直方图
Imgproc.line(dst, new Point(offsetx, 0), new Point(offsetx, offsety), new Scalar(0, 0, 0));
Imgproc.line(dst, new Point(offsetx, offsety), new Point(400, offsety), new Scalar(0, 0, 0));
for(int i=0; i<height-1; i++) {
int y1 = (int)histdata[i];
int y2 = (int)histdata[i+1];
Rect rect = new Rect();
rect.x = offsetx+i;
rect.y = offsety-y1;
rect.width = 1;
rect.height = y1;
Imgproc.rectangle(dst, rect.tl(), rect.br(), new Scalar(15, 15, 15));
}
// 释放内存
gray.release();
}