JAVA-基于SVM+HOG的对象检测

这是我的毕设,因为在实现过程中发现网上关于JAVA实现的代码较少所以想在这里分享一下经验。

实现所采用的技术主要是opencv中的svm,hog,语言采用的是JAVA(实现的时候发现选的不好。。),一开始的时候不清楚数据集的影响,就自己做了杯子的数据集,但效果不好。后来使用的是INRIA和MIT的行人数据集,FDDB的人脸数据集(我这里是对象检测,所以测试了不同对象)。

下面贴代码:

1):训练

public class Svm_train {
    /**
     * SVM训练
     */
    public void svm_train(){
        Integer ITERATION_NUM = 3000;
        String traintxt = "E:\\biyesheji\\image\\traindata.txt";//行人
        //String traintxt = "E:\\biyesheji\\image\\traindata2.txt";//人脸
        ArrayList<String> img_path = new ArrayList<String>();
        ArrayList<Float> img_label = new ArrayList<Float>();
        //System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        System.load("E:\\biyesheji\\opencv\\build\\java\\x64\\opencv_java330.dll");
        System.out.println("类库加载成功·");
        
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File(traintxt)),
                    "UTF-8"));
            //System.out.println("读取文件");
            String linetxt = null;
            Integer nline = 0;
            while((linetxt = br.readLine()) !=null){
                nline++;
                String []path_label = linetxt.split("\t");
                //System.out.println(path_label[0] + ' ' + path_label[1]);
                img_path.add(path_label[0]);
                img_label.add(Float.valueOf(path_label[1]).floatValue());
            }
            br.close();
            //一个block内有4个cell,每个cell含9维特征向量,故每个block就由4x9=36维特征向量来表征
            //128x64(高128,宽64),即有(128÷8)x(64÷8)=16x8个cell,也即有15x7个block
            //在提取每个窗口的HOG特征,则可得到105x36=3780维HOG特征向量
            Integer SAMPLE_COUNT = nline; //样本数
            Integer PICTURE_FEATURE_DIM = 3780;//图片特征维数,64*64(1764),64*128(3780),128*128(8100)
            //Integer PICTURE_FEATURE_DIM = 1764;//人脸
            Mat data_mat = new Mat(SAMPLE_COUNT, PICTURE_FEATURE_DIM, CvType.CV_32FC1);//行,列,类型
            Mat res_mat = new Mat(SAMPLE_COUNT, 1, CvType.CV_32SC1);

            //svm descriptors
            ArrayList<float[]> descriptors = new ArrayList<float[]>();
            for (Integer i=0;i<img_path.size();i++){
                System.out.println("GetHog:"+img_path.get(i));
                Mat src = Imgcodecs.imread(img_path.get(i));
                
                if(src.empty()){
                    System.out.println(img_path.get(i));
                    throw new Exception("no such picture");
                }
                if(img_label.get(i)==1)
                {
                    if(src.cols()>64 || src.rows()>128)//行人128
                    {
                        //去掉上下左右16个像素
                        Rect Roi=new Rect(new Point(16,16),new Size(64,128));//行人128
                        Mat image= src.submat(Roi);//子图
                        HOGDescriptor hog = new HOGDescriptor(new Size(64, 128), new Size(16, 16), new Size(8, 8), new Size(8, 8), 9);//行人128
                        MatOfFloat descriptorsOfMat = new MatOfFloat();
                        hog.compute(image, descriptorsOfMat);//调用计算函数
                        float[] descriptor = descriptorsOfMat.toArray();//一列
                        descriptors.add(descriptor);
                    }else{
                        HOGDescriptor hog = new HOGDescriptor(new Size(64, 128), new Size(16, 16), new Size(8, 8), new Size(8, 8), 9);//行人
                        //HOGDescriptor hog = new HOGDescriptor(new Size(64, 64), new Size(16, 16), new Size(8, 8), new Size(8, 8), 9);//人脸
                        MatOfFloat descriptorsOfMat = new MatOfFloat();
                        hog.compute(src, descriptorsOfMat);//调用计算函数
                        float[] descriptor = descriptorsOfMat.toArray();//一列
                        descriptors.add(descriptor);
                    }
                    
               }else if(img_label.get(i)==0){
                    //Hog特征,窗口大小(宽,高),块大小,块滑动增量,胞元大小,梯度方向数
                    HOGDescriptor hog = new HOGDescriptor(new Size(64, 128), new Size(16, 16), new Size(8, 8), new Size(8, 8), 9);//行人128
                    MatOfFloat descriptorsOfMat = new MatOfFloat();
                    hog.compute(src, descriptorsOfMat);//调用计算函数
                    float[] descriptor = descriptorsOfMat.toArray();//一列
                    descriptors.add(descriptor);
                }
            }

            for (Integer m = 0; m < descriptors.size(); m++) {
                for (int n = 0; n < descriptors.get(m).length; n++) {
                    data_mat.put(m, n, descriptors.get(m)[n]);//按行存储
                }
                res_mat.put(m, 0, img_label.get(m));//一列向量
            }
            
            System.out.println("开始训练。。。。。");
            SVM svm = SVM.create();
            svm.setType(SVM.C_SVC);
            svm.setKernel(SVM.LINEAR);//线性,HogDescriptor检测函数只支持线性检测
            svm.setC(0.01);
            svm.setGamma(0.5);
            svm.setTermCriteria(new TermCriteria(TermCriteria.MAX_ITER, ITERATION_NUM, 1e-6));
            svm.train(data_mat, Ml.ROW_SAMPLE, res_mat);

            svm.save("E:\\biyesheji\\image\\svm_java");//行人
            //svm.save("E:\\biyesheji\\image\\svm_java2");//人脸
            
            System.out.println("保存模型。。");
        }catch (Exception e){
            System.err.println("read err:" + e);
        }
    }
    
    
    //自定义检测器
    public Mat myDetector()
    {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        
        //SVM svm = SVM.load("E:\\biyesheji\\image\\svm_java2");//人脸
        SVM svm = SVM.load("E:\\biyesheji\\image\\svm_java");//行人
        //获取支持向量
        Mat svecsmat = svm.getSupportVectors();
        int numofsv = svecsmat.rows();//支持向量个数
        System.out.println("支持向量个数:"+numofsv);
        int svdim = svm.getVarCount();//特征向量维数,即HOG描述子的维数
        System.out.println("特征向量维数:"+svdim);
        
        //初始化alphamat和svindex
        Mat alphaMat = Mat.zeros(1, numofsv, CvType.CV_32F);
        Mat supportVectorMat = Mat.zeros(numofsv, svdim, CvType.CV_32FC1);
        Mat resultMat = Mat.zeros(1, svdim, CvType.CV_32FC1);
        Mat svidx = Mat.zeros(1, numofsv, CvType.CV_32F);
        //获得模型中的rho
        double rho = svm.getDecisionFunction(0, alphaMat, svidx);
        System.out.println("rho:"+rho);
        alphaMat.convertTo(alphaMat, CvType.CV_32F);
        //System.out.println(alphaMat.rows()+","+alphaMat.cols());
        //将支持向量和alpha复制到对应Mat中
        supportVectorMat = svecsmat;
        //alpha*src1*src2 + beta*src3,-1 * alphamat * supportVectorMat,点乘
        Core.gemm(alphaMat, supportVectorMat, -1, new Mat(), 0, resultMat);
        
        //定义一个大一维的向量,便于后面添加rho
        Mat myDetector = new Mat(1, svdim+1, CvType.CV_32FC1);
        for(int j=0;j<svdim;j++)
        {
            double[] value2 = resultMat.get(0, j);
            myDetector.put(0, j, value2[0]);
        }
        //添加rho
        myDetector.put(0, svdim, rho);
        System.out.println("rho:"+myDetector.get(0, svdim)[0]);
        
        return myDetector;
        //开始检测
        
    }    
}

 

