opencv调⽤yolov3模型进⾏深度学习⽬标检测(Java版)
由于项⽬其他模块多⽤Scala/Java编写,与python交互不易。
将原多数⽤python/C++编写的⽬标检测程序,改写为Java。
建⽴⽬录,配置初始参数
类属性
static String pro_dir = "E:/yolov3/"; // 根路径
static float confThreshold = 0.3f; // 置信度阈值
static float nmsThreshold = 0.4f; // iou阈值
static int inpWidth = 416; // 修改输⼊图⽚的宽⾼
static int inpHeight = 416;
static List<String> classes = new ArrayList<String>(); // 存放类别的列表
主函数
public static void main(String[] args) throws Exception{
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
// 配置权重图⽚路径类别⽂件
String modelConfiguration = pro_dir + "yolov3.cfg"; // 模型配置⽂件
String modelWeights = pro_dir + "yolov3.weights"; // 模型权重⽂件
String image_path = "E:/pictures_test/pictureName.jpg"; // 图⽚路径,名字
String classesFile = pro_dir + "coco.names"; // 模型可识别类别的标签⽂件
// 进⼊识别图⽚的⽅法
detect_image(image_path, modelWeights, modelConfiguration, classesFile);
waitKey(0);
}
识别图⽚(detect_image)
读取可识别类别的标签⽂件,将所有类别存⼊List列表classses
coco.names的格式可能不同,读取时注意。
// 检测图⽚
public static void detect_image(String image_path, String modelWeights, String modelConfiguration, String classesFile) throws Exception {
// 读取classesFile路径的⽂件
InputStream in = new FileInputStream(classesFile);
int iAvail = in.available(); // 适⽤于本地⼀次读取多个字节时,返回得到的字节数。
byte[] bytes = new byte[iAvail];
String allContent = new String(bytes); // ⽂件中的所有内容
String[] tempContent = im().split("\n"); // allContent去除⾸尾空格,再按换⾏符分割。
System.out.println(tempContent.length);
// 遍历tempContent,添加到保存类别名的列表classes⾥。
for(int i=0; i<tempContent.length; i++){
classes.add(tempContent[i]);
}
System.out.println(classes.size());
加载⽹络配置与训练权重的⽂件,构建⽹络
Net net = adNetFromDarknet(modelConfiguration, modelWeights);
net.setPreferableBackend(Dnn.DNN_BACKEND_OPENCV);
net.setPreferableTarget(Dnn.DNN_TARGET_OPENCL);
读⼊图⽚,配置宽⾼
Mat im = imread(image_path, Imgcodecs.IMREAD_COLOR); // 读⼊待检测的图⽚
// 当前是以窗体的形式查看检测后的图⽚,给窗体命名
final String kWinName = "Deep object detection in OpenCV";
namedWindow(kWinName, WINDOW_NORMAL);java调用python模型
// 将输⼊图⽚的宽⾼重新设置 (416, 416)
Mat frame = new Mat();
Size sz1 = new ls(), im.rows());
Mat resized = new Mat();
Size sz = new Size(inpWidth, inpHeight);
float scale = 1.0F / 255.0F; // 像素归⼀化
遍历得到Yolo输出层的名字
此处即原python代码
ln = [ln[i[0] - 1] for i UnconnectedOutLayers()
out:[[200, 267, 400]]
<(0):[200, 267, 400]
Yolo的输出层是未连接层的前⼀个元素,所以需要减⼀:i.get(a) - 1得到的也是索引值
<(199) = “yolo_82”
<(265) = “yolo_94”
<(399) = “yolo_106”
Java:
List<String> ln = LayerNames(); // 获得YOLO各层的名字
List<String> x = new ArrayList<String>();
List<List<Integer>> out = new ArrayList<List<Integer>>(); // 存放的是列表
List<Integer> temp = UnconnectedOutLayers().toList(); // 获得未连接的输出层的索引列表
out.add(temp);
// out中存放的是⼀个List ,get(0)得到的就是list i 索引列表
List<Integer> i = (0);
System.out.println(i.size()); // 3
for(int a=0; a<i.size(); a++){
String n = ln.(a)-1); // 输出层的名字
x.add(n); // 到所有的输出层
}
ln = x; // 给ln重新赋值
System.out.println(ln); // [yolo_82, yolo_94, yolo_106]
将图像转换为标准输⼊格式
⽤需要检测的原始图像im构造⼀个blob图像,对原图像进⾏像素归⼀化 1/255.0,缩放尺⼨(416,416),对应训练模型时的cfg⽂件交换了通道。
若原图像:BGR,且swapRB=true时,交换第⼀个通道和第三个通道的值,即变为 RGB
crop:调整⼤⼩后是否裁剪图像。
Mat blob = Dnn.blobFromImage(im, scale, sz, new Scalar(0), true, false);
进⼊检测⽅法
// 矩阵列表 [Mat[...], Mat[...], Mat[...]]
List<Mat> outs = new ArrayList<Mat>();
net.forward(outs, ln); // ln此时为输出层的名字列表,向前传播,将得到的检测结果传⼊outs System.out.println(outs);
// 进⼊检测识别⽅法
postprocess_(im, outs);
// 检测结束后显⽰图⽚
imshow(kWinName, im); // 显⽰图⽚
waitKey(300000);
} // detect_image ⽅法结束
检测识别⽅法(postprocess_)
1. 循环遍历outs中的每⼀个矩阵
2. 过滤置信度⼩于阈值的检测结果
3. 得到矩形框列表,类别序号列表,置信度列表,每⼀个都相互对应。
4. ⾮极⼤值抑制
5. 进⼊画框,添加⽂字标签⽅法
6. 截取每张图⽚画出的每个矩形框,存为⼀个List<Mat>/存⼊本地路径
public static void postprocess_(Mat im, List<Mat> outs) {
System.out.println("检测过程开始");
List<Rect> boxes = new ArrayList<Rect>(); // 矩形框列表
List<Integer> classIds = new ArrayList<Integer>(); // 类的序号列表
List<Float> confidences = new ArrayList<Float>(); // 置信度列表
/
/ HashMap<String, Object> predicts_dict = new HashMap<String, Object>();
// 循环List<Mat>
for (int i = 0; i < outs.size(); i++) {
Mat mat = (i);
// 循环每⼀个mat对象
// 按⾏循环
for (int j = 0; j < ws(); j++) {
int probaility_index = 5; // [x,y,h,w,c,class1,class2] 所以是标号5
int size = (int) (ls() * mat.channels());
float[] data = new float[size];
<(j, 0, data);
float confidence = -1;
int classId = -1;
// 按列循环
for (int k = 0; k < ls(); k++) {
// 相当于[5:] np.argmax(scores)
if (k >= probaility_index && confidence < data[k]) {
confidence = data[k]; // 最⼤值
classId = k - probaility_index; // 得到检测得的类别索引
}
}
// 过滤掉置信度较⼩的检测结果
if (confidence > 0.5) {
System.out.println("Result Object:" + j);
for (int k = 0; k < ls(); k++) {
System.out.println(" " + k + ":" + data[k]);
}
System.out.println("");
float x = data[0]; // centerX 矩形中⼼点的X坐标
float y = data[1]; // centerY 矩形中⼼点的Y坐标
float width = data[2]; // 矩形框的宽
float height = data[3]; //矩形框的⾼
float xLeftBottom = (x - width / 2) * im.cols(); // 矩形左下⾓点的X坐标
float yLeftBottom = (y - height / 2) * im.rows(); // 矩形左下⾓点的Y坐标
float yLeftBottom = (y - height / 2) * im.rows(); // 矩形左下⾓点的Y坐标
float xRightTop = (x + width / 2) * im.cols(); // 矩形右上⾓点的X坐标
float yRightTop = (y + height / 2) * im.rows(); // 矩形右上⾓点的Y坐标
// boxes列表填值 Rect对象,参数是两个点
boxes.add(new Rect(new Point(xLeftBottom, yLeftBottom), new Point(xRightTop, yRightTop))); confidences.add(confidence);
classIds.add(classId);
}
}
}
System.out.println(classIds);
System.out.println(confidences);
System.out.println(boxes.size());
System.out.println(boxes);
// ⾮极⼤值抑制
List<Integer> indices = new ArrayList<Integer>();
// 此处的⾮极⼤值抑制为⾃⼰重写的⽅法。
NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices);
List<Mat> cutImages = new ArrayList<Mat>();
Mat cut;
int a = 0;
// indices : 最终剩下的按置信度由⾼到低的矩形框序号
if (indices.size() > 0) {
for (int b = 0; b < indices.size(); b++) {
a = a + 1;
Rect box = ((b));
Point p1 = box.tl(); // 获得左上⾓点
Point p2 = box.br(); // 获得右下⾓点
int classId = (a - 1); // 得到类别序号
float confidence = (a - 1); // 得到置信度值
// 进⼊画框框⽅法
drawPred_(classId, confidence, im, p1, p2);
// 将每个矩形框裁剪
cut = im.submat((int)p1.y, (int)p2.y, (int)p1.x, (int)p2.x);
cutImages.add(cut);
}
}
System.out.println(cutImages);
// 将裁剪后的图⽚存⼊本地路径(可省略)
String outputFilePath = "E:/pictures_output";
for (int i=0; i<cutImages.size(); i++){
Mat mat = (i);
MatOfByte mob = new MatOfByte();
Imgcodecs.imencode(".jpg", mat, mob);
byte[] byteArray = Array();
ByteArrayInputStream in = new ByteArrayInputStream(byteArray);
try {
BufferedImage image = ad(in);
OutputStream bOut = new FileOutputStream(outputFilePath + "/" + i + ".jpg");
ImageIO.write(image, "jpg", bOut);
} catch (IOException e) {
e.printStackTrace();
}
}
}
⾮极⼤值抑制(NMSBoxes)
先创建⼀个静态类BBox
static class BBox{
public Rect getBox() {
return box;
}
public void setBox(Rect box) {
this.box = box;
}
public float getConfidence() {
return confidence;
}
public void setConfidence(float confidence) { fidence = confidence;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
Rect box = new Rect();
float confidence;
int index;
}
重写的⾮极⼤值抑制⽅法
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论