利用OpenCV实现HOG+SVM行人检测的Java代码

笔者最近在做关于行人检测方面的论文,难于一直实现不了行人检测的效果,参考了网上大部分代码都是关于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实现HOG+SVM行人检测的Java代码_第1张图片利用OpenCV实现HOG+SVM行人检测的Java代码_第2张图片

当然,还有比较不尽人意的效果图:

利用OpenCV实现HOG+SVM行人检测的Java代码_第3张图片

出现不好的效果有很多因素,图片的光照、遮挡之类的,还有对于分类器的训练,如果训练的样本比较少的话,也会导致检测效果很不好,笔者在这里使用的分类器是OpenCV的默认分类器(HOGDescriptor.getDefaultPeopleDetector())。

关于如何训练分类器,笔者对于OpenCV官网的CascadeClassifier训练步骤做了一篇翻译供大家参考,传送门:点这里传送

以上就是笔者利用HOG+SVM实现的行人检测,此外,笔者还利用CascadeClassifier这个类也实现了行人检测,达到的效果和参数为(8, 8)(16, 16)时的检测效果类似。详情请关注笔者的后续博文。

你可能感兴趣的:(计算机视觉)