总结下opencv java部分使用过的API.
opencv各种模块功能繁多,目前博主也只是简单了解一点,下面就用到的一些功能简答列举记录下.
一.图像的简单处理
图像加载
直接使用Imgcodecs模块 imread, 读取图片路径,选择图片模式,即可读取图片的Mat格式.
this.image = Imgcodecs.imread(file.getAbsolutePath());// , Imgcodecs.IMREAD_COLOR);
this.grayimage = Imgcodecs.imread(file.getAbsolutePath(), Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);
后续Java上的相关操作都是以这个为基础.
我们需要简单的了解,Mat存储了图片的矩阵点信息,它的设计理念是包含通用的图片头部及一个指向真真图片数据的指针.,以便能方便的复用图片数据.Java里面封装了使用JNI调用c++对象的细节。
在Java里面,需要知道,这就是个引用对象,如果不想修改图片,
我们需要复制图片,然后再操作。
this.grayimage.copyTo(m);
mat的数据结构 灰度图片
mat的数据结构,彩色图片
这里是Mat 原始c的文档说明
https://docs.opencv.org/3.4.2/d3/d63/classcv_1_1Mat.html
图像灰度调整
简单图片处理调整灰度。 BGR2GRAY.
普通读取图片然后转换
Imgproc.cvtColor(frame, grayFrame, Imgproc.COLOR_BGR2GRAY);
或者直接读取为灰度
this.grayimage = Imgcodecs.imread(file.getAbsolutePath(), Imgcodecs.CV_LOAD_IMAGE_GRAYSCALE);
图像锐化
图像的锐化,可用使用Imgproc.filter2D方法来实现。
Mat kernel = new Mat(3, 3, CvType.CV_32F);
// int[] values = {0, -1, 0, -1, 5, -1, 0, -1, 0};
// Log.d("imageType", CvType.typeToString(src.type()) + "");
kernel.put(0, 0, 0, -1, 0, -1, 5, -1, 0, -1, 0);
Mat dstImage = new Mat();
Imgproc.filter2D(input, dstImage, input.depth(), kernel);
结果如下:
图像模糊去噪
主要用于图片离散杂点的去除,边界的模糊等.
Imgproc.blur(frame, dest, new Size(2, 2));
模糊结果:
图像放大缩小
放大
Imgproc.pyrUp(matc, big, new Size(matc.width() * beishu, matc.height() * beishu));
缩小 默认一半
Imgproc.pyrDown(m_addr, m_addr);
图像切割
Rect rt = new Rect(new Point(startx, starty), new Point(endx, endy));
Mat m = new Mat(matcgray, rt);
图像腐蚀和膨胀
腐蚀说明:
图像的一部分区域与指定的核进行卷积,求核的最`小`值并赋值给指定区域。
腐蚀可以理解为图像中`高亮区域`的'领域缩小'。
意思是高亮部分会被不是高亮部分的像素侵蚀掉,使高亮部分越来越少。
// 腐蚀,去除离散点
Imgproc.erode(thresholdImg, thresholdImg, new Mat(), new Point(-1, -1), 2);
膨胀说明:
图像的一部分区域与指定的核进行卷积,求核的最`大`值并赋值给指定区域。
膨胀可以理解为图像中`高亮区域`的'领域扩大'。
意思是高亮部分会侵蚀不是高亮的部分,使高亮部分越来越多。
// 膨胀
Mat m_dilate = new Mat();
Mat structElement1 = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(4, 4), new Point(-1, -1));
Imgproc.dilate(nmat, m_dilate, structElement1);
// imshow("膨胀", out1);imwrite("膨胀.jpg", out1);
rode(nmat, m_erode, structElement1);
左侧原图,中间膨胀,右侧腐蚀.
图像简单合并
Core.add 复制合并.
Mat frame = new Mat(imgwidth * 2, imgheight * 2, this.image.type());
Rect roiArea = new Rect(0, 0, imgwidth, imgheight);
Rect roiArea2 = new Rect(0, imgheight, imgwidth, imgheight);
Rect roiArea3 = new Rect(imgwidth, 0, imgwidth, imgheight);
Rect roiArea4 = new Rect(imgwidth, imgheight, imgwidth, imgheight);
Mat src1Roi = new Mat(src1, roiArea);
Mat dstRoi = new Mat(frame, roiArea);
Core.add(src1Roi, dstRoi, dstRoi);
dstRoi = new Mat(frame, roiArea2);
Core.add(src1, dstRoi, dstRoi);
图像线段表格识别
Mat line = new Mat();
Mat matc = new Mat();
Imgproc.cvtColor(mask, matc, Imgproc.COLOR_GRAY2BGR);
Mat matcgray = new Mat();
matc.copyTo(matcgray, matc);
Imgproc.HoughLinesP(mask, line, 1, Math.PI / 180, 40, 35, 2.5);
RHO 累加器的距离分辨率,以像素为单位。
THETA 累加器的角度分辨率,以弧度表示。
阈 累加器阈值参数。只返回那些得到足够票数的行(> 阈值 )。
minLineLength 最小线长。短于此的线段被拒绝。
maxLineGap 在同一行上的点之间允许的最大间隙以链接它们。
图像人脸识别
使用faceCascade. 可以配置不同的检测配置。具体检测xml没有深入研究.
使用灰度图片检测,检测前对图像做直方图均衡化处理,提高检测效果.
// //函数功能:直方图均衡化,该函数能归一化图像亮度和增强对比度
Imgproc.equalizeHist(grayFrame, grayFrame);
// face
this.faceCascade = new CascadeClassifier();
this.absoluteFaceSize = 0;
this.faceCascade.load("resources/lbpcascades/lbpcascade_frontalface.xml");
// detect faces
this.faceCascade.detectMultiScale(grayFrame, faces, 1.1, 2, 0 | Objdetect.CASCADE_SCALE_IMAGE,
new Size(this.absoluteFaceSize, this.absoluteFaceSize), new Size());
具体脚本xxx.xml
<opencv_storage>
<cascade type_id="opencv-cascade-classifier">
<stageType>BOOST</stageType>
<featureType>LBP</featureType>
<height>24</height>
<width>24</width>
<stageParams>
<boostType>GAB</boostType>
<minHitRate>0.9950000047683716</minHitRate>
<maxFalseAlarm>0.5000000000000000</maxFalseAlarm>
<weightTrimRate>0.9500000000000000</weightTrimRate>
<maxDepth>1</maxDepth>
<maxWeakCount>100</maxWeakCount></stageParams>
<featureParams>
<maxCatCount>256</maxCatCount></featureParams>
<stageNum>20</stageNum>
<stages>
<!-- stage 0 -->
<_>
<maxWeakCount>3</maxWeakCount>
<stageThreshold>-0.7520892024040222</stageThreshold>
<weakClassifiers>
二.图像的二值化处理
图像hls-inrage 双值化调整(根据参数过滤提取图片)
使用Core.inRage方法。
将在两个阈值内的像素值设置为白色(255),而不在阈值区间内的像素值设置为黑色(0)
合理运用,可以结合其他内容实现对目前区域的提取与分割
下面根据界面设置的hls参数范围处理图片. 将图片转换为hls模式,根据图片亮度提取关键信息
Scalar minValues = new Scalar(this.hueStart.getValue(), this.saturationStart.getValue(),
this.valueStart.getValue());
Scalar maxValues = new Scalar(this.hueStop.getValue(), this.saturationStop.getValue(),
this.valueStop.getValue());
// show the current selected HSV range
String valuesToPrint = "Hue range: " + minValues.val[0] + "-" + maxValues.val[0] + "\tSaturation range: "
+ minValues.val[1] + "-" + maxValues.val[1] + "\tValue range: " + minValues.val[2] + "-"
+ maxValues.val[2];
Utils.onFXThread(this.hsvValuesProp, valuesToPrint);
// threshold HSV image to select tennis balls
Core.inRange(frame, minValues, maxValues, mask);
左边为锐化的图片,右边为按hls过滤后的,可以用于后面的身份证及驾照关键信息的提取.
图像二值化2
int thresh_type = Imgproc.THRESH_BINARY_INV;
//threshValue
//双值化图像
Imgproc.threshold(frame, thresholdImg, threshValue, to, thresh_type);
其实最后都可以自己手动实现,根据各个点的值去手动修改为0或者255即可.
只是封装好的方法会方便很多,有些时候还是自己实现来的实在一点~
比如这个,简单的二指化计算,有时会有奇效,指定范围黑色以下的保留,其他全部为白色,
Mat hist2 = new Mat(imgheight, imgwidth, CvType.CV_8UC1);
double[] v2 = new double[imgwidth * imgheight];
for (int j = 0; j < imgwidth * imgheight; j++) {
v2[j] = 255;
}
hist2.put(0, 0, v2);
double param=5;
double max = imgheight;// 0;
for (int j = 0; j < imgwidth; j++) {
for (int i = 0; i < imgheight; i++) {
try {
if (rectM.get(i, j)[0] <= 100+param && rectM.get(i, j)[1] <= 100+param && rectM.get(i, j)[2] <= 100+param) {
hist2.put(i, j, 0);
} else {
// int k = 0;
}
} catch (Exception e) {
// TODO: handle exception
}
}
}
三.图像的轮廓计算
寻找图像轮廓
//dilated_edges 二值化处理后的图像,比如candy/threshold等
List<MatOfPoint> contours = new ArrayList<>();//每一组Point点集就是一个轮廓。
//向量内每个元素保存了一个包含4个int整型的数,hierarchy向量内每一个元素的4个int型变量——hierarchy[i][0] ~hierarchy[i][3],分别表示第
//i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,没有值则为-1
Mat hierarchy = new Mat();
//RETR_EXTERNAL:表示只检测最外层轮廓,对所有轮廓设置hierarchy[i][2]=hierarchy[i][3]=-1
//RETR_LIST:提取所有轮廓,并放置在list中,检测的轮廓不建立等级关系
//RETR_CCOMP:提取所有轮廓,并将轮廓组织成双层结构(two-level hierarchy),顶层为连通域的外围边界,次层位内层边界
//RETR_TREE:提取所有轮廓并重新建立网状轮廓结构
//RETR_FLOODFILL:官网没有介绍,应该是洪水填充法
//CHAIN_APPROX_NONE:获取每个轮廓的每个像素,相邻的两个点的像素位置差不超过1
//CHAIN_APPROX_SIMPLE:压缩水平方向,垂直方向,对角线方向的元素,值保留该方向的重点坐标,如果一个矩形轮廓只需4个点来保存轮廓信息
//CHAIN_APPROX_TC89_L1和CHAIN_APPROX_TC89_KCOS使用Teh-Chinl链逼近算法中的一种
//寻找边界轮廓
Imgproc.findContours(dilated_edges, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);
轮廓长度计算
// Convert contours from MatOfPoint to MatOfPoint2f
MatOfPoint2f contour2f = new MatOfPoint2f(contours.get(i).toArray());
// Processing on mMOP2f1 which is in type MatOfPoint2f
//计算轮廓的长度
double approxDistance = Imgproc.arcLength(contour2f, true)
图像线段简化
//连续轮廓折线化
Imgproc.approxPolyDP(contour2f, approxCurve, approxDistance, true);
绘制图像轮廓
//绘制边界轮廓
Imgproc.drawContours(o, contours, i, new Scalar(255, 0, 0, .8), 6);
本文基于CC BY-NC-ND 4.0 许可协议发布,作者:野生的喵喵。 固定链接: 【opencv-java 基础API使用总结】 转载请注明
相关文章: