WHCSRL 技术网

使用Java和OpenCV调用YOLOv3完成实施目标检测

使用Java和OpenCV调用YOLOv3完成实施目标检测,识别到目标就将包含目标的图片保存下来,没有目标就不保存。详细代码以及步骤。

第一步:在IDEA中完成对OpenCV包的导入

        从官网上下载OpenCV:Releases - OpenCV。选择window版本下载,解压之后

把 opencvuild目录下的java文件整个复制到项目文件下,名字随意。

         File>Project Structure>Libraries ,选择+号,选择java,之后选择jar包位置。导入即可。

 

 这里我已经导入过了。最后再配置dll

Run>Edit Configurations ,选中你所要配置的java文件,填入以下内容:

-Djava.library.path=$PROJECT_DIR$opencvx64 (64位选x64,32位选x86)

 第二步:完成目标检测

首先下载darknet:GitHub - pjreddie/darknet: Convolutional Neural Networks

为了方便操作,这里只需要将darknet的路径以及保存的图片路径修改为自己的即可。下边直接上代码。当然你也可以训练自己的YOLOv3模型来完成相关的操作。

主程序

  1. import org.opencv.core.*;
  2. import org.opencv.core.Point;
  3. import org.opencv.dnn.Dnn;
  4. import org.opencv.dnn.Net;
  5. import org.opencv.imgcodecs.Imgcodecs;
  6. import org.opencv.videoio.VideoCapture;
  7. import javax.imageio.ImageIO;
  8. import java.awt.image.BufferedImage;
  9. import java.io.*;
  10. import java.util.ArrayList;
  11. import java.util.List;
  12. import static org.opencv.dnn.Dnn.NMSBoxes;
  13. import static org.opencv.highgui.HighGui.*;
  14. import static org.opencv.highgui.HighGui.waitKey;
  15. import static org.opencv.imgproc.Imgproc.*;
  16. import static org.opencv.imgproc.Imgproc.FONT_HERSHEY_SIMPLEX;
  17. public class VideoDet{
  18. final static String ROOTDIR = "E:\ZxxProject\darknet-master"; // 根路径
  19. final static float CONTHRES = 0.8f; // 置信度阈值
  20. final static float NMSTHRES = 0.8f; // iou阈值
  21. final static List<String> CLASSES = new ArrayList<>(); // 存放类别的列表集合(这里直接用的原模型,所以是80类)
  22. public static int count=0;//标记,用来间隔多少次来保存图片
  23. public static boolean judge=true;
  24. public static void main(String[] args) {
  25. System.loadLibrary(Core.NATIVE_LIBRARY_NAME);//必须要加,不然程序不可执行
  26. //配置 权重 图片路径 类别文件
  27. String modelConfiguration = ROOTDIR + "\cfg\yolov3.cfg"; // 模型配置文件
  28. String modelWeights = ROOTDIR + "\yolov3.weights"; // 模型权重文件
  29. String classesFile = ROOTDIR + "\data\coco.names"; // 模型可识别类别的标签文件
  30. // 进入识别图片的方法
  31. try {
  32. detect_image(modelWeights, modelConfiguration, classesFile);
  33. } catch (Exception e) {
  34. e.printStackTrace();
  35. }
  36. }

 启动摄像头

  1. public static void detect_image(String modelWeights, String modelConfiguration, String classesFile) throws Exception {
  2. // 使用字节输入流读取classesFile路径的文件(从硬盘读取数据到内存)
  3. InputStream inputStream = new FileInputStream(classesFile);
  4. int allByte = inputStream.available();
  5. byte[] bytes = new byte[allByte]; //通过调节allByte的值来完成每次读取多少字节,这里直接读完
  6. inputStream.read(bytes);
  7. String allContent = new String(bytes); // 文件中的所有内容
  8. String[] tempContent = allContent.trim().split(" "); // allContent去除首尾空格,再按换行符分割。
  9. // 遍历tempContent,添加到保存类别名的列表classes里。
  10. for(int i=0; i<tempContent.length; i++){
  11. CLASSES.add(tempContent[i]);
  12. }
  13. System.out.println(CLASSES.size());
  14. Net net = Dnn.readNetFromDarknet(modelConfiguration, modelWeights);
  15. //打开摄像头
  16. VideoCapture cameraCapture = new VideoCapture(0);
  17. namedWindow("实时检测");
  18. int frame_width = (int) cameraCapture.get(3);
  19. int frame_height = (int) cameraCapture.get(4);
  20. Mat frame = new Mat();
  21. Size sz1 = new Size(frame_width, frame_height);
  22. while (judge) {
  23. cameraCapture.read(frame);
  24. count=count+1;
  25. System.out.println("-----------第"+count+"轮----------------");
  26. Mat blobImg = Dnn.blobFromImage(frame, 1.0F / 255.0F, sz1);
  27. net.setInput(blobImg);
  28. // 获取网络输出层信息(所有输出层的名字),设定并前向传播
  29. List<String> ln = net.getLayerNames(); // 获得YOLO各层的名字
  30. List<String> x = new ArrayList<>();
  31. List<List<Integer>> out = new ArrayList<>();
  32. List<Integer> temp = net.getUnconnectedOutLayers().toList(); // 获得未连接的输出层的索引列表
  33. out.add(temp);
  34. // out中存放的是一个List ,get(0)得到的就是list i 索引列表
  35. List<Integer> i = out.get(0);
  36. System.out.println(i.size()); // 3
  37. for (int a = 0; a < i.size(); a++) {
  38. String n = ln.get(i.get(a) - 1); // 输出层的名字
  39. x.add(n); // 找到所有的输出层
  40. }
  41. ln = x; // 给ln重新赋值
  42. // 矩阵列表 [Mat[...], Mat[...], Mat[...]]
  43. List<Mat> outs = new ArrayList<Mat>();
  44. net.forward(outs, ln); // ln此时为输出层的名字列表,向前传播,将得到的检测结果传入outs
  45. // 检测识别
  46. detection(frame, outs);
  47. }
  48. }

 检测摄像头中的目标

  1. public static void detection(Mat frame, List<Mat> outs) {
  2. System.out.println("检测过程开始");
  3. List<Rect2d> boxes = new ArrayList<>(); // 矩形框列表
  4. List<Integer> classIds = new ArrayList<>(); // 类的序号列表
  5. List<Float> confidences = new ArrayList<>(); // 置信度列表
  6. List<Integer> indices = new ArrayList<>(); //数量标记
  7. MatOfRect2d newbox = new MatOfRect2d();
  8. MatOfFloat newconf = new MatOfFloat();
  9. MatOfInt ind = new MatOfInt();
  10. ind.fromList(indices);
  11. //TODO
  12. for (int i = 0; i < outs.size(); i++) {
  13. Mat mat = outs.get(i);
  14. // 循环每一个mat对象
  15. for (int j = 0; j < mat.rows(); j++) {
  16. int probaility_index = 5; // [x,y,h,w,c,class1,class2] 所以是标号5
  17. int size = (mat.cols() * mat.channels());
  18. float[] data = new float[size];
  19. mat.get(j, 0, data);
  20. float confidence = -1;//初始值设为-1,
  21. int classId = -1;
  22. // 按列循环
  23. for (int k = 0; k < mat.cols(); k++) {
  24. if (k >= probaility_index && confidence < data[k]) {
  25. confidence = data[k]; // 最大值付给confidence
  26. classId = k - probaility_index; // 得到检测的类别索引
  27. }
  28. }
  29. // 过滤掉置信度较小的检测结果
  30. if (confidence > 0.6) {
  31. System.out.println("Result Object:" + j);
  32. for (int k = 0; k < mat.cols(); k++) {
  33. if (data[k] > 0) {
  34. System.out.println(" " + k + ":" + data[k]);
  35. }
  36. }
  37. float x = data[0]; // centerX 矩形中心点的X坐标
  38. float y = data[1]; // centerY 矩形中心点的Y坐标
  39. float width = data[2]; // 矩形框的宽
  40. float height = data[3]; //矩形框的高
  41. float xLeftBottom = (x - width / 2) * frame.cols(); // 矩形左下角点的X坐标
  42. float yLeftBottom = (y - height / 2) * frame.rows(); // 矩形左下角点的Y坐标
  43. float xRightTop = (x + width / 2) * frame.cols(); // 矩形右上角点的X坐标
  44. float yRightTop = (y + height / 2) * frame.rows(); // 矩形右上角点的Y坐标
  45. // boxes主要包括左下角坐标与右上角坐标
  46. boxes.add(new Rect2d(new Point(xLeftBottom, yLeftBottom), new Point(xRightTop, yRightTop)));
  47. newbox.fromList(boxes);
  48. confidences.add(confidence);
  49. newconf.fromList(confidences);
  50. classIds.add(classId);
  51. }
  52. }
  53. }
  54. //使用OpenCV的非极大抑制
  55. NMSBoxes(newbox, newconf, CONTHRES, NMSTHRES, ind);
  56. //当摄像头中无出现目标的时候,newbox/newconf.cols()==0,有目标则是1。
  57. if (newbox.cols() == 0 && newconf.cols() == 0) {
  58. imshow("实时检测", frame);
  59. waitKey(-1);
  60. return;
  61. }
  62. List<Integer> indices1 = ind.toList();
  63. List<Rect2d> bboxs1 = newbox.toList();
  64. List<Float> conf1 = newconf.toList();
  65. List<Mat> cutImages = new ArrayList<>();
  66. int a = 0;
  67. if (indices1.size() > 0) {
  68. for (int b = 0; b < indices1.size(); b++) {
  69. a = a + 1;
  70. Rect2d box = bboxs1.get(indices1.get(b));
  71. Point p1 = box.tl(); // 获得左 上角点
  72. Point p2 = box.br(); // 获得右下角点
  73. int classId = classIds.get(a - 1); // 得到类别序号
  74. float confidence = conf1.get(a - 1); // 得到置信度值
  75. // 在原图上绘制目标边框
  76. drawPic(classId, confidence, frame, p1, p2);
  77. cutImages.add(frame);
  78. }
  79. }
  80. System.out.println("cutImages" + cutImages);
  81. // 将含有目标的图片存入本地路径,无目标不保存
  82. String outputFilePath = "E:\ZxxProject\darknet-master\imgRes";
  83. for (int i = 0; i < cutImages.size(); i++) {
  84. Mat mat = cutImages.get(i);
  85. //显示图片
  86. imshow("实时检测", mat); // 显示图片
  87. waitKey(-1);
  88. if (count %%%% 10 == 0) {
  89. MatOfByte mob = new MatOfByte();
  90. Imgcodecs.imencode(".jpg", mat, mob);
  91. byte[] byteArray = mob.toArray();
  92. ByteArrayInputStream in = new ByteArrayInputStream(byteArray);
  93. try {
  94. BufferedImage image = ImageIO.read(in);
  95. OutputStream bOut = new FileOutputStream(outputFilePath + "/" + count + ".jpg");
  96. ImageIO.write(image, "jpg", bOut);
  97. } catch (IOException e) {
  98. e.printStackTrace();
  99. }
  100. }
  101. }
  102. }

绘制边框程序

  1. public static void drawPic(int classId, float confidence, Mat im, Point p1, Point p2){
  2. String text;
  3. double x = p1.x; // p1 的 x 坐标
  4. double y = p1.y; // p1 的 y 坐标
  5. if(classId == 0){
  6. System.out.println("1");
  7. rectangle(im, p1, p2, new Scalar(0, 0, 255), 1);
  8. text = CLASSES.get(classId) + ":" + confidence;
  9. putText(im, text, new Point(x, y-5), FONT_HERSHEY_SIMPLEX, 0.3, new Scalar(0, 255, 0), 1);
  10. }else {
  11. System.out.println("2");
  12. rectangle(im, p1, p2, new Scalar(0, 255, 0), 1); // 画框
  13. text = String.format("%%%%s %%%%f", CLASSES.get(classId), confidence); // 标签内容
  14. System.out.println(text);
  15. putText(im, text, new Point(x, y-5), FONT_HERSHEY_SIMPLEX, 0.5, new Scalar(0, 0, 255), 1);
  16. }
  17. }
  18. }

第三步:结果

结果中可能出现的绘制的框过多的现象,代码还有许多优化的地方,可以在评论中留言。

参考文章

[1] opencv调用yolov3模型进行深度学习目标检测(Java版)_w112112_的博客-CSDN博客

[2] 使用python+opencv+yolov3实现实时目标检测_Lz~ryeom的博客-CSDN博客

推荐阅读