Java+OpenCV目标检测

Java+OpenCV目标检测

简介:大学生一枚,加入了学校项目组学习,参与到项目中,最近项目需要,通过各方面的学习,做了一个小小的物体识别的cascade.xml,把自己学到的干货分享出来。

环境:jdk1.8 opencv 3.4.0

首先,我们要有足量的图像,我相信这个各位是有办法收集到自己想要检测的目标的图像的。实际训练过程中,我发现500左右已经可以大体上识别到自己想要的目标,但是如果想要更高的精度,建议在1000+的正面例子,反面例子的话只要里面没有目标图像,可以是任何图像,但是建议根据实际需要,在所处的场景中截取不含正面例子的图像作为反面例子。据了解,正:反最佳比例是1:3左右哦。

下面开始上代码了,我把需要的东西都整合了,只需要图像文件地址就可以把所需的文件生成进行训练的操作。

import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

public class Project{
    
    //下面都是文件src,根据自己的实际情况填写
    
    public static String posFilePath = "";//正面例子源文件夹
    public static String posOutPath = "";//正面例子输出文件夹
    public static String posTxtPath = "";//正面例子信息txt文件
    public static String negFilePath = "";//反面例子源文件夹
    public static String NegOutPath = "";//反面例子输出文件夹
    public static String negTxtPath = "";//反面例子信息txt文件

    //修改文件大小为20*20  可自定义设置,根据opencv官方推荐20*20效果最佳,
    //个人建议最大不要超过40*40,太大的话跑不太动
    public static int width = 20 ;
    public static int height = 20 ;

    //导入opencv库
    static {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    }

    public static void renameFiles(String filePath){
        File file = new File(filePath);
        File[] files = file.listFiles();
        int i = 0;
        for (File f : files){
            f.renameTo(new File(filePath+"\\"+i+".jpg"));
            i++;
        }
    }

    //获取img和txt的过程
    /*
    * 1、对图片缩放至20*20
    * 2、对图片进行灰度处理
    * 3、获取Txt
    * */
    public static void getPosImgAndTxt() throws IOException {
        File infile = new File(posFilePath);//获取正面例子的文件夹
        File outfile = new File(posOutPath);//缩放和灰度处理后的例子存放文件夹
        File txtFile = new File(posTxtPath);//txt信息文件的文件位置

        if (txtFile.isDirectory()){
            System.out.println("txt文件别忘了在文件夹地址后加上文件名");
            return;
        }
        if (!txtFile.exists()){
            txtFile.createNewFile();
            System.out.println("文件不存在,自动创建完毕");
        }
        if (!outfile.exists()){
            outfile.mkdir();
            System.out.println("输出路径不存在,自动创建完毕");
        }
        if (!infile.isDirectory()){
            System.out.println("请确保参数posFilePath为文件夹路径");
        }else if (infile.isDirectory()){
            File[] files = infile.listFiles();
            FileOutputStream fileOutputStream = new FileOutputStream(txtFile);
            PrintWriter printWriter = new PrintWriter(fileOutputStream,true);

            for (File file :files){
                Mat mat = Imgcodecs.imread(file.getPath());

                //缩放
                Imgproc.resize(mat,mat,new Size(width,height),0,0,Imgproc.INTER_CUBIC);

                //灰度处理
                Imgproc.cvtColor(mat,mat,Imgproc.COLOR_BGR2GRAY,0);

                //获取图片
                Imgcodecs.imwrite(outfile+"\\"+file.getName()+".jpg",mat);

                //添加文件信息到txt文件中
                printWriter.println(outfile.getPath()+"\\"+file.getName()+" 1 0 0 20 20");
            }
            printWriter.close();
            fileOutputStream.close();
        }
    }
    //获取反面img和txt的过程
    /*
    *1、对图片进行灰度处理
    *2、获取txt
    * */
    public static void getNegImgAndTxt() throws IOException {
        File infile = new File(negFilePath);//获取反面例子的文件夹
        File outfile = new File(NegOutPath);//灰度处理后的例子存放文件夹
        File txtFile = new File(negTxtPath);//txt信息文件的文件位置
        if (txtFile.isDirectory()){
            System.out.println("txt文件别忘了在文件夹地址后加上文件名");
            return;
        }
        if (!txtFile.exists()){
            txtFile.createNewFile();
            System.out.println("文件不存在,自动创建完毕");
        }
        if (!outfile.exists()){
            outfile.mkdir();
            System.out.println("输出路径不存在,自动创建完毕");
        }
        if (!infile.isDirectory()){
            System.out.println("请确保参数posFilePath为文件夹路径");
        }else if (infile.isDirectory()){
            File[] files = infile.listFiles();
            FileOutputStream fileOutputStream = new FileOutputStream(txtFile);
            PrintWriter printWriter = new PrintWriter(fileOutputStream,true);

            for (File file :files){
                Mat mat = Imgcodecs.imread(file.getPath());

                //灰度处理
                Imgproc.cvtColor(mat,mat,Imgproc.COLOR_BGR2GRAY,0);

                //获取图片
                Imgcodecs.imwrite(outfile+"\\"+file.getName()+".jpg",mat);

                //添加文件信息到txt文件中
                printWriter.println(outfile.getPath()+"\\"+file.getName());
            }
            printWriter.close();
            fileOutputStream.close();
        }
    }