2):预测

public class Svm_predict {
    public void svm_predict() {
        //The winSize should match the dimensions of your training images. 
        //In my case I used 32x64 images (for training) and so I needed to use a winSize=(32x64). 
        Mat myDetector = new Svm_train().myDetector();
        HOGDescriptor hog = new HOGDescriptor(new Size(64, 128), new Size(16, 16), new Size(8, 8), new Size(8, 8), 9);//行人128
        hog.setSVMDetector(myDetector);
        
        //String testtxt = "E:\\biyesheji\\image\\traindata_cc2.txt";//人脸
        String testtxt = "E:\\biyesheji\\image\\traindata_cc.txt";//行人
        ArrayList<String> img_path = new ArrayList<String>();
    
        
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(
                    new FileInputStream(new File(testtxt)), "UTF-8"));
            String linetxt = null;
            Integer nline = 0;
            while ((linetxt = br.readLine()) != null) {
                nline++;
                String[] path_label = linetxt.split("\t");
                System.out.println(path_label[0] + ' ');
                img_path.add(path_label[0]);
            }
            br.close();

            for (Integer i = 0; i < img_path.size(); i++) {
                Mat src_test = Imgcodecs.imread(img_path.get(i));// 读取一张图片
                if (src_test.empty()) {
                    throw new Exception("no such picture");
                }
                
                MatOfRect mor = new MatOfRect(); // 检测完毕后会储存在这里  
                MatOfDouble mod = new MatOfDouble();
               // System.out.println("正在检测...");
                /* 参数解读: 
                 * Mat img 待检测的图像,Mat类型,
                 * 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)
                 * 常见的pad size 有(8, 8), (16, 16), (24, 24), (32, 32).
                 * double scale 比例,1.05,通常scale在1.01-1.5这个区间
                 * double finalThreshold 最终阈值,2 
                 * boolean useMeanshiftGrouping 使用均值移位分组,false的效果比true的好 
                 * winStride和scale都是比较重要的参数,需要合理的设置。一个合适参数能够大大提升检测精确度,同时也不会使检测时间太长。
                 */
                //hog.detectMultiScale(src_test, mor, mod);
                hog.detectMultiScale(src_test, mor, mod, 0, new Size(4, 4), new Size(8, 8), 1.05, 2, false); // 调用方法进行检测  
                //System.out.println("检测完毕!画出矩形...");
                if(mor.toArray().length > 0){ //判断是否检测到目标对象,如果有就画矩形,没有就执行下一步  
                    //找出所有没有嵌套的矩形框r,并放入found_filtered中,如果有嵌套的话,则取外面最大的那个矩形框放入found_filtered中  
                    Rect[] found = mor.toArray();
                    List<Rect> found_filtered = new ArrayList<Rect>();
                    //先判断是否有嵌套
                    for(int m=0;m<found.length;m++)
                    {
                        Rect r = found[m];
                        int area = r.width*r.height;
                        //System.out.println(r.x+","+r.y+";"+r.width+","+r.height);
                        int n=0;
                        for(;n<found.length;n++)
                        {
                            if(n!=m && getOverLappingArea(r, found[n])==area)//且found[n]在r内
                                break;
                        }
                        if(n==found.length)
                        {
                            found_filtered.add(r);
                        }
                    }
                    
                    for(int j=0;j<found_filtered.size();j++)
                    {
                        Rect r = found_filtered.get(j);
                        Imgproc.rectangle(src_test, new Point(r.x, r.y), new Point(r.x + r.width, r.y + r.height),new Scalar(0, 0, 255), 2);
                    }
             
                    System.out.println("矩形绘制完毕!正在输出...");
                }else{
                    System.out.println("未检测到目标!绘制矩形失败!输出原文件!");
                }
                    // 获取图片名
                    String fname = img_path.get(i).trim();
                    String fileName = fname
                            .substring(fname.lastIndexOf("\\") + 1);
                    String filename = "E:\\biyesheji\\image\\detectImage\\"
                            + fileName;//行人
                    //String filename = "E:\\biyesheji\\image\\facedetectImage\\"
                            //+ fileName;//人脸
                    
                    System.out.println(String.format("Writing %s", filename));
                    Imgcodecs.imwrite(filename, src_test);
            }

        } catch (Exception e) {
            System.err.println("read err:" + e);
        }
    }
    /**
     * 判断两个矩形的重叠面积
     * @param a
     * @param b
     * @return
     */
    public  int getOverLappingArea(Rect a,Rect b)  
    {  
        int overLappingArea = 0;  
          
        int startX = Math.min(a.x,b.x);  
        int endX = Math.max(a.x + a.width, b.x + b.width);  
        int overLappingWidth = a.width + b.width - (endX - startX);  
          
        int startY = Math.min(a.y, b.y);  
        int endY = Math.max(a.y + a.height, b.y + b.height);  
        int overLappingHeight = a.height + b.height - (endY - startY);  
          
        if(overLappingWidth <= 0 || overLappingHeight <= 0)  
        {  
            overLappingArea = 0;  
        }  
        else  
        {  
            overLappingArea = overLappingWidth * overLappingHeight;  
        }  
        return overLappingArea;  
          
    }  

上述为两个主要过程,还有很多小过程没有细说,像数据集的处理使用了PS,图片的存储和读取以及切割我是借助了C++的算法来操作的,成功实现了之后才发现其实也没什么太难的地方,只不过是一开始不了解罢了。

下面贴上几张效果图:

1)行人检测效果

 

2)人脸检测效果图

 

当然既然是机器学习而且我的技术也不是那么厉害才刚开始入门,所以还没有达到百分百的准确率,我这里的准确率是使用下面公式进行计算的,准确率大概都在70%左右。

总结一下,嗯....这是我第一次在CSDN上面发表自己的博客,需要学习的地方还有很多,希望以后能发表更好的博客,(#^.^#)。

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
OpenCV(Open Source Computer Vision Library)是一款开源的计算机视觉库,专门为图像和视频处理任务设计,广泛应用于学术研究、工业应用以及个人项目中。以下是关于OpenCV的详细介绍: 历史与发展 起源:OpenCV于1999年由英特尔公司发起,旨在促进计算机视觉技术的普及和商业化应用。该项目旨在创建一个易于使用、高效且跨平台的库,为开发者提供实现计算机视觉算法所需的基础工具。 社区与支持:随着时间的推移,OpenCV吸引了全球众多开发者和研究人员的参与,形成了活跃的社区。目前,OpenCV由非盈利组织OpenCV.org维护,并得到了全球开发者、研究机构以及企业的持续贡献和支持。 主要特点 跨平台:OpenCV支持多种操作系统,包括但不限于Windows、Linux、macOS、Android和iOS,确保代码能够在不同平台上无缝运行。 丰富的功能:库中包含了数千个优化过的函数,涵盖了计算机视觉领域的诸多方面,如图像处理(滤波、形态学操作、色彩空间转换等)、特征检测与描述(如SIFT、SURF、ORB等)、物体识别与检测(如Haar级联分类器、HOG、DNN等)、视频分析、相机校正、立体视觉、机器学习(SVM、KNN、决策树等)、深度学习(基于TensorFlow、PyTorch后端的模型加载与部署)等。 高效性能:OpenCV代码经过高度优化,能够利用多核CPU、GPU以及特定硬件加速(如Intel IPP、OpenCL等),实现高速图像处理和实时计算机视觉应用。 多语言支持:尽管OpenCV主要使用C++编写,但它提供了丰富的API绑定,支持包括C、Python、Java、MATLAB、JavaScript等多种编程语言,方便不同领域的开发者使用。 开源与免费:OpenCV遵循BSD开源许可证发布,用户可以免费下载、使用、修改和分发库及其源代码,无需担心版权问题。 架构与核心模块 OpenCV的架构围绕核心模块构建,这些模块提供了不同层次的功能: Core:包含基本的数据结构(如cv::Mat用于图像存储和操作)、基本的图像和矩阵操作、数学函数、文件I/O等底层功能。 ImgProc:提供图像预处理、滤波、几何变换、形态学操作、直方图计算、轮廓发现与分析等图像处理功能。 HighGui:提供图形用户界面(GUI)支持,如图像和视频的显示、用户交互(如鼠标事件处理)以及简单的窗口管理。 VideoIO:负责视频的读写操作,支持多种视频格式和捕获设备。 Objdetect:包含预训练的对象检测模型(如Haar级联分类器用于人脸检测)。 Features2D:提供特征点检测(如SIFT、ORB)与描述符计算、特征匹配与对应关系估计等功能。 Calib3d:用于相机标定、立体视觉、多视图几何等问题。 ML:包含传统机器学习算法,如支持向量机(SVM)、K近邻(KNN)、决策树等。 DNN:深度神经网络模块,支持导入和运行预训练的深度学习模型,如卷积神经网络(CNN)。
OpenCV(Open Source Computer Vision Library)是一款开源的计算机视觉库,专门为图像和视频处理任务设计,广泛应用于学术研究、工业应用以及个人项目中。以下是关于OpenCV的详细介绍: 历史与发展 起源:OpenCV于1999年由英特尔公司发起,旨在促进计算机视觉技术的普及和商业化应用。该项目旨在创建一个易于使用、高效且跨平台的库,为开发者提供实现计算机视觉算法所需的基础工具。 社区与支持:随着时间的推移,OpenCV吸引了全球众多开发者和研究人员的参与,形成了活跃的社区。目前,OpenCV由非盈利组织OpenCV.org维护,并得到了全球开发者、研究机构以及企业的持续贡献和支持。 主要特点 跨平台:OpenCV支持多种操作系统,包括但不限于Windows、Linux、macOS、Android和iOS,确保代码能够在不同平台上无缝运行。 丰富的功能:库中包含了数千个优化过的函数,涵盖了计算机视觉领域的诸多方面,如图像处理(滤波、形态学操作、色彩空间转换等)、特征检测与描述(如SIFT、SURF、ORB等)、物体识别与检测(如Haar级联分类器、HOG、DNN等)、视频分析、相机校正、立体视觉、机器学习(SVM、KNN、决策树等)、深度学习(基于TensorFlow、PyTorch后端的模型加载与部署)等。 高效性能:OpenCV代码经过高度优化,能够利用多核CPU、GPU以及特定硬件加速(如Intel IPP、OpenCL等),实现高速图像处理和实时计算机视觉应用。 多语言支持:尽管OpenCV主要使用C++编写,但它提供了丰富的API绑定,支持包括C、Python、Java、MATLAB、JavaScript等多种编程语言,方便不同领域的开发者使用。 开源与免费:OpenCV遵循BSD开源许可证发布,用户可以免费下载、使用、修改和分发库及其源代码,无需担心版权问题。 架构与核心模块 OpenCV的架构围绕核心模块构建,这些模块提供了不同层次的功能: Core:包含基本的数据结构(如cv::Mat用于图像存储和操作)、基本的图像和矩阵操作、数学函数、文件I/O等底层功能。 ImgProc:提供图像预处理、滤波、几何变换、形态学操作、直方图计算、轮廓发现与分析等图像处理功能。 HighGui:提供图形用户界面(GUI)支持,如图像和视频的显示、用户交互(如鼠标事件处理)以及简单的窗口管理。 VideoIO:负责视频的读写操作,支持多种视频格式和捕获设备。 Objdetect:包含预训练的对象检测模型(如Haar级联分类器用于人脸检测)。 Features2D:提供特征点检测(如SIFT、ORB)与描述符计算、特征匹配与对应关系估计等功能。 Calib3d:用于相机标定、立体视觉、多视图几何等问题。 ML:包含传统机器学习算法,如支持向量机(SVM)、K近邻(KNN)、决策树等。 DNN:深度神经网络模块,支持导入和运行预训练的深度学习模型,如卷积神经网络(CNN)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值