Welcome to my GitHub
Github.com/zq2599/blog…
Content: all original article classification summary and supporting source code, involving Java, Docker, Kubernetes, DevOPS, etc.;
This paper gives an overview of
- In the article “3 Minutes: Fast Experience of Object Detection (YOLO4) JAVA Edition”, we experienced the powerful object recognition ability of YOLO4, as shown in the picture below, the dog, man and horse in the original picture are identified and marked:
-
If you are familiar with deep learning and YOLO, Darknet, etc., you may be wondering: Can Java do this?
-
Yes, today we will start from scratch and develop a SpringBoot application to achieve the above functions, which is called Yolo – Demo
-
For SpringBoot to recognize the object in the image, the key is how to use the trained neural network model, fortunately, OpenCV integrated DNN module can load and use YOLO4 model, we just need to find a way to use OpenCV
-
My approach here is to use the JavaCV library, because JavaCV itself encapsulates OpenCV, and ultimately you can use the YOLO4 model for reasoning, with dependencies as shown below:
The key technology
-
This article involves JavaCV, OpenCV, YOLO4, etc., from the figure above we can see that JavaCV has done these packages, including the final reasoning used in the model is YOLO4 official training in advance, we just know how to use JavaCV API can
-
YOVO4’s paper is here: arxiv.org/pdf/2004.10…
Version information
- Here is my development environment for your reference:
- Operating system: Ubuntu 16 (MacBook Pro also available, version 11.2.3, macOS Big Sur)
- Docker: 20.10.2 Community
- Java: 1.8.0 comes with _211
- Springboot: from 2.4.8
- Javacv: 1.5.6
- Opencv: 4.5.3
Practical steps
-
Before the formal start, the actual combat steps of the first clear, behind the step-by-step implementation can be;
-
In order to reduce the impact of environmental and software differences and make the operation and debugging of the program easier, the SpringBoot application will be made into a Docker image and then run in the Docker environment. Therefore, the whole practice can be simply divided into three steps: making the basic image, developing the SpringBoot application and making the application into a mirror, as shown below:
-
Based image made the first step in the process, has been in the making JavaCV applications rely on the basis of the Docker mirror (CentOS7 + + OpenCV4 JDK8) “article introduced in detail, we directly use mirror bolingcavalry/opencv4.5.3:0.0.1. The next section will focus on SpringBoot application development;
-
The SpringBoot application has a very simple function, as shown below:
- The whole development process involves the following steps: web page submission of photos, neural network initialization, file processing, image detection, processing detection results, standard recognition results on images, front-end display images, etc. The complete steps have been sorted out as follows:
- The content is very rich, the harvest will not be less, not to mention the previous has ensured that it can be successfully run, so, do not hesitate, let’s start!
Download the source code
- The full source code for this article can be downloaded at GitHub with the following address and link information (github.com/zq2599/blog…
The name of the | link | note |
---|---|---|
Project home page | Github.com/zq2599/blog… | The project’s home page on GitHub |
Git repository address (HTTPS) | Github.com/zq2599/blog… | The project source warehouse address, HTTPS protocol |
Git repository address (SSH) | [email protected]:zq2599/blog_demos.git | The project source warehouse address, SSH protocol |
- The Git project has multiple folders. The source code for this project is in the Javacv-tutorials folder, as shown in the red box below:
- Javacv-tutorials have many sub-projects, and today’s code is under the Yolo – Demo project:
Example Create a SpringBoot application
- Create maven project named yolo- Demo, first this is a standard SpringBoot project, second add javacv dependency library, pom. XML content as follows, focus on javacv, OpencV and other library dependencies and accurate version matching:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bolingcavalry</groupId>
<version>1.0 the SNAPSHOT</version>
<artifactId>yolo-demo</artifactId>
<packaging>jar</packaging>
<properties>
<java.version>1.8</java.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<maven-compiler-plugin.version>3.6.1 track</maven-compiler-plugin.version>
<springboot.version>From 2.4.8</springboot.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${springboot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<! FreeMarker template view dependency -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.6</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>opencv-platform-gpu</artifactId>
<version>4.5.3-1.5.6</version>
</dependency>
</dependencies>
<build>
<plugins>
<! -- If the parent project is not Springboot, you need to use the plugin in the following way to generate a normal JAR -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>com.bolingcavalry.yolodemo.YoloDemoApplication</mainClass>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Copy the code
- In addition to the common Spring configuration, there are several file paths that will be used to store the corresponding files in the application. How to obtain these files will be discussed later:
# # # FreeMarker configuration
spring.freemarker.allow-request-override=false
#Enable template caching.
spring.freemarker.cache=false
spring.freemarker.check-template-location=true
spring.freemarker.charset=UTF-8
spring.freemarker.content-type=text/html
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=false
# Set the panel suffix
spring.freemarker.suffix=.ftl
# set the maximum memory for a single file
spring.servlet.multipart.max-file-size=100MB
# set maximum memory for all files
spring.servlet.multipart.max-request-size=1000MB
# Customize the file upload path
web.upload-path=/app/images
# Model path
The location of the # yOLO profile
opencv.yolo-cfg-path=/app/model/yolov4.cfg
# yOLO model file location
opencv.yolo-weights-path=/app/model/yolov4.weights
The location of the # yOLO classification file
opencv.yolo-coconames-path=/app/model/coco.names
# yOLO Image width for model inference
opencv.yolo-width=608
# YOLO model reasoning when the picture height
opencv.yolo-height=608
Copy the code
- Start the yolodemoApplication.java class:
package com.bolingcavalry.yolodemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class YoloDemoApplication {
public static void main(String[] args) { SpringApplication.run(YoloDemoApplication.class, args); }}Copy the code
- Now that the project is built, start coding, starting with the front page
The front page
-
As long as it involves the front end, Xichen will generally issue a self-protection statement: Please forgive xichen’s poor front end level, I can not bear to look at the page, but for the integrity of the function, please bear, it is not impossible to use, we must have a place to submit photos and display the recognition results, isn’t it?
-
Add the front-end template file named index. FTL in the red box as shown below:
- FTL has a form for selecting and submitting files, a script for displaying the results, and a message returned from the background.
<! DOCTYPEhtml>
<head>
<meta charset="UTF-8" />
<title>Uploading pictures to Demo</title>
</head>
<body>
<h1 >Uploading pictures to Demo</h1>
<form action="fileUpload" method="post" enctype="multipart/form-data">
<p>Select test file:<input type="file" name="fileName"/></p>
<p><input type="submit" value="Submit"/></p>
</form><# MSG?? ><span>${msg}</span><br><br>
<#else >
<span>${msg! (" file not uploaded ")}</span><br></#if> <#if fileName?? </#if fileName?? > < # -<img src="/show? fileName=${fileName}" style="width: 100px"/>-->
<img src="/show? fileName=${fileName}"/>
<#else>
<#--<img src="/show" style="width: 200px"/>-->
</#if>
</body>
</html>
Copy the code
- The page should look something like this:
Back-end logic: Initialization
-
In order to keep it simple, all the back-end logic into a Java file: YoloServiceController. Java, according to the front of carding process, let’s look at the initialization section
-
The first are member variables and dependencies
private final ResourceLoader resourceLoader;
@Autowired
public YoloServiceController(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Value("${web.upload-path}")
private String uploadPath;
@Value("${opencv.yolo-cfg-path}")
private String cfgPath;
@Value("${opencv.yolo-weights-path}")
private String weightsPath;
@Value("${opencv.yolo-coconames-path}")
private String namesPath;
@Value("${opencv.yolo-width}")
private int width;
@Value("${opencv.yolo-height}")
private int height;
/** * confidence threshold (above which the inference result is considered credible) */
private float confidenceThreshold = 0.5 f;
private float nmsThreshold = 0.4 f;
// Neural network
private Net net;
/ / output layer
private StringVector outNames;
// Class name
private List<String> names;
Copy the code
- The key method is to call readNetFromDarknet, and check if there are cudA-enabled devices. If there are CUDA-enabled devices, set them in the neural network:
@PostConstruct
private void init(a) throws Exception {
// Initialize the print to make sure the encoding is normal, otherwise the log output will be garbled
log.error("file.encoding is " + System.getProperty("file.encoding"));
// Initialize the neural network
net = readNetFromDarknet(cfgPath, weightsPath);
// Check whether the network is empty
if (net.empty()) {
log.error("Neural network initialization failed");
throw new Exception("Neural network initialization failed");
}
/ / output layer
outNames = net.getUnconnectedOutLayersNames();
/ / check the GPU
if (getCudaEnabledDeviceCount() > 0) {
net.setPreferableBackend(opencv_dnn.DNN_BACKEND_CUDA);
net.setPreferableTarget(opencv_dnn.DNN_TARGET_CUDA);
}
// Class name
try {
names = Files.readAllLines(Paths.get(namesPath));
} catch (IOException e) {
log.error("Failed to get class name, file path [{}]", namesPath, e); }}Copy the code
Handling uploaded Files
- How does the front end deal with the image file submitted in binary format? A simple file processing method upload will save the file to the specified location on the server, and then call:
/** * Upload the file to the specified directory *@paramThe file file *@paramPath File path *@paramFileName indicates the source fileName *@return* /
private static boolean upload(MultipartFile file, String path, String fileName){
// Use the original file name
String realPath = path + "/" + fileName;
File dest = new File(realPath);
// Check whether the file parent directory exists
if(! dest.getParentFile().exists()){ dest.getParentFile().mkdir(); }try {
// Save the file
file.transferTo(dest);
return true;
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return false; }}Copy the code
Object detection
- We are ready to write the core object detection code in the yolo-Demo application’s method of handling Web requests, as shown below. This is just an outline, linking reasoning, result processing, image annotation and other functions together to form a complete process, but we don’t get into the details of each specific function:
@RequestMapping("fileUpload")
public String upload(@RequestParam("fileName") MultipartFile file, Map<String, Object> map){
log.info("File [{}], size [{}]", file.getOriginalFilename(), file.getSize());
// File name
String originalFileName = file.getOriginalFilename();
if(! upload(file, uploadPath, originalFileName)){ map.put("msg"."Upload failed!");
return "forward:/index";
}
// Read files to Mat
Mat src = imread(uploadPath + "/" + originalFileName);
// Perform reasoning
MatVector outs = doPredict(src);
// Process the original inference result,
// For each target detected, find the category with the highest confidence as the target category.
// Also find out the location of each target, which is stored in the ObjectDetectionResult object
List<ObjectDetectionResult> results = postprocess(src, outs);
// Release resources
outs.releaseReference();
// Total number of detected targets
int detectNum = results.size();
log.info("{} objects have been detected.", detectNum);
// Not detected
if (detectNum<1) {
// Display the image
map.put("msg"."No target detected");
/ / file name
map.put("fileName", originalFileName);
return "forward:/index";
} else {
// Prompt information on the detection result page
map.put("msg"."Detected" + results.size() + "A target.");
}
// Calculate the total time and print it in the upper left corner of the picture
printTimeUsed(src);
// Place each identified object in the picture box and mark the category of the object in the upper left corner of the box
markEveryDetectObject(src, results);
// Keep the annotated image on disk and write the image information to map (for jump page use)
saveMarkedImage(map, src);
return "forward:/index";
}
Copy the code
- Now that you’ve got the whole process figured out, let’s expand on each detail
Detect objects with neural networks
-
As can be seen from the above code, after the image is converted into a Mat object (an important data structure in OpenCV, which can be interpreted as a matrix, containing information about each pixel of the image), it is fed into the doPredict method, which gets the object recognition result after execution
-
A closer look at the doPredict method shows that the core of the doPredict method is to use the blobFromImage method to get a four-dimensional BLOB object and send it to the neural network for detection (net.setinput, net.forward).
/** * Perform reasoning with neural network *@param src
* @return* /
private MatVector doPredict(Mat src) {
// Convert the image to a 4-dimensional blog and resize it
Mat inputBlob = blobFromImage(src,
1 / 255.0.new Size(width, height),
new Scalar(0.0),
true.false,
CV_32F);
// Neural network input
net.setInput(inputBlob);
// Set the container in which the output is stored
MatVector outs = new MatVector(outNames.size());
// Reason, the result is saved in outs
net.forward(outs, outNames);
// Release resources
inputBlob.release();
return outs;
}
Copy the code
-
Note that blobFromImage, net.setInput, and net.forward are native methods provided by OpenCV’s DNN module
-
The doPredict method returns a MatVector, which contains the test results
Process the original test results
- The detection result MatVector object is a set, which contains multiple Mat objects. Each Mat object is a table, which contains abundant data. The specific content is shown in the figure below:
- Seen above, I believe you are have answers to how to deal with the original test results, as long as from MatVector fetch Mat, one each Mat as form, the form each row in the column with the highest probability is found, this column is the object of the class (each column as to what is, why does it have to be above the fifth columns in a table, Bicycles in the sixth column and toothbrushes in the last? More on this later) :
@param frame @param outs @return */ private List<ObjectDetectionResult> PostProcess MatVector outs) { final IntVector classIds = new IntVector(); final FloatVector confidences = new FloatVector(); final RectVector boxes = new RectVector(); For (int I = 0; i < outs.size(); ++i) { // extract the bounding boxes that have a high enough score // and assign their highest confidence class // For example, the confidence level of the car to the cat is 90%, and the confidence level of the dog is 80%, then the cat will be considered as Mat result = outs.get(I); FloatIndexer data = result.createIndexer(); For (int j = 0; int j = 0; int j = 0; int j = 0; j < result.rows(); j++) { // minMaxLoc implemented in java because it is 1D int maxIndex = -1; float maxScore = Float.MIN_VALUE; for (int k = 5; k < result.cols(); k++) { float score = data.get(j, k); if (score > maxScore) { maxScore = score; maxIndex = k - 5; }} // If the maximum value is greater than the previously set confidence threshold, it indicates that the object can be identified as this type of object, // Then the object identification information is saved, the information to be saved is: If (maxScore > confidenceThreshold) {int centerX = (int) (data.get(j, 0) * frame.cols())); int centerY = (int) (data.get(j, 1) * frame.rows()); int width = (int) (data.get(j, 2) * frame.cols()); int height = (int) (data.get(j, 3) * frame.rows()); int left = centerX - width / 2; int top = centerY - height / 2; Push_back (maxIndex); // save confidences. Push_back (maxScore); Push_back (new Rect(left, top, width, height)); } // Data.release (); result.release(); } // remove overlapping bounding boxes with NMS IntPointer indices = new IntPointer(confidences.size()); FloatPointer confidencesPointer = new FloatPointer(confidences.size()); confidencesPointer.put(confidences.get()); NMSBoxes(boxes, confidencesPointer, confidenceThreshold, nmsThreshold, indices, 1.f, 0); List<ObjectDetectionResult> Detections = new ArrayList<>(); for (int i = 0; i < indices.limit(); ++i) { final int idx = indices.get(i); final Rect box = boxes.get(idx); final int clsId = classIds.get(idx); detections.add(new ObjectDetectionResult( clsId, names.get(clsId), confidences.get(idx), box.x(), box.y(), box.width(), box.height() )); // Release the resource box.releasereference (); } // Release resource indices.releasereference (); confidencesPointer.releaseReference(); classIds.releaseReference(); confidences.releaseReference(); boxes.releaseReference(); return detections; }Copy the code
- The visible code is simple, treating each Mat as a table, with two special things to deal with:
- ConfidenceThreshold variable, here is 0.5. If the maximum probability of a line is less than 0.5, it means that all categories are not likely to be known, so it is not recognized. Therefore, it will not be stored in the Detections set (not marked in the result image).
- NMSBoxes: When the classifier evolved into a detector, Windows were created on multiple scales in the original image. This resulted in the effect on the left of the image below, where multiple faces were detected by the same person and NMSBoxes were used to preserve the optimal one
- This table is the detection result of YOLO4, so the classification of each column should be explained by YOLO4. The file named coco-names is provided officially, and the contents of the file are shown as follows. There are 80 lines in total, and each line represents a category:
- By now you are smart enough to know what category each column in the Mat table represents: each column in the Mat table corresponds to each row of coco-.names, as shown below:
- After the postProcess method is executed, the detection result of a photo is put into the collection named Detections. Each element in the collection represents a recognized object. Let’s take a look at the data structure of this element, as shown below.
@Data
@AllArgsConstructor
public class ObjectDetectionResult {
// Category index
int classId;
// Category name
String className;
/ / confidence level
float confidence;
// The horizontal coordinate of the object in the photo
int x;
// The vertical coordinate of the object in the photo
int y;
// Object width
int width;
// Object height
int height;
}
Copy the code
Draw the results on the picture
-
With the detection results in hand, the next thing to do is to draw these results on the original image, so that we have the effect of object recognition. Drawing is divided into two parts, the first is the total time in the upper left corner, and the second is the result of each object recognition
-
First draw the total time of this test in the upper corner of the picture, as shown below:
- The printTimeUsed method is used to draw the total time, which is calculated by dividing the number of multiple layers of the network by the frequency. Note that this is not the total time of the interface on the page, but the total time of the neural network to identify the object.
/** * Calculates the total time and prints it in the upper left corner of the picture *@param src
*/
private void printTimeUsed(Mat src) {
/ / total number
long totalNums = net.getPerfProfile(new DoublePointer());
/ / frequency
double freq = getTickFrequency()/1000;
// Total times divided by frequency is total time
double t = totalNums / freq;
// Print the total detection time in the upper left corner of the display image
putText(src,
String.format("Inference time : %.2f ms", t),
new Point(10.20),
FONT_HERSHEY_SIMPLEX,
0.6.new Scalar(255.0.0.0),
1,
LINE_AA,
false);
}
Copy the code
- The next step is to draw the result of each object. With the ObjectDetectionResult object collection, drawing is very simple: call the local method to draw rectangles and text:
/** * Place each identified object in the picture box and mark the category of the object * in the upper left corner of the box@param src
* @param results
*/
private void markEveryDetectObject(Mat src, List<ObjectDetectionResult> results) {
// Mark each target with category and confidence level on the picture
for(ObjectDetectionResult result : results) {
log.info("Category [{}], confidence [{}%]", result.getClassName(), result.getConfidence() * 100f);
// annotate on image
rectangle(src,
new Point(result.getX(), result.getY()),
new Point(result.getX() + result.getWidth(), result.getY() + result.getHeight()),
Scalar.MAGENTA,
1,
LINE_8,
0);
// The content in the top left corner of the target: category + confidence
String label = result.getClassName() + ":" + String.format("%.2f%%", result.getConfidence() * 100f);
// Calculate the height required to display these contents
IntPointer baseLine = new IntPointer();
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5.1, baseLine);
int top = Math.max(result.getY(), labelSize.height());
// Add content to the image
putText(src, label, new Point(result.getX(), top-4), FONT_HERSHEY_SIMPLEX, 0.5.new Scalar(0.255.0.0), 1, LINE_4, false); }}Copy the code
Display the results
- Now that the core work is done, it’s time to save the image and jump to the display page:
- Now that the SpringBoot project coding is complete, the next thing to do is to make the whole project into a Docker image
Make the SpringBoot project a Docker image
-
In front of the production of JavaCV application rely on the basis of Docker image (CentOS7+JDK8+OpenCV4) “do a good job of the basic image, help us prepare JDK and OpenCV library, make the following operation is very simple, let’s step by step
-
Dockerfile: Dockerfile: Dockerfile: Dockerfile:
#The base image integrates openjdk8 and opencv4.5.3The FROM bolingcavalry/opencv4.5.3:0.0.1
#Create a directory
RUN mkdir -p /app/images && mkdir -p /app/model
#Specifies the source location for the content of the mirror
ARG DEPENDENCY=target/dependency
#Copy content to mirror
COPY ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY ${DEPENDENCY}/META-INF /app/META-INF
COPY ${DEPENDENCY}/BOOT-INF/classes /app
ENV LANG C.UTF-8
ENV LANGUAGE zh_CN.UTF-8
ENV LC_ALL C.UTF-8
ENV TZ Asia/Shanghai
#Specify the start command (be careful to perform encoding, otherwise the log is garbled)
ENTRYPOINT ["java","-Dfile.encoding=utf-8","-cp","app:app/lib/*","com.bolingcavalry.yolodemo.YoloDemoApplication"]
Copy the code
-
Go to the pom. XML directory and run MVN clean package -u, which is a regular Maven command that compilers the source code. Yolo-demo-1.0-snapshot.jar is generated in the target directory
-
To extract the contents needed to make the Docker image from the JAR file, run the following command:
mkdir -p target/dependency && (cd target/dependency; jar -xf .. /*.jar)Copy the code
- To build an image, run the following command:
Docker build -t bolingcavalry/yolodemo: 0.0.1.Copy the code
- Successful construction:
will@willMini yolo-demo % Docker images REPOSITORY TAG IMAGE ID CREATED SIZE BolingCavalry/Yolodemo 0.0.1d0ef6e734b53 About a minute line 2.99 GB bolingcavalry/opencv4.5.3 0.0.1 d1518ffa4699 6 days line 2.01 GBCopy the code
-
At this point, the SpringBoot application with full object recognition capability has been developed. Remember the file path configuration in application.properties? We are going to download these files, there are two ways to download, you can choose one of them
-
The first one is from the official download, from the following three addresses respectively download:
- YOLOv4 configuration file: raw.githubusercontent.com/AlexeyAB/da…
- Weight: github.com/AlexeyAB/da…
- Category name: raw.githubusercontent.com/AlexeyAB/da…
-
The second is from CSDN download (without integral), the above three documents I packaged on this: download.csdn.net/download/bo…
-
No matter what kind of the above two ways, eventually will have three files: yolov4. CFG, yolov4. Weights, coco. The names, please put them in the same directory, I am here: / home/will/temp / 202110/19 / model
-
Create a new directory to store photos, I here the new directory is: / home/will/temp / 202110/19 / images, pay attention to ensure that the directory can read and write
The final directory structure is as follows:
/ home/will/temp / 202110/19 / ├ ─ ─ images └ ─ ─ model ├ ─ ─ coco. Names ├ ─ ─ yolov4. CFG └ ─ ─ yolov4. The weightsCopy the code
- When all is ready, execute the following command to run the service:
sudo docker run \ --rm \ --name yolodemo \ -p 8080:8080 \ -v /home/will/temp/202110/19/images:/app/images \ -v / home/will/temp / 202110/19 / model: / app/model/bolingcavalry/yolodemo: 0.0.1Copy the code
-
Once the service is up and running, the operation process and effect are exactly the same as in the article “Three Minutes: Extreme Experience of Object Detection in JAVA Edition (YOLO4)”, so I won’t go into details
-
At this point, the whole object recognition development practice is completed, Java in engineering convenience, combined with the excellent model in the field of deep learning, for us to solve the problem of visual image added an alternative, if you are a Java programmer interested in vision and image, I hope this article can give you some reference
You are not alone, Xinchen original accompany all the way
- Java series
- Spring series
- The Docker series
- Kubernetes series
- Database + middleware series
- The conversation series
Welcome to pay attention to the public number: programmer Xin Chen
Wechat search “programmer Xin Chen”, I am Xin Chen, looking forward to enjoying the Java world with you…
Github.com/zq2599/blog…