    public static void main(String[] args) throws IOException {
        
        /*
        * 如果有需要,可以调用重命名函数,因为图像文件名中是
        * 不可以有汉字的我的例子图像好多都是通过qq截图截的,其中含有“截图”这两个字,
        * 所以被迫写一个重命名的函数,我把图像的名字都改成了数字
        *  重命名函数已经写好,可以直接调用
        * renameFiles(参数);
        * 参数路径中不要有不是作为目标检测素材的其他文件,因为如果有非图像文件
        * 需要的参数是文件夹路径,会将整个文件夹中的文件重命名为  数字.jpg 的格式
        * 
        * */
        getPosImgAndTxt();
        getNegImgAndTxt();
    }
}

经过上面的操作,我们的文件夹应该是这个样子的(见下图)
Java+OpenCV目标检测_第1张图片
然后我们在这个文件夹里面创建一个名为xml的文件夹,用来保存训练所得的cascade.xml以及其他每一层的文件

然后我们要把opencv的一些文件复制过来进行应用路径为:
build/x64/vc14/bin,
内容如图,将其中的所有文件复制到我们需要训练模型的文件夹中。
Java+OpenCV目标检测_第2张图片
如果没有这些文件可以在我的百度云里面下载:
链接:https://pan.baidu.com/s/115KfcgUr5SL_BmWJ6-73Cg
提取码:loiq
复制过去后我们的训练模型用的文件夹是这个样子的
Java+OpenCV目标检测_第3张图片
之后,打开cmd进入到我们训练用文件夹输入:

opencv_createsamples.exe -vec pos.vec -info pos.txt -num 500 -w 20 -h 20
其中,-num 是指你的正面例子的个数, -w 是指我们的正面例子的宽度 ,-h 是指我们的正面例子的高度。

这段代码执行后,会生成文件pos.vec

然后在cmd中本文件夹目录下输入:

opencv_traincascade.exe -data xml -vec pos.vec -bg neg.txt -numPos 500 -numNeg 1500 -numStages 15 -w 20 -h 20 -minHitRate 0.999 -maxFalseAlarmRate 0.2 -weightTrimRate 0.95 -featureType LBP
其中-numStage是指训练层数,可以根据实际情况自己定,我自己跑的时候,由于跑到stage15~16附近就已经很慢了,所以我选择了15层,实际训练过程中发现样本数量越少跑起来越慢,在这段代码回车后会进行一段时间的模型训练在xml那个文件夹中生成一堆xml文件,用得到的是cascade.xml
上方参数详见官方帮助文档

这时候xml文件夹中是这样的:
Java+OpenCV目标检测_第4张图片
然后就是对我们的模型进行应用啦代码如下:

import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.objdetect.CascadeClassifier;

import java.io.File;

public class New {
    public static void main(String[] args) {
        System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
        CascadeClassifier carDetector = new CascadeClassifier("E:\\new2\\xml\\cascade.xml");//调取训练出来的模型

        String filepath="E:\\project\\outputcam1";//输入路径,
        String outPath="E:\\project\\success";//输出路径

        File file=new File(filepath);
        File outfile=new File(outPath);

        if(!outfile.exists()) {
            outfile.mkdir();
        }if(!file.isDirectory()){
            System.out.println("请输入正确的文件夹路径");
        }else if(file.isDirectory()){
            String[] fileList=file.list();
            for(String imgName:fileList) {
                Mat img = Imgcodecs.imread(filepath+"/"+imgName);
                MatOfRect detections = new MatOfRect();
                carDetector.detectMultiScale(img,detections);
                int i = 0 ;
                for (Rect rect :detections.toArray()){
                    //将识别到的目表物体框起来
                    Imgproc.rectangle(img,new Point(rect.x,rect.y),new Point(rect.x+rect.width,rect.y+rect.height),new Scalar(0,255,0));
                    //将框出来目标的图像输出
                    Imgcodecs.imwrite(outPath+"/"+imgName, img);
                    i ++;
                }

            }
        }
    }
}

这段代码处理过的图像是这样子的:
Java+OpenCV目标检测_第5张图片
就是这个样子的了,有什么疑问欢迎评论。希望我的经验可以帮到你,喜欢的话别忘了点赞哦!

你可能感兴趣的:(Java+OpenCV目标检测)