笔者最近在做关于行人检测方面的论文,难于一直实现不了行人检测的效果,参考了网上大部分代码都是关于C++的,在此特把自己写的、已经测试可以使用的Java代码贴出来,供后来者作为参考。
笔者的OpenCV版本为3.4.0。关于OpenCV在Eclipse中怎么配置,网上有相关的参考文章,大体上就是:
1、下载OpenCV文件,找到“opencv/build/java/”文件夹下的jar包;
2、加到Eclipse中的User Library中;
3、在项目中导入对应的User Library中的jar包。
笔者的具体实现代码如下:
package test;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfDouble;
import org.opencv.core.MatOfRect;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.HOGDescriptor;
public class Test {
public void run(){
System.loadLibrary(Core.NATIVE_LIBRARY_NAME); // 加载OpenCV主类
HOGDescriptor hog = new HOGDescriptor(); // 构建HOG描述子
hog.setSVMDetector(HOGDescriptor.getDefaultPeopleDetector()); // 设置默认SVM分类器
String path = "F:/PeopleDetectionNeeds/INRIA Dataset/INRIAPerson/Test/pos/person_120.png"; // 要检测的图片的路径
Mat img = Imgcodecs.imread(path); // 读取图片
// Mat img = Imgcodecs.imread("./data/crowd_2.jpg"); // 读取图片
if(img.empty()){ // 判断构建的Mat是否为空,如果为空的话是会报错的,为代码强壮性在这里做一个判断
System.out.println("文件不存在!程序退出!");
System.exit(0);
}
MatOfRect mor = new MatOfRect(); // 检测完毕后会储存在这里
MatOfDouble mod = new MatOfDouble(); // 不清楚是什么,调用的方法参数里面有,就做个实例吧
System.out.println("正在检测...");
hog.detectMultiScale(img, mor, mod, 0, new Size(4, 4), new Size(8, 8), 1.05, 2, false); // 调用方法进行检测
System.out.println("检测完毕!画出矩形...");
if(mor.toArray().length > 0){ // 判断是否检测到目标对象,如果有就画矩形,没有就执行下一步
for(Rect r:mor.toArray()){ // 检测到的目标转成数组形式,方便遍历
r.x += Math.round(r.width*0.1);
r.width = (int) Math.round(r.width*0.8);
r.y += Math.round(r.height*0.045);
r.height = (int) Math.round(r.height*0.85);
Imgproc.rectangle(img, r.tl(), r.br(), new Scalar(0, 0, 255), 2); // 画出矩形
}
System.out.println("矩形绘制完毕!正在输出...");
}else{
System.out.println("未检测到目标!绘制矩形失败!输出原文件!");
}
Imgcodecs.imwrite("./data/TestResult.jpg", img); // 将已经完成检测的Mat对象写出,参数:输出路径,检测完毕的Mat对象。
System.out.println("输出完毕!");
}
public static void main(String[] args) {
Test t = new Test();
t.run();
}
}
其中,需要重点提到hog.detectMultiScale()方法,笔者对于该方法的理解如下:
// 源码如下
void detectMultiScale(Mat img, MatOfRect foundLocations, MatOfDouble foundWeights, double hitThreshold, Size winStride, Size padding, double scale, double finalThreshold, boolean useMeanshiftGrouping)
/* 参数解读:
* Mat img 待检测的图像,Mat类型,Java中有自带的Image类,但是两者之间的转换比较麻烦,所以还是直接使用Imgcodecs.imread(filePath)这个方法较为合适
* MatOfRect foundLocations 用于储存检测后的序列(或者叫做矩阵,笔者在此就叫它序列吧)
* MatOfDouble foundWeights 不清楚是什么东西,只是用到了,简单的做个实例之后程序就能跑,笔者没有做过多了解
* double hitThreshold 命中阈值,0
* Size winStride 检测步长,网上大多的步长参数为(8,8),笔者在此用的是(4,4),原因是(8,8)的步长对于INRIA数据集中的图片不能很好的检测,而(4,4)可以
* Size padding 这个参数笔者没有过多了解,笔者的理解为:block的大小。大多代码推荐(16,,16),笔者使用的是(8,8)
* double scale 比例,1.05
* double finalThreshold 最终阈值,2
* boolean useMeanshiftGrouping 使用均值移位分组,笔者测试:false的效果比true的好
*/
效果图:
当然,还有比较不尽人意的效果图:
出现不好的效果有很多因素,图片的光照、遮挡之类的,还有对于分类器的训练,如果训练的样本比较少的话,也会导致检测效果很不好,笔者在这里使用的分类器是OpenCV的默认分类器(HOGDescriptor.getDefaultPeopleDetector())。
关于如何训练分类器,笔者对于OpenCV官网的CascadeClassifier训练步骤做了一篇翻译供大家参考,传送门:点这里传送
以上就是笔者利用HOG+SVM实现的行人检测,此外,笔者还利用CascadeClassifier这个类也实现了行人检测,达到的效果和参数为(8, 8)(16, 16)时的检测效果类似。详情请关注笔者的后续博文。