因为目前更多的使用到的是java来编译opencv,所以很多例子使用了java,但是网上的相关java的资料比较少,多数都是c++的,所以整理一下,和c++大同小异。
这里提供一个比较好的 论坛资料,当然还有官方网站,这里提供3.4.1的版本API链接
1. 基本结构
1.1 Mat
1.1.1 创建和销毁
Mat是opencv中最基本的矩阵存储类型
//创建
Mat A = new Mat(3, 4, CvType.CV_64FC1);
//或者分两步:
Mat A = new Mat();
A.create(3,4,CvType.CV_64FC1);
//或者静态类创建:
A = Mat.eye(3,3, CvType.CV_64FC1);
A = Mat.zero(3,3,CvType.CV_64FC1);
矩阵使用完毕不用了,要记得销毁:
A.release();
1.1.2 运算
对矩阵进行操作是很简单的,因为OpenCV有专门的方法,其他的不说了,有一个特别要命,主要是Java开发人员特别要小心的,就是矩阵的乘法。对于C++开发者而言,矩阵乘法可以直接用*搞定,例如A=B*C。Java语言是不支持运算符重载的,因此也就不可能用这么简单的形式实现,只能用方法实现。
不过你找javadoc的时候会发现,Mat类里也提供了multi的方法,哈哈……且慢欢喜,这个不是我们说的矩阵乘法,而是矩阵对应元素的乘法,真正的矩阵乘法在Core类里,需要用Core.gemm()的形式调用。我写了一个简单那的函数实现了简单的矩阵乘法:
/**
* 计算矩阵A和B的乘,得到新的矩阵C。即 C=A*B;
* 注意:A,B,C调用前必需要初始化完毕。
* @param A 被乘矩阵
* @param B 乘矩阵
* @param C 结果矩阵
* @return 矩阵C
*/
public Mat matMul(Mat A, Mat B, Mat C)
{
Core.gemm(A, B, 1.0, Mat.zeros(A.size(), A.type()), 0.0, C);
return C;
}
这个方法是有文档的,不说啥了,目前还没有找到其他更好的方法。注意Mat类里还有dot()方法和cross()方法,都是给向量准备的,一个点乘,一个叉乘,其中叉乘还只能是三元素的向量。
更多相关Mat操作的方法详见链接
1.2 Point
在图像中定义 2D 点
Point pt;
pt.x = 10;
pt.y = 8;
//或者
Point pt = Point(10, 8);
1.3 Scalar
表示了具有4个元素的数组。次类型在OpenCV中被大量用于传递像素值。
本节中,我们将进一步用它来表示RGB颜色值(三个参数)。如果用不到第四个参数,则无需定义。
我们来看个例子,如果给出以下颜色参数表达式:Scalar( a, b, c )
那么定义的RGB颜色值为: Red = c, Green = b and Blue = a
point 和scalar example:
public static void main(String[] args) {
// TODO Auto-generated method stub
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
int width = 1000 ;
Mat atom_image = Mat.zeros(width, width,CvType.CV_32F);
rectangle(atom_image, new Point(width/2-50,width/2-50),new Point(width/2+50,width/2+50), new Scalar(255, 255, 255), 5);
Imgcodecs.imwrite("F:\\java_program_workspace\\pdf-extractor\\resources\\outputImage\\tmp.png", atom_image);
}
关于Point和Scalar更多example详见链接
2. 常见方法
2.1 轮廓和边界识别
OpenCV支持大量的轮廓、边缘、边界的相关函数,相应的函数有moments、HuMoments、findContours、 drawContours、approxPolyDP、arcLength、boundingRect、contourArea、convexHull、 fitEllipse、fitLine、isContourConvex、minAreaRect、minEnclosingCircle、 mathcShapes、pointPolygonTest。
findContours
:找到图像中轮廓(方法详情详见链接1 链接2)
approxPolyDP
:对多边形曲线做近似(方法详见 链接 )
boundingRect
:计算并返回包围轮廓点集的最小矩形
minEnclosingCircle
:计算并返回包围轮廓点集的最小圆形及其半径
drawContours
:根据轮廓点集和轮廓结构画出轮廓
// 050 创建包围轮廓的矩形和圆形边界框.cpp :定义控制台应用程序的入口点。
#include"stdafx.h"
#include"opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream>
#include <stdio.h>
#include<stdlib.h>
using namespace cv;
using namespace std;
Mat src; Mat src_gray;
int thresh = 100;
int max_thresh = 255;
RNG rng(12345);
/// 函数声明
void thresh_callback(int, void* );
int main( int argc, char** argv )
{
/// 载入原图像, 返回3通道图像
src = imread( "boundrect.jpg");
///转化成灰度图像并进行平滑
cvtColor( src, src_gray, CV_BGR2GRAY );
blur( src_gray, src_gray, Size(3,3));
/// 创建窗口
char* source_window = "Source";
namedWindow( source_window, CV_WINDOW_AUTOSIZE);
imshow( source_window, src);
createTrackbar( "Threshold:", "Source", &thresh, max_thresh,thresh_callback );
thresh_callback( 0, 0);
waitKey(0);
return(0);
}
void thresh_callback(int, void* )
{
Mat threshold_output;
vector<vector<Point>>contours; //轮廓数组(非矩形数组),每个轮廓是一个Point型的vector
vector<Vec4i>hierarchy; //见下面findContours的解释
///使用Threshold二值
threshold( src_gray, threshold_output, thresh,255, THRESH_BINARY );
/// 找到轮廓
//contours参数为检测的轮廓数组,每一个轮廓用一个point类型的vector表示
//hiararchy参数和轮廓个数相同,每个轮廓contours[ i ]对应4个hierarchy元素hierarchy[ i][ 0 ] ~hierarchy[ i ][ 3 ],
//分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,该值设置为负数。
//CV_RETR_TREE:建立一个等级树结构的轮廓
findContours( threshold_output, contours,hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
/// 多边形逼近轮廓 +获取矩形和圆形边界框
vector<vector<Point>> contours_poly( contours.size()); //近似后的轮廓点集
vector<Rect>boundRect( contours.size()); //包围点集的最小矩形vector
vector<Point2f>center(contours.size()); //包围点集的最小圆形vector
vector<float>radius( contours.size()); //包围点集的最小圆形半径vector
for( int i = 0; i< contours.size(); i++ )
{
approxPolyDP(Mat(contours[i]), contours_poly[i], 3, true); //对多边形曲线做适当近似,contours_poly[i]是输出的近似点集
boundRect[i] = boundingRect( Mat(contours_poly[i])); //计算并返回包围轮廓点集的最小矩形
minEnclosingCircle( contours_poly[i], center[i], radius[i]); //计算并返回包围轮廓点集的最小圆形及其半径
}
/// 画多边形轮廓 + 包围的矩形框 + 圆形框
Mat drawing = Mat::zeros(threshold_output.size(), CV_8UC3 );
for( int i = 0; i<contours.size(); i++ )
{
Scalar color = Scalar( rng.uniform(0, 255), rng.uniform(0,255),rng.uniform(0,255) ); //随机颜色
// drawContours( drawing,contours_poly, i, color, 1, 8,vector<Vec4i>(), 0, Point() );
drawContours( drawing, contours_poly, i, color, 1, 8, hierarchy, 0,Point()); //根据轮廓点集contours_poly和轮廓结构hierarchy画出轮廓
rectangle( drawing, boundRect[i].tl(), boundRect[i].br(), color, 2,8, 0); //画矩形,tl矩形左上角,br右上角
circle( drawing, center[i], (int)radius[i], color, 2, 8, 0); //画圆形
}
/// 显示在一个窗口
namedWindow( "Contours", CV_WINDOW_AUTOSIZE);
imshow( "Contours", drawing );
}
参考链接
2.2 滤波函数
图像滤波的作用: 在尽可能保留图像细节特征的条件下,对目标图像的噪声进行抑制。
常见的图像滤波器:
- 线性滤波: 均值滤波 高斯滤波
- 非线性滤波:中值滤波 双边滤波
在opencv的C++中,常见的滤波函数有为:blur
,GaussianBlur
,meidaBlur
,bilateralFilter
.其分别为均值滤波、
2.2.1 均值滤波(blur
)
void blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DEFAULT )
功能:对输入的图像src进行均值滤波后用dst输出。
参数:src和dst当然分别是输入图像和输出图像。size为均值滤波器模板大小。Anchor为锚点(具体什么没看源码不懂),如果为Point(-1,-1),则锚点是滤波器的中心点。borderType为边缘点插值类型。
理解:以原图对应像素为中心的与模板中心重叠,将模板覆盖领域内全部像素求均值就是滤波后像素的值了。
2.2.2 高斯滤波(GaussianBlur
)
void GaussianBlur(InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY=0, int borderType=BORDER_DEFAULT )
功能:对输入的图像src进行高斯滤波后用dst输出。
参数:src和dst当然分别是输入图像和输出图像。Ksize为高斯滤波器模板大小,sigmaX和sigmaY分别为高斯滤波在横线和竖向的滤波系数(有点晦涩,等下解释)。borderType为边缘点插值类型。
理解:数字图像的滤波可以简单的这么理解,就是对原图像的每一个像素滤波,那么对应这个像素滤波后的值是根据其相邻像素(包括自己那个点)与一个滤波模板进行相乘即可。所以具体到高斯滤波,我们只要知道这个高斯滤波的模板即可。
更多关于高斯滤波
2.2.3 中值滤波(medianBlur
)
void medianBlur(InputArray src, OutputArray dst, int ksize)
功能:对输入的图像src进行中值滤波后用dst输出。
参数:src和dst当然分别是输入图像和输出图像。ksize为均值滤波器模板大小,因为模板为正方形,所以只有一个参数。
理解:以原图对应像素为中心的与模板中心重叠,将模板覆盖领域内全部像素排序后的中间值就是滤波后像素的值了,所以模板长度必须为奇数。
2.2.4 双向滤波(bilateralFilter
)
void bilateralFilter(InputArray src, OutputArray dst, int d, double sigmaColor, double sigmaSpace, int borderType=BORDER_DEFAULT )
功能:对输入的图像src进行双向滤波后用dst输出。
参数:src和dst当然分别是输入图像和输出图像。d为每个像素领域的直径,sigmaColor为颜色空间的标准偏差,sigmaSpace为坐标空间的标准偏差。borderType为边缘点插值类型。
理解:暂时不明白双向滤波的工作原理,以后有时间弄懂再补上吧,也欢迎大家补上。
关于更多滤波细节详见链接
图像二值化
图像的二值化就是将图像上的像素点的灰度值设置为0或255,这样将使整个图像呈现出明显的黑白效果。在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。OpenCV中提供了函数cv::threshold();
函数原型(链接):
double cv::threshold( InputArray src,
OutputArray dst,
double thresh,
double maxval,
int type
)
2.3 图像腐蚀和膨胀
图像腐蚀和图像膨胀是图像中两种最基本形态学操作。通过腐蚀和膨胀,可以让图像的线条更加明显,便于后续处理。
opencv中腐蚀和膨胀的参数完全一样。
- src:原图像。
- dst:目标图像。
-element:腐蚀操作的内核。 如果不指定,默认为一个简单的 3x3 矩阵。否则,我们就要明确指定它的形状,可以使用函数getStructuringElement(). - anchor:默认为Point(-1,-1),内核中心点。省略时为默认值。
- iterations:腐蚀次数。省略时为默认值1。
- borderType:推断边缘类型,具体参见borderInterpolate函数。默认为BORDER_DEFAULT,省略时为默认值。
- borderValue:边缘值,具体可参见createMorphoogyFilter函数。可省略。
通常情况下,我们可以使用函数getStructuringElement()来制作操作内核。
Mat getStructuringElement(int shape, Size esize, Point anchor=Point(-1,-1));
参数:
- shape:内核形状,主要有MORPH_RECT,MORPH_CROSS和MORPH_ELLIPSE,分别为矩形、椭圆形和交叉形,对应的值分别为0,1,2
- enum { MORPH_RECT=0, MORPH_CROSS=1, MORPH_ELLIPSE=2 };
- esize:内核大小。
anchor:内核锚点,默认为内核中心点。
例:Mat element = getStructuringElement( 0,Size( 2*i + 1, 2*i+1 ), Point(i, i ) ); /// 腐蚀操作 erode( src, dst, element ); Mat element = getStructuringElement( 0,Size( 2*i + 1, 2*i+1 ), Point(i, i ) ); /// 膨胀操作 dilate( src, dst, element );
2.3.1 图像腐蚀(erode
)
void erode( const Mat& src, Mat& dst, const Mat& element,Point anchor=Point(-1,-1), int iterations=1,int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue() );
2.3.2 图像膨胀(dilate
)
void dilate( const Mat& src, Mat& dst, const Mat& element,Point anchor=Point(-1,-1), int iterations=1,int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue() );