Spaces:
Runtime error
Runtime error
SakshiRathi77
commited on
Commit
•
12b0903
1
Parent(s):
7fe9782
Upload 33 files
Browse files- .gitattributes +6 -12
- CONTRIBUTING.md +94 -0
- Dockerfile +60 -0
- LICENSE +674 -0
- Planogram_compliance_inference.ipynb +0 -0
- Procfile +1 -0
- README.md +160 -7
- _requirements.txt +36 -0
- app_test.ipynb +0 -0
- detect.py +460 -0
- export.py +1013 -0
- hubconf.py +309 -0
- packages.txt +6 -0
- planogram.yaml +18 -0
- requirements.txt +0 -52
- runtime.txt +1 -0
- sample_master_planogram.jpeg +3 -0
- sample_planogram.jpg +0 -0
- setup.sh +8 -0
- test_local_infernce.ipynb +0 -0
- tmp.png +3 -0
- tmp_xml_annotation.xml +0 -0
- train.py +1046 -0
- tutorial.ipynb +1022 -0
- utils.py +61 -0
- val.py +593 -0
- yolo_inference_util.py +369 -0
- yolov5s.pt +3 -0
.gitattributes
CHANGED
@@ -1,37 +1,31 @@
|
|
1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
|
|
4 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
5 |
-
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
11 |
-
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
12 |
*.model filter=lfs diff=lfs merge=lfs -text
|
13 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
14 |
-
*.npy filter=lfs diff=lfs merge=lfs -text
|
15 |
-
*.npz filter=lfs diff=lfs merge=lfs -text
|
16 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
17 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
18 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
19 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
20 |
-
*.pickle filter=lfs diff=lfs merge=lfs -text
|
21 |
-
*.pkl filter=lfs diff=lfs merge=lfs -text
|
22 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
23 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
24 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
25 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
26 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
27 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
28 |
-
*.tar filter=lfs diff=lfs merge=lfs -text
|
29 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
30 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
31 |
-
*.wasm filter=lfs diff=lfs merge=lfs -text
|
32 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
34 |
-
*.
|
35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
36 |
-
|
37 |
-
|
|
|
|
|
|
1 |
*.7z filter=lfs diff=lfs merge=lfs -text
|
2 |
*.arrow filter=lfs diff=lfs merge=lfs -text
|
3 |
*.bin filter=lfs diff=lfs merge=lfs -text
|
4 |
+
*.bin.* filter=lfs diff=lfs merge=lfs -text
|
5 |
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
|
|
6 |
*.ftz filter=lfs diff=lfs merge=lfs -text
|
7 |
*.gz filter=lfs diff=lfs merge=lfs -text
|
8 |
*.h5 filter=lfs diff=lfs merge=lfs -text
|
9 |
*.joblib filter=lfs diff=lfs merge=lfs -text
|
10 |
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
|
|
11 |
*.model filter=lfs diff=lfs merge=lfs -text
|
12 |
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
13 |
*.onnx filter=lfs diff=lfs merge=lfs -text
|
14 |
*.ot filter=lfs diff=lfs merge=lfs -text
|
15 |
*.parquet filter=lfs diff=lfs merge=lfs -text
|
16 |
*.pb filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
17 |
*.pt filter=lfs diff=lfs merge=lfs -text
|
18 |
*.pth filter=lfs diff=lfs merge=lfs -text
|
19 |
*.rar filter=lfs diff=lfs merge=lfs -text
|
|
|
20 |
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
21 |
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
|
|
22 |
*.tflite filter=lfs diff=lfs merge=lfs -text
|
23 |
*.tgz filter=lfs diff=lfs merge=lfs -text
|
|
|
24 |
*.xz filter=lfs diff=lfs merge=lfs -text
|
25 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
26 |
+
*.zstandard filter=lfs diff=lfs merge=lfs -text
|
27 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
28 |
+
sample_master_planogram.jpeg filter=lfs diff=lfs merge=lfs -text
|
29 |
+
tmp.png filter=lfs diff=lfs merge=lfs -text
|
30 |
+
tmp/master_tmp.png filter=lfs diff=lfs merge=lfs -text
|
31 |
+
tmp/to_score_planogram_tmp.png filter=lfs diff=lfs merge=lfs -text
|
CONTRIBUTING.md
ADDED
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
## Contributing to YOLOv5 🚀
|
2 |
+
|
3 |
+
We love your input! We want to make contributing to YOLOv5 as easy and transparent as possible, whether it's:
|
4 |
+
|
5 |
+
- Reporting a bug
|
6 |
+
- Discussing the current state of the code
|
7 |
+
- Submitting a fix
|
8 |
+
- Proposing a new feature
|
9 |
+
- Becoming a maintainer
|
10 |
+
|
11 |
+
YOLOv5 works so well due to our combined community effort, and for every small improvement you contribute you will be
|
12 |
+
helping push the frontiers of what's possible in AI 😃!
|
13 |
+
|
14 |
+
## Submitting a Pull Request (PR) 🛠️
|
15 |
+
|
16 |
+
Submitting a PR is easy! This example shows how to submit a PR for updating `requirements.txt` in 4 steps:
|
17 |
+
|
18 |
+
### 1. Select File to Update
|
19 |
+
|
20 |
+
Select `requirements.txt` to update by clicking on it in GitHub.
|
21 |
+
<p align="center"><img width="800" alt="PR_step1" src="https://user-images.githubusercontent.com/26833433/122260847-08be2600-ced4-11eb-828b-8287ace4136c.png"></p>
|
22 |
+
|
23 |
+
### 2. Click 'Edit this file'
|
24 |
+
|
25 |
+
Button is in top-right corner.
|
26 |
+
<p align="center"><img width="800" alt="PR_step2" src="https://user-images.githubusercontent.com/26833433/122260844-06f46280-ced4-11eb-9eec-b8a24be519ca.png"></p>
|
27 |
+
|
28 |
+
### 3. Make Changes
|
29 |
+
|
30 |
+
Change `matplotlib` version from `3.2.2` to `3.3`.
|
31 |
+
<p align="center"><img width="800" alt="PR_step3" src="https://user-images.githubusercontent.com/26833433/122260853-0a87e980-ced4-11eb-9fd2-3650fb6e0842.png"></p>
|
32 |
+
|
33 |
+
### 4. Preview Changes and Submit PR
|
34 |
+
|
35 |
+
Click on the **Preview changes** tab to verify your updates. At the bottom of the screen select 'Create a **new branch**
|
36 |
+
for this commit', assign your branch a descriptive name such as `fix/matplotlib_version` and click the green **Propose
|
37 |
+
changes** button. All done, your PR is now submitted to YOLOv5 for review and approval 😃!
|
38 |
+
<p align="center"><img width="800" alt="PR_step4" src="https://user-images.githubusercontent.com/26833433/122260856-0b208000-ced4-11eb-8e8e-77b6151cbcc3.png"></p>
|
39 |
+
|
40 |
+
### PR recommendations
|
41 |
+
|
42 |
+
To allow your work to be integrated as seamlessly as possible, we advise you to:
|
43 |
+
|
44 |
+
- ✅ Verify your PR is **up-to-date with origin/master.** If your PR is behind origin/master an
|
45 |
+
automatic [GitHub actions](https://github.com/ultralytics/yolov5/blob/master/.github/workflows/rebase.yml) rebase may
|
46 |
+
be attempted by including the /rebase command in a comment body, or by running the following code, replacing 'feature'
|
47 |
+
with the name of your local branch:
|
48 |
+
|
49 |
+
```bash
|
50 |
+
git remote add upstream https://github.com/ultralytics/yolov5.git
|
51 |
+
git fetch upstream
|
52 |
+
git checkout feature # <----- replace 'feature' with local branch name
|
53 |
+
git merge upstream/master
|
54 |
+
git push -u origin -f
|
55 |
+
```
|
56 |
+
|
57 |
+
- ✅ Verify all Continuous Integration (CI) **checks are passing**.
|
58 |
+
- ✅ Reduce changes to the absolute **minimum** required for your bug fix or feature addition. _"It is not daily increase
|
59 |
+
but daily decrease, hack away the unessential. The closer to the source, the less wastage there is."_ -Bruce Lee
|
60 |
+
|
61 |
+
## Submitting a Bug Report 🐛
|
62 |
+
|
63 |
+
If you spot a problem with YOLOv5 please submit a Bug Report!
|
64 |
+
|
65 |
+
For us to start investigating a possibel problem we need to be able to reproduce it ourselves first. We've created a few
|
66 |
+
short guidelines below to help users provide what we need in order to get started.
|
67 |
+
|
68 |
+
When asking a question, people will be better able to provide help if you provide **code** that they can easily
|
69 |
+
understand and use to **reproduce** the problem. This is referred to by community members as creating
|
70 |
+
a [minimum reproducible example](https://stackoverflow.com/help/minimal-reproducible-example). Your code that reproduces
|
71 |
+
the problem should be:
|
72 |
+
|
73 |
+
* ✅ **Minimal** – Use as little code as possible that still produces the same problem
|
74 |
+
* ✅ **Complete** – Provide **all** parts someone else needs to reproduce your problem in the question itself
|
75 |
+
* ✅ **Reproducible** – Test the code you're about to provide to make sure it reproduces the problem
|
76 |
+
|
77 |
+
In addition to the above requirements, for [Ultralytics](https://ultralytics.com/) to provide assistance your code
|
78 |
+
should be:
|
79 |
+
|
80 |
+
* ✅ **Current** – Verify that your code is up-to-date with current
|
81 |
+
GitHub [master](https://github.com/ultralytics/yolov5/tree/master), and if necessary `git pull` or `git clone` a new
|
82 |
+
copy to ensure your problem has not already been resolved by previous commits.
|
83 |
+
* ✅ **Unmodified** – Your problem must be reproducible without any modifications to the codebase in this
|
84 |
+
repository. [Ultralytics](https://ultralytics.com/) does not provide support for custom code ⚠️.
|
85 |
+
|
86 |
+
If you believe your problem meets all of the above criteria, please close this issue and raise a new one using the 🐛 **
|
87 |
+
Bug Report** [template](https://github.com/ultralytics/yolov5/issues/new/choose) and providing
|
88 |
+
a [minimum reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) to help us better
|
89 |
+
understand and diagnose your problem.
|
90 |
+
|
91 |
+
## License
|
92 |
+
|
93 |
+
By contributing, you agree that your contributions will be licensed under
|
94 |
+
the [GPL-3.0 license](https://choosealicense.com/licenses/gpl-3.0/)
|
Dockerfile
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# # YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
2 |
+
|
3 |
+
# # Start FROM Nvidia PyTorch image https://ngc.nvidia.com/catalog/containers/nvidia:pytorch
|
4 |
+
# FROM nvcr.io/nvidia/pytorch:21.05-py3
|
5 |
+
|
6 |
+
# # Install linux packages
|
7 |
+
# RUN apt update && apt install -y zip htop screen libgl1-mesa-glx
|
8 |
+
|
9 |
+
# # Install python dependencies
|
10 |
+
# COPY requirements.txt .
|
11 |
+
# RUN python -m pip install --upgrade pip
|
12 |
+
# RUN pip uninstall -y nvidia-tensorboard nvidia-tensorboard-plugin-dlprof
|
13 |
+
# RUN pip install --no-cache -r requirements.txt coremltools onnx gsutil notebook
|
14 |
+
# RUN pip install --no-cache -U torch torchvision numpy
|
15 |
+
# # RUN pip install --no-cache torch==1.9.0+cu111 torchvision==0.10.0+cu111 -f https://download.pytorch.org/whl/torch_stable.html
|
16 |
+
|
17 |
+
# # Create working directory
|
18 |
+
# RUN mkdir -p /usr/src/app
|
19 |
+
# WORKDIR /usr/src/app
|
20 |
+
|
21 |
+
# # Copy contents
|
22 |
+
# COPY . /usr/src/app
|
23 |
+
|
24 |
+
# # Set environment variables
|
25 |
+
# ENV HOME=/usr/src/app
|
26 |
+
|
27 |
+
|
28 |
+
# Usage Examples -------------------------------------------------------------------------------------------------------
|
29 |
+
|
30 |
+
# Build and Push
|
31 |
+
# t=ultralytics/yolov5:latest && sudo docker build -t $t . && sudo docker push $t
|
32 |
+
|
33 |
+
# Pull and Run
|
34 |
+
# t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all $t
|
35 |
+
|
36 |
+
# Pull and Run with local directory access
|
37 |
+
# t=ultralytics/yolov5:latest && sudo docker pull $t && sudo docker run -it --ipc=host --gpus all -v "$(pwd)"/datasets:/usr/src/datasets $t
|
38 |
+
|
39 |
+
# Kill all
|
40 |
+
# sudo docker kill $(sudo docker ps -q)
|
41 |
+
|
42 |
+
# Kill all image-based
|
43 |
+
# sudo docker kill $(sudo docker ps -qa --filter ancestor=ultralytics/yolov5:latest)
|
44 |
+
|
45 |
+
# Bash into running container
|
46 |
+
# sudo docker exec -it 5a9b5863d93d bash
|
47 |
+
|
48 |
+
# Bash into stopped container
|
49 |
+
# id=$(sudo docker ps -qa) && sudo docker start $id && sudo docker exec -it $id bash
|
50 |
+
|
51 |
+
# Clean up
|
52 |
+
# docker system prune -a --volumes
|
53 |
+
FROM python:3.9
|
54 |
+
EXPOSE 8501
|
55 |
+
WORKDIR /app
|
56 |
+
COPY requirements.txt ./requirements.txt
|
57 |
+
RUN pip3 install -r requirements.txt
|
58 |
+
COPY . .
|
59 |
+
# CMD streamlit run app.py
|
60 |
+
CMD streamlit run --server.port $PORT app.py
|
LICENSE
ADDED
@@ -0,0 +1,674 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
GNU GENERAL PUBLIC LICENSE
|
2 |
+
Version 3, 29 June 2007
|
3 |
+
|
4 |
+
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
5 |
+
Everyone is permitted to copy and distribute verbatim copies
|
6 |
+
of this license document, but changing it is not allowed.
|
7 |
+
|
8 |
+
Preamble
|
9 |
+
|
10 |
+
The GNU General Public License is a free, copyleft license for
|
11 |
+
software and other kinds of works.
|
12 |
+
|
13 |
+
The licenses for most software and other practical works are designed
|
14 |
+
to take away your freedom to share and change the works. By contrast,
|
15 |
+
the GNU General Public License is intended to guarantee your freedom to
|
16 |
+
share and change all versions of a program--to make sure it remains free
|
17 |
+
software for all its users. We, the Free Software Foundation, use the
|
18 |
+
GNU General Public License for most of our software; it applies also to
|
19 |
+
any other work released this way by its authors. You can apply it to
|
20 |
+
your programs, too.
|
21 |
+
|
22 |
+
When we speak of free software, we are referring to freedom, not
|
23 |
+
price. Our General Public Licenses are designed to make sure that you
|
24 |
+
have the freedom to distribute copies of free software (and charge for
|
25 |
+
them if you wish), that you receive source code or can get it if you
|
26 |
+
want it, that you can change the software or use pieces of it in new
|
27 |
+
free programs, and that you know you can do these things.
|
28 |
+
|
29 |
+
To protect your rights, we need to prevent others from denying you
|
30 |
+
these rights or asking you to surrender the rights. Therefore, you have
|
31 |
+
certain responsibilities if you distribute copies of the software, or if
|
32 |
+
you modify it: responsibilities to respect the freedom of others.
|
33 |
+
|
34 |
+
For example, if you distribute copies of such a program, whether
|
35 |
+
gratis or for a fee, you must pass on to the recipients the same
|
36 |
+
freedoms that you received. You must make sure that they, too, receive
|
37 |
+
or can get the source code. And you must show them these terms so they
|
38 |
+
know their rights.
|
39 |
+
|
40 |
+
Developers that use the GNU GPL protect your rights with two steps:
|
41 |
+
(1) assert copyright on the software, and (2) offer you this License
|
42 |
+
giving you legal permission to copy, distribute and/or modify it.
|
43 |
+
|
44 |
+
For the developers' and authors' protection, the GPL clearly explains
|
45 |
+
that there is no warranty for this free software. For both users' and
|
46 |
+
authors' sake, the GPL requires that modified versions be marked as
|
47 |
+
changed, so that their problems will not be attributed erroneously to
|
48 |
+
authors of previous versions.
|
49 |
+
|
50 |
+
Some devices are designed to deny users access to install or run
|
51 |
+
modified versions of the software inside them, although the manufacturer
|
52 |
+
can do so. This is fundamentally incompatible with the aim of
|
53 |
+
protecting users' freedom to change the software. The systematic
|
54 |
+
pattern of such abuse occurs in the area of products for individuals to
|
55 |
+
use, which is precisely where it is most unacceptable. Therefore, we
|
56 |
+
have designed this version of the GPL to prohibit the practice for those
|
57 |
+
products. If such problems arise substantially in other domains, we
|
58 |
+
stand ready to extend this provision to those domains in future versions
|
59 |
+
of the GPL, as needed to protect the freedom of users.
|
60 |
+
|
61 |
+
Finally, every program is threatened constantly by software patents.
|
62 |
+
States should not allow patents to restrict development and use of
|
63 |
+
software on general-purpose computers, but in those that do, we wish to
|
64 |
+
avoid the special danger that patents applied to a free program could
|
65 |
+
make it effectively proprietary. To prevent this, the GPL assures that
|
66 |
+
patents cannot be used to render the program non-free.
|
67 |
+
|
68 |
+
The precise terms and conditions for copying, distribution and
|
69 |
+
modification follow.
|
70 |
+
|
71 |
+
TERMS AND CONDITIONS
|
72 |
+
|
73 |
+
0. Definitions.
|
74 |
+
|
75 |
+
"This License" refers to version 3 of the GNU General Public License.
|
76 |
+
|
77 |
+
"Copyright" also means copyright-like laws that apply to other kinds of
|
78 |
+
works, such as semiconductor masks.
|
79 |
+
|
80 |
+
"The Program" refers to any copyrightable work licensed under this
|
81 |
+
License. Each licensee is addressed as "you". "Licensees" and
|
82 |
+
"recipients" may be individuals or organizations.
|
83 |
+
|
84 |
+
To "modify" a work means to copy from or adapt all or part of the work
|
85 |
+
in a fashion requiring copyright permission, other than the making of an
|
86 |
+
exact copy. The resulting work is called a "modified version" of the
|
87 |
+
earlier work or a work "based on" the earlier work.
|
88 |
+
|
89 |
+
A "covered work" means either the unmodified Program or a work based
|
90 |
+
on the Program.
|
91 |
+
|
92 |
+
To "propagate" a work means to do anything with it that, without
|
93 |
+
permission, would make you directly or secondarily liable for
|
94 |
+
infringement under applicable copyright law, except executing it on a
|
95 |
+
computer or modifying a private copy. Propagation includes copying,
|
96 |
+
distribution (with or without modification), making available to the
|
97 |
+
public, and in some countries other activities as well.
|
98 |
+
|
99 |
+
To "convey" a work means any kind of propagation that enables other
|
100 |
+
parties to make or receive copies. Mere interaction with a user through
|
101 |
+
a computer network, with no transfer of a copy, is not conveying.
|
102 |
+
|
103 |
+
An interactive user interface displays "Appropriate Legal Notices"
|
104 |
+
to the extent that it includes a convenient and prominently visible
|
105 |
+
feature that (1) displays an appropriate copyright notice, and (2)
|
106 |
+
tells the user that there is no warranty for the work (except to the
|
107 |
+
extent that warranties are provided), that licensees may convey the
|
108 |
+
work under this License, and how to view a copy of this License. If
|
109 |
+
the interface presents a list of user commands or options, such as a
|
110 |
+
menu, a prominent item in the list meets this criterion.
|
111 |
+
|
112 |
+
1. Source Code.
|
113 |
+
|
114 |
+
The "source code" for a work means the preferred form of the work
|
115 |
+
for making modifications to it. "Object code" means any non-source
|
116 |
+
form of a work.
|
117 |
+
|
118 |
+
A "Standard Interface" means an interface that either is an official
|
119 |
+
standard defined by a recognized standards body, or, in the case of
|
120 |
+
interfaces specified for a particular programming language, one that
|
121 |
+
is widely used among developers working in that language.
|
122 |
+
|
123 |
+
The "System Libraries" of an executable work include anything, other
|
124 |
+
than the work as a whole, that (a) is included in the normal form of
|
125 |
+
packaging a Major Component, but which is not part of that Major
|
126 |
+
Component, and (b) serves only to enable use of the work with that
|
127 |
+
Major Component, or to implement a Standard Interface for which an
|
128 |
+
implementation is available to the public in source code form. A
|
129 |
+
"Major Component", in this context, means a major essential component
|
130 |
+
(kernel, window system, and so on) of the specific operating system
|
131 |
+
(if any) on which the executable work runs, or a compiler used to
|
132 |
+
produce the work, or an object code interpreter used to run it.
|
133 |
+
|
134 |
+
The "Corresponding Source" for a work in object code form means all
|
135 |
+
the source code needed to generate, install, and (for an executable
|
136 |
+
work) run the object code and to modify the work, including scripts to
|
137 |
+
control those activities. However, it does not include the work's
|
138 |
+
System Libraries, or general-purpose tools or generally available free
|
139 |
+
programs which are used unmodified in performing those activities but
|
140 |
+
which are not part of the work. For example, Corresponding Source
|
141 |
+
includes interface definition files associated with source files for
|
142 |
+
the work, and the source code for shared libraries and dynamically
|
143 |
+
linked subprograms that the work is specifically designed to require,
|
144 |
+
such as by intimate data communication or control flow between those
|
145 |
+
subprograms and other parts of the work.
|
146 |
+
|
147 |
+
The Corresponding Source need not include anything that users
|
148 |
+
can regenerate automatically from other parts of the Corresponding
|
149 |
+
Source.
|
150 |
+
|
151 |
+
The Corresponding Source for a work in source code form is that
|
152 |
+
same work.
|
153 |
+
|
154 |
+
2. Basic Permissions.
|
155 |
+
|
156 |
+
All rights granted under this License are granted for the term of
|
157 |
+
copyright on the Program, and are irrevocable provided the stated
|
158 |
+
conditions are met. This License explicitly affirms your unlimited
|
159 |
+
permission to run the unmodified Program. The output from running a
|
160 |
+
covered work is covered by this License only if the output, given its
|
161 |
+
content, constitutes a covered work. This License acknowledges your
|
162 |
+
rights of fair use or other equivalent, as provided by copyright law.
|
163 |
+
|
164 |
+
You may make, run and propagate covered works that you do not
|
165 |
+
convey, without conditions so long as your license otherwise remains
|
166 |
+
in force. You may convey covered works to others for the sole purpose
|
167 |
+
of having them make modifications exclusively for you, or provide you
|
168 |
+
with facilities for running those works, provided that you comply with
|
169 |
+
the terms of this License in conveying all material for which you do
|
170 |
+
not control copyright. Those thus making or running the covered works
|
171 |
+
for you must do so exclusively on your behalf, under your direction
|
172 |
+
and control, on terms that prohibit them from making any copies of
|
173 |
+
your copyrighted material outside their relationship with you.
|
174 |
+
|
175 |
+
Conveying under any other circumstances is permitted solely under
|
176 |
+
the conditions stated below. Sublicensing is not allowed; section 10
|
177 |
+
makes it unnecessary.
|
178 |
+
|
179 |
+
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
180 |
+
|
181 |
+
No covered work shall be deemed part of an effective technological
|
182 |
+
measure under any applicable law fulfilling obligations under article
|
183 |
+
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
184 |
+
similar laws prohibiting or restricting circumvention of such
|
185 |
+
measures.
|
186 |
+
|
187 |
+
When you convey a covered work, you waive any legal power to forbid
|
188 |
+
circumvention of technological measures to the extent such circumvention
|
189 |
+
is effected by exercising rights under this License with respect to
|
190 |
+
the covered work, and you disclaim any intention to limit operation or
|
191 |
+
modification of the work as a means of enforcing, against the work's
|
192 |
+
users, your or third parties' legal rights to forbid circumvention of
|
193 |
+
technological measures.
|
194 |
+
|
195 |
+
4. Conveying Verbatim Copies.
|
196 |
+
|
197 |
+
You may convey verbatim copies of the Program's source code as you
|
198 |
+
receive it, in any medium, provided that you conspicuously and
|
199 |
+
appropriately publish on each copy an appropriate copyright notice;
|
200 |
+
keep intact all notices stating that this License and any
|
201 |
+
non-permissive terms added in accord with section 7 apply to the code;
|
202 |
+
keep intact all notices of the absence of any warranty; and give all
|
203 |
+
recipients a copy of this License along with the Program.
|
204 |
+
|
205 |
+
You may charge any price or no price for each copy that you convey,
|
206 |
+
and you may offer support or warranty protection for a fee.
|
207 |
+
|
208 |
+
5. Conveying Modified Source Versions.
|
209 |
+
|
210 |
+
You may convey a work based on the Program, or the modifications to
|
211 |
+
produce it from the Program, in the form of source code under the
|
212 |
+
terms of section 4, provided that you also meet all of these conditions:
|
213 |
+
|
214 |
+
a) The work must carry prominent notices stating that you modified
|
215 |
+
it, and giving a relevant date.
|
216 |
+
|
217 |
+
b) The work must carry prominent notices stating that it is
|
218 |
+
released under this License and any conditions added under section
|
219 |
+
7. This requirement modifies the requirement in section 4 to
|
220 |
+
"keep intact all notices".
|
221 |
+
|
222 |
+
c) You must license the entire work, as a whole, under this
|
223 |
+
License to anyone who comes into possession of a copy. This
|
224 |
+
License will therefore apply, along with any applicable section 7
|
225 |
+
additional terms, to the whole of the work, and all its parts,
|
226 |
+
regardless of how they are packaged. This License gives no
|
227 |
+
permission to license the work in any other way, but it does not
|
228 |
+
invalidate such permission if you have separately received it.
|
229 |
+
|
230 |
+
d) If the work has interactive user interfaces, each must display
|
231 |
+
Appropriate Legal Notices; however, if the Program has interactive
|
232 |
+
interfaces that do not display Appropriate Legal Notices, your
|
233 |
+
work need not make them do so.
|
234 |
+
|
235 |
+
A compilation of a covered work with other separate and independent
|
236 |
+
works, which are not by their nature extensions of the covered work,
|
237 |
+
and which are not combined with it such as to form a larger program,
|
238 |
+
in or on a volume of a storage or distribution medium, is called an
|
239 |
+
"aggregate" if the compilation and its resulting copyright are not
|
240 |
+
used to limit the access or legal rights of the compilation's users
|
241 |
+
beyond what the individual works permit. Inclusion of a covered work
|
242 |
+
in an aggregate does not cause this License to apply to the other
|
243 |
+
parts of the aggregate.
|
244 |
+
|
245 |
+
6. Conveying Non-Source Forms.
|
246 |
+
|
247 |
+
You may convey a covered work in object code form under the terms
|
248 |
+
of sections 4 and 5, provided that you also convey the
|
249 |
+
machine-readable Corresponding Source under the terms of this License,
|
250 |
+
in one of these ways:
|
251 |
+
|
252 |
+
a) Convey the object code in, or embodied in, a physical product
|
253 |
+
(including a physical distribution medium), accompanied by the
|
254 |
+
Corresponding Source fixed on a durable physical medium
|
255 |
+
customarily used for software interchange.
|
256 |
+
|
257 |
+
b) Convey the object code in, or embodied in, a physical product
|
258 |
+
(including a physical distribution medium), accompanied by a
|
259 |
+
written offer, valid for at least three years and valid for as
|
260 |
+
long as you offer spare parts or customer support for that product
|
261 |
+
model, to give anyone who possesses the object code either (1) a
|
262 |
+
copy of the Corresponding Source for all the software in the
|
263 |
+
product that is covered by this License, on a durable physical
|
264 |
+
medium customarily used for software interchange, for a price no
|
265 |
+
more than your reasonable cost of physically performing this
|
266 |
+
conveying of source, or (2) access to copy the
|
267 |
+
Corresponding Source from a network server at no charge.
|
268 |
+
|
269 |
+
c) Convey individual copies of the object code with a copy of the
|
270 |
+
written offer to provide the Corresponding Source. This
|
271 |
+
alternative is allowed only occasionally and noncommercially, and
|
272 |
+
only if you received the object code with such an offer, in accord
|
273 |
+
with subsection 6b.
|
274 |
+
|
275 |
+
d) Convey the object code by offering access from a designated
|
276 |
+
place (gratis or for a charge), and offer equivalent access to the
|
277 |
+
Corresponding Source in the same way through the same place at no
|
278 |
+
further charge. You need not require recipients to copy the
|
279 |
+
Corresponding Source along with the object code. If the place to
|
280 |
+
copy the object code is a network server, the Corresponding Source
|
281 |
+
may be on a different server (operated by you or a third party)
|
282 |
+
that supports equivalent copying facilities, provided you maintain
|
283 |
+
clear directions next to the object code saying where to find the
|
284 |
+
Corresponding Source. Regardless of what server hosts the
|
285 |
+
Corresponding Source, you remain obligated to ensure that it is
|
286 |
+
available for as long as needed to satisfy these requirements.
|
287 |
+
|
288 |
+
e) Convey the object code using peer-to-peer transmission, provided
|
289 |
+
you inform other peers where the object code and Corresponding
|
290 |
+
Source of the work are being offered to the general public at no
|
291 |
+
charge under subsection 6d.
|
292 |
+
|
293 |
+
A separable portion of the object code, whose source code is excluded
|
294 |
+
from the Corresponding Source as a System Library, need not be
|
295 |
+
included in conveying the object code work.
|
296 |
+
|
297 |
+
A "User Product" is either (1) a "consumer product", which means any
|
298 |
+
tangible personal property which is normally used for personal, family,
|
299 |
+
or household purposes, or (2) anything designed or sold for incorporation
|
300 |
+
into a dwelling. In determining whether a product is a consumer product,
|
301 |
+
doubtful cases shall be resolved in favor of coverage. For a particular
|
302 |
+
product received by a particular user, "normally used" refers to a
|
303 |
+
typical or common use of that class of product, regardless of the status
|
304 |
+
of the particular user or of the way in which the particular user
|
305 |
+
actually uses, or expects or is expected to use, the product. A product
|
306 |
+
is a consumer product regardless of whether the product has substantial
|
307 |
+
commercial, industrial or non-consumer uses, unless such uses represent
|
308 |
+
the only significant mode of use of the product.
|
309 |
+
|
310 |
+
"Installation Information" for a User Product means any methods,
|
311 |
+
procedures, authorization keys, or other information required to install
|
312 |
+
and execute modified versions of a covered work in that User Product from
|
313 |
+
a modified version of its Corresponding Source. The information must
|
314 |
+
suffice to ensure that the continued functioning of the modified object
|
315 |
+
code is in no case prevented or interfered with solely because
|
316 |
+
modification has been made.
|
317 |
+
|
318 |
+
If you convey an object code work under this section in, or with, or
|
319 |
+
specifically for use in, a User Product, and the conveying occurs as
|
320 |
+
part of a transaction in which the right of possession and use of the
|
321 |
+
User Product is transferred to the recipient in perpetuity or for a
|
322 |
+
fixed term (regardless of how the transaction is characterized), the
|
323 |
+
Corresponding Source conveyed under this section must be accompanied
|
324 |
+
by the Installation Information. But this requirement does not apply
|
325 |
+
if neither you nor any third party retains the ability to install
|
326 |
+
modified object code on the User Product (for example, the work has
|
327 |
+
been installed in ROM).
|
328 |
+
|
329 |
+
The requirement to provide Installation Information does not include a
|
330 |
+
requirement to continue to provide support service, warranty, or updates
|
331 |
+
for a work that has been modified or installed by the recipient, or for
|
332 |
+
the User Product in which it has been modified or installed. Access to a
|
333 |
+
network may be denied when the modification itself materially and
|
334 |
+
adversely affects the operation of the network or violates the rules and
|
335 |
+
protocols for communication across the network.
|
336 |
+
|
337 |
+
Corresponding Source conveyed, and Installation Information provided,
|
338 |
+
in accord with this section must be in a format that is publicly
|
339 |
+
documented (and with an implementation available to the public in
|
340 |
+
source code form), and must require no special password or key for
|
341 |
+
unpacking, reading or copying.
|
342 |
+
|
343 |
+
7. Additional Terms.
|
344 |
+
|
345 |
+
"Additional permissions" are terms that supplement the terms of this
|
346 |
+
License by making exceptions from one or more of its conditions.
|
347 |
+
Additional permissions that are applicable to the entire Program shall
|
348 |
+
be treated as though they were included in this License, to the extent
|
349 |
+
that they are valid under applicable law. If additional permissions
|
350 |
+
apply only to part of the Program, that part may be used separately
|
351 |
+
under those permissions, but the entire Program remains governed by
|
352 |
+
this License without regard to the additional permissions.
|
353 |
+
|
354 |
+
When you convey a copy of a covered work, you may at your option
|
355 |
+
remove any additional permissions from that copy, or from any part of
|
356 |
+
it. (Additional permissions may be written to require their own
|
357 |
+
removal in certain cases when you modify the work.) You may place
|
358 |
+
additional permissions on material, added by you to a covered work,
|
359 |
+
for which you have or can give appropriate copyright permission.
|
360 |
+
|
361 |
+
Notwithstanding any other provision of this License, for material you
|
362 |
+
add to a covered work, you may (if authorized by the copyright holders of
|
363 |
+
that material) supplement the terms of this License with terms:
|
364 |
+
|
365 |
+
a) Disclaiming warranty or limiting liability differently from the
|
366 |
+
terms of sections 15 and 16 of this License; or
|
367 |
+
|
368 |
+
b) Requiring preservation of specified reasonable legal notices or
|
369 |
+
author attributions in that material or in the Appropriate Legal
|
370 |
+
Notices displayed by works containing it; or
|
371 |
+
|
372 |
+
c) Prohibiting misrepresentation of the origin of that material, or
|
373 |
+
requiring that modified versions of such material be marked in
|
374 |
+
reasonable ways as different from the original version; or
|
375 |
+
|
376 |
+
d) Limiting the use for publicity purposes of names of licensors or
|
377 |
+
authors of the material; or
|
378 |
+
|
379 |
+
e) Declining to grant rights under trademark law for use of some
|
380 |
+
trade names, trademarks, or service marks; or
|
381 |
+
|
382 |
+
f) Requiring indemnification of licensors and authors of that
|
383 |
+
material by anyone who conveys the material (or modified versions of
|
384 |
+
it) with contractual assumptions of liability to the recipient, for
|
385 |
+
any liability that these contractual assumptions directly impose on
|
386 |
+
those licensors and authors.
|
387 |
+
|
388 |
+
All other non-permissive additional terms are considered "further
|
389 |
+
restrictions" within the meaning of section 10. If the Program as you
|
390 |
+
received it, or any part of it, contains a notice stating that it is
|
391 |
+
governed by this License along with a term that is a further
|
392 |
+
restriction, you may remove that term. If a license document contains
|
393 |
+
a further restriction but permits relicensing or conveying under this
|
394 |
+
License, you may add to a covered work material governed by the terms
|
395 |
+
of that license document, provided that the further restriction does
|
396 |
+
not survive such relicensing or conveying.
|
397 |
+
|
398 |
+
If you add terms to a covered work in accord with this section, you
|
399 |
+
must place, in the relevant source files, a statement of the
|
400 |
+
additional terms that apply to those files, or a notice indicating
|
401 |
+
where to find the applicable terms.
|
402 |
+
|
403 |
+
Additional terms, permissive or non-permissive, may be stated in the
|
404 |
+
form of a separately written license, or stated as exceptions;
|
405 |
+
the above requirements apply either way.
|
406 |
+
|
407 |
+
8. Termination.
|
408 |
+
|
409 |
+
You may not propagate or modify a covered work except as expressly
|
410 |
+
provided under this License. Any attempt otherwise to propagate or
|
411 |
+
modify it is void, and will automatically terminate your rights under
|
412 |
+
this License (including any patent licenses granted under the third
|
413 |
+
paragraph of section 11).
|
414 |
+
|
415 |
+
However, if you cease all violation of this License, then your
|
416 |
+
license from a particular copyright holder is reinstated (a)
|
417 |
+
provisionally, unless and until the copyright holder explicitly and
|
418 |
+
finally terminates your license, and (b) permanently, if the copyright
|
419 |
+
holder fails to notify you of the violation by some reasonable means
|
420 |
+
prior to 60 days after the cessation.
|
421 |
+
|
422 |
+
Moreover, your license from a particular copyright holder is
|
423 |
+
reinstated permanently if the copyright holder notifies you of the
|
424 |
+
violation by some reasonable means, this is the first time you have
|
425 |
+
received notice of violation of this License (for any work) from that
|
426 |
+
copyright holder, and you cure the violation prior to 30 days after
|
427 |
+
your receipt of the notice.
|
428 |
+
|
429 |
+
Termination of your rights under this section does not terminate the
|
430 |
+
licenses of parties who have received copies or rights from you under
|
431 |
+
this License. If your rights have been terminated and not permanently
|
432 |
+
reinstated, you do not qualify to receive new licenses for the same
|
433 |
+
material under section 10.
|
434 |
+
|
435 |
+
9. Acceptance Not Required for Having Copies.
|
436 |
+
|
437 |
+
You are not required to accept this License in order to receive or
|
438 |
+
run a copy of the Program. Ancillary propagation of a covered work
|
439 |
+
occurring solely as a consequence of using peer-to-peer transmission
|
440 |
+
to receive a copy likewise does not require acceptance. However,
|
441 |
+
nothing other than this License grants you permission to propagate or
|
442 |
+
modify any covered work. These actions infringe copyright if you do
|
443 |
+
not accept this License. Therefore, by modifying or propagating a
|
444 |
+
covered work, you indicate your acceptance of this License to do so.
|
445 |
+
|
446 |
+
10. Automatic Licensing of Downstream Recipients.
|
447 |
+
|
448 |
+
Each time you convey a covered work, the recipient automatically
|
449 |
+
receives a license from the original licensors, to run, modify and
|
450 |
+
propagate that work, subject to this License. You are not responsible
|
451 |
+
for enforcing compliance by third parties with this License.
|
452 |
+
|
453 |
+
An "entity transaction" is a transaction transferring control of an
|
454 |
+
organization, or substantially all assets of one, or subdividing an
|
455 |
+
organization, or merging organizations. If propagation of a covered
|
456 |
+
work results from an entity transaction, each party to that
|
457 |
+
transaction who receives a copy of the work also receives whatever
|
458 |
+
licenses to the work the party's predecessor in interest had or could
|
459 |
+
give under the previous paragraph, plus a right to possession of the
|
460 |
+
Corresponding Source of the work from the predecessor in interest, if
|
461 |
+
the predecessor has it or can get it with reasonable efforts.
|
462 |
+
|
463 |
+
You may not impose any further restrictions on the exercise of the
|
464 |
+
rights granted or affirmed under this License. For example, you may
|
465 |
+
not impose a license fee, royalty, or other charge for exercise of
|
466 |
+
rights granted under this License, and you may not initiate litigation
|
467 |
+
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
468 |
+
any patent claim is infringed by making, using, selling, offering for
|
469 |
+
sale, or importing the Program or any portion of it.
|
470 |
+
|
471 |
+
11. Patents.
|
472 |
+
|
473 |
+
A "contributor" is a copyright holder who authorizes use under this
|
474 |
+
License of the Program or a work on which the Program is based. The
|
475 |
+
work thus licensed is called the contributor's "contributor version".
|
476 |
+
|
477 |
+
A contributor's "essential patent claims" are all patent claims
|
478 |
+
owned or controlled by the contributor, whether already acquired or
|
479 |
+
hereafter acquired, that would be infringed by some manner, permitted
|
480 |
+
by this License, of making, using, or selling its contributor version,
|
481 |
+
but do not include claims that would be infringed only as a
|
482 |
+
consequence of further modification of the contributor version. For
|
483 |
+
purposes of this definition, "control" includes the right to grant
|
484 |
+
patent sublicenses in a manner consistent with the requirements of
|
485 |
+
this License.
|
486 |
+
|
487 |
+
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
488 |
+
patent license under the contributor's essential patent claims, to
|
489 |
+
make, use, sell, offer for sale, import and otherwise run, modify and
|
490 |
+
propagate the contents of its contributor version.
|
491 |
+
|
492 |
+
In the following three paragraphs, a "patent license" is any express
|
493 |
+
agreement or commitment, however denominated, not to enforce a patent
|
494 |
+
(such as an express permission to practice a patent or covenant not to
|
495 |
+
sue for patent infringement). To "grant" such a patent license to a
|
496 |
+
party means to make such an agreement or commitment not to enforce a
|
497 |
+
patent against the party.
|
498 |
+
|
499 |
+
If you convey a covered work, knowingly relying on a patent license,
|
500 |
+
and the Corresponding Source of the work is not available for anyone
|
501 |
+
to copy, free of charge and under the terms of this License, through a
|
502 |
+
publicly available network server or other readily accessible means,
|
503 |
+
then you must either (1) cause the Corresponding Source to be so
|
504 |
+
available, or (2) arrange to deprive yourself of the benefit of the
|
505 |
+
patent license for this particular work, or (3) arrange, in a manner
|
506 |
+
consistent with the requirements of this License, to extend the patent
|
507 |
+
license to downstream recipients. "Knowingly relying" means you have
|
508 |
+
actual knowledge that, but for the patent license, your conveying the
|
509 |
+
covered work in a country, or your recipient's use of the covered work
|
510 |
+
in a country, would infringe one or more identifiable patents in that
|
511 |
+
country that you have reason to believe are valid.
|
512 |
+
|
513 |
+
If, pursuant to or in connection with a single transaction or
|
514 |
+
arrangement, you convey, or propagate by procuring conveyance of, a
|
515 |
+
covered work, and grant a patent license to some of the parties
|
516 |
+
receiving the covered work authorizing them to use, propagate, modify
|
517 |
+
or convey a specific copy of the covered work, then the patent license
|
518 |
+
you grant is automatically extended to all recipients of the covered
|
519 |
+
work and works based on it.
|
520 |
+
|
521 |
+
A patent license is "discriminatory" if it does not include within
|
522 |
+
the scope of its coverage, prohibits the exercise of, or is
|
523 |
+
conditioned on the non-exercise of one or more of the rights that are
|
524 |
+
specifically granted under this License. You may not convey a covered
|
525 |
+
work if you are a party to an arrangement with a third party that is
|
526 |
+
in the business of distributing software, under which you make payment
|
527 |
+
to the third party based on the extent of your activity of conveying
|
528 |
+
the work, and under which the third party grants, to any of the
|
529 |
+
parties who would receive the covered work from you, a discriminatory
|
530 |
+
patent license (a) in connection with copies of the covered work
|
531 |
+
conveyed by you (or copies made from those copies), or (b) primarily
|
532 |
+
for and in connection with specific products or compilations that
|
533 |
+
contain the covered work, unless you entered into that arrangement,
|
534 |
+
or that patent license was granted, prior to 28 March 2007.
|
535 |
+
|
536 |
+
Nothing in this License shall be construed as excluding or limiting
|
537 |
+
any implied license or other defenses to infringement that may
|
538 |
+
otherwise be available to you under applicable patent law.
|
539 |
+
|
540 |
+
12. No Surrender of Others' Freedom.
|
541 |
+
|
542 |
+
If conditions are imposed on you (whether by court order, agreement or
|
543 |
+
otherwise) that contradict the conditions of this License, they do not
|
544 |
+
excuse you from the conditions of this License. If you cannot convey a
|
545 |
+
covered work so as to satisfy simultaneously your obligations under this
|
546 |
+
License and any other pertinent obligations, then as a consequence you may
|
547 |
+
not convey it at all. For example, if you agree to terms that obligate you
|
548 |
+
to collect a royalty for further conveying from those to whom you convey
|
549 |
+
the Program, the only way you could satisfy both those terms and this
|
550 |
+
License would be to refrain entirely from conveying the Program.
|
551 |
+
|
552 |
+
13. Use with the GNU Affero General Public License.
|
553 |
+
|
554 |
+
Notwithstanding any other provision of this License, you have
|
555 |
+
permission to link or combine any covered work with a work licensed
|
556 |
+
under version 3 of the GNU Affero General Public License into a single
|
557 |
+
combined work, and to convey the resulting work. The terms of this
|
558 |
+
License will continue to apply to the part which is the covered work,
|
559 |
+
but the special requirements of the GNU Affero General Public License,
|
560 |
+
section 13, concerning interaction through a network will apply to the
|
561 |
+
combination as such.
|
562 |
+
|
563 |
+
14. Revised Versions of this License.
|
564 |
+
|
565 |
+
The Free Software Foundation may publish revised and/or new versions of
|
566 |
+
the GNU General Public License from time to time. Such new versions will
|
567 |
+
be similar in spirit to the present version, but may differ in detail to
|
568 |
+
address new problems or concerns.
|
569 |
+
|
570 |
+
Each version is given a distinguishing version number. If the
|
571 |
+
Program specifies that a certain numbered version of the GNU General
|
572 |
+
Public License "or any later version" applies to it, you have the
|
573 |
+
option of following the terms and conditions either of that numbered
|
574 |
+
version or of any later version published by the Free Software
|
575 |
+
Foundation. If the Program does not specify a version number of the
|
576 |
+
GNU General Public License, you may choose any version ever published
|
577 |
+
by the Free Software Foundation.
|
578 |
+
|
579 |
+
If the Program specifies that a proxy can decide which future
|
580 |
+
versions of the GNU General Public License can be used, that proxy's
|
581 |
+
public statement of acceptance of a version permanently authorizes you
|
582 |
+
to choose that version for the Program.
|
583 |
+
|
584 |
+
Later license versions may give you additional or different
|
585 |
+
permissions. However, no additional obligations are imposed on any
|
586 |
+
author or copyright holder as a result of your choosing to follow a
|
587 |
+
later version.
|
588 |
+
|
589 |
+
15. Disclaimer of Warranty.
|
590 |
+
|
591 |
+
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
592 |
+
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
593 |
+
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
594 |
+
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
595 |
+
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
596 |
+
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
597 |
+
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
598 |
+
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
599 |
+
|
600 |
+
16. Limitation of Liability.
|
601 |
+
|
602 |
+
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
603 |
+
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
604 |
+
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
605 |
+
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
606 |
+
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
607 |
+
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
608 |
+
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
609 |
+
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
610 |
+
SUCH DAMAGES.
|
611 |
+
|
612 |
+
17. Interpretation of Sections 15 and 16.
|
613 |
+
|
614 |
+
If the disclaimer of warranty and limitation of liability provided
|
615 |
+
above cannot be given local legal effect according to their terms,
|
616 |
+
reviewing courts shall apply local law that most closely approximates
|
617 |
+
an absolute waiver of all civil liability in connection with the
|
618 |
+
Program, unless a warranty or assumption of liability accompanies a
|
619 |
+
copy of the Program in return for a fee.
|
620 |
+
|
621 |
+
END OF TERMS AND CONDITIONS
|
622 |
+
|
623 |
+
How to Apply These Terms to Your New Programs
|
624 |
+
|
625 |
+
If you develop a new program, and you want it to be of the greatest
|
626 |
+
possible use to the public, the best way to achieve this is to make it
|
627 |
+
free software which everyone can redistribute and change under these terms.
|
628 |
+
|
629 |
+
To do so, attach the following notices to the program. It is safest
|
630 |
+
to attach them to the start of each source file to most effectively
|
631 |
+
state the exclusion of warranty; and each file should have at least
|
632 |
+
the "copyright" line and a pointer to where the full notice is found.
|
633 |
+
|
634 |
+
<one line to give the program's name and a brief idea of what it does.>
|
635 |
+
Copyright (C) <year> <name of author>
|
636 |
+
|
637 |
+
This program is free software: you can redistribute it and/or modify
|
638 |
+
it under the terms of the GNU General Public License as published by
|
639 |
+
the Free Software Foundation, either version 3 of the License, or
|
640 |
+
(at your option) any later version.
|
641 |
+
|
642 |
+
This program is distributed in the hope that it will be useful,
|
643 |
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
644 |
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
645 |
+
GNU General Public License for more details.
|
646 |
+
|
647 |
+
You should have received a copy of the GNU General Public License
|
648 |
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
649 |
+
|
650 |
+
Also add information on how to contact you by electronic and paper mail.
|
651 |
+
|
652 |
+
If the program does terminal interaction, make it output a short
|
653 |
+
notice like this when it starts in an interactive mode:
|
654 |
+
|
655 |
+
<program> Copyright (C) <year> <name of author>
|
656 |
+
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
657 |
+
This is free software, and you are welcome to redistribute it
|
658 |
+
under certain conditions; type `show c' for details.
|
659 |
+
|
660 |
+
The hypothetical commands `show w' and `show c' should show the appropriate
|
661 |
+
parts of the General Public License. Of course, your program's commands
|
662 |
+
might be different; for a GUI interface, you would use an "about box".
|
663 |
+
|
664 |
+
You should also get your employer (if you work as a programmer) or school,
|
665 |
+
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
666 |
+
For more information on this, and how to apply and follow the GNU GPL, see
|
667 |
+
<http://www.gnu.org/licenses/>.
|
668 |
+
|
669 |
+
The GNU General Public License does not permit incorporating your program
|
670 |
+
into proprietary programs. If your program is a subroutine library, you
|
671 |
+
may consider it more useful to permit linking proprietary applications with
|
672 |
+
the library. If this is what you want to do, use the GNU Lesser General
|
673 |
+
Public License instead of this License. But first, please read
|
674 |
+
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
Planogram_compliance_inference.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
Procfile
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
web: sh setup.sh && streamlit run app.py
|
README.md
CHANGED
@@ -1,13 +1,166 @@
|
|
1 |
---
|
2 |
-
title: Plano Lit
|
3 |
-
emoji: 🚀
|
4 |
-
colorFrom: purple
|
5 |
-
colorTo: red
|
6 |
sdk: streamlit
|
7 |
-
sdk_version: 1.
|
8 |
app_file: app.py
|
9 |
pinned: false
|
10 |
-
|
11 |
---
|
|
|
|
|
12 |
|
13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
---
|
|
|
|
|
|
|
|
|
2 |
sdk: streamlit
|
3 |
+
sdk_version: 1.10.0 # The latest supported version
|
4 |
app_file: app.py
|
5 |
pinned: false
|
6 |
+
fullWidth: True
|
7 |
---
|
8 |
+
## <div align="center">Planogram Scoring</div>
|
9 |
+
<p>
|
10 |
|
11 |
+
</p>
|
12 |
+
- Train a Yolo Model on the available products in our data base to detect them on a shelf
|
13 |
+
- https://wandb.ai/abhilash001vj/YOLOv5/runs/1v6yh7nk?workspace=user-abhilash001vj
|
14 |
+
- Have the master planogram data captured as a matrix of products encoded as numbers (label encoding by looking the products names saved in a list of all - the available product names )
|
15 |
+
- Detect the products on real images from stores.
|
16 |
+
- Arrange the detected products in the captured photograph to rows and columns
|
17 |
+
- Compare the product arrangement of captured photograph to the existing master planogram and produce the compliance score for correctly placed products
|
18 |
+
|
19 |
+
</div>
|
20 |
+
|
21 |
+
## <div align="center">YOLOv5</div>
|
22 |
+
<p>
|
23 |
+
YOLOv5 🚀 is a family of object detection architectures and models pretrained on the COCO dataset, and represents <a href="https://ultralytics.com">Ultralytics</a>
|
24 |
+
open-source research into future vision AI methods, incorporating lessons learned and best practices evolved over thousands of hours of research and development.
|
25 |
+
</p>
|
26 |
+
|
27 |
+
</div>
|
28 |
+
|
29 |
+
## <div align="center">Documentation</div>
|
30 |
+
|
31 |
+
See the [YOLOv5 Docs](https://docs.ultralytics.com) for full documentation on training, testing and deployment.
|
32 |
+
|
33 |
+
## <div align="center">Quick Start Examples</div>
|
34 |
+
|
35 |
+
<details open>
|
36 |
+
<summary>Install</summary>
|
37 |
+
|
38 |
+
[**Python>=3.6.0**](https://www.python.org/) is required with all
|
39 |
+
[requirements.txt](https://github.com/ultralytics/yolov5/blob/master/requirements.txt) installed including
|
40 |
+
[**PyTorch>=1.7**](https://pytorch.org/get-started/locally/):
|
41 |
+
<!-- $ sudo apt update && apt install -y libgl1-mesa-glx libsm6 libxext6 libxrender-dev -->
|
42 |
+
|
43 |
+
```bash
|
44 |
+
$ git clone https://github.com/ultralytics/yolov5
|
45 |
+
$ cd yolov5
|
46 |
+
$ pip install -r requirements.txt
|
47 |
+
```
|
48 |
+
|
49 |
+
</details>
|
50 |
+
|
51 |
+
<details open>
|
52 |
+
<summary>Inference</summary>
|
53 |
+
|
54 |
+
Inference with YOLOv5 and [PyTorch Hub](https://github.com/ultralytics/yolov5/issues/36). Models automatically download
|
55 |
+
from the [latest YOLOv5 release](https://github.com/ultralytics/yolov5/releases).
|
56 |
+
|
57 |
+
```python
|
58 |
+
import torch
|
59 |
+
|
60 |
+
# Model
|
61 |
+
model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # or yolov5m, yolov5l, yolov5x, custom
|
62 |
+
|
63 |
+
# Images
|
64 |
+
img = 'https://ultralytics.com/images/zidane.jpg' # or file, Path, PIL, OpenCV, numpy, list
|
65 |
+
|
66 |
+
# Inference
|
67 |
+
results = model(img)
|
68 |
+
|
69 |
+
# Results
|
70 |
+
results.print() # or .show(), .save(), .crop(), .pandas(), etc.
|
71 |
+
```
|
72 |
+
|
73 |
+
</details>
|
74 |
+
|
75 |
+
|
76 |
+
## <div align="center">Why YOLOv5</div>
|
77 |
+
|
78 |
+
<p align="center"><img width="800" src="https://user-images.githubusercontent.com/26833433/114313216-f0a5e100-9af5-11eb-8445-c682b60da2e3.png"></p>
|
79 |
+
<details>
|
80 |
+
<summary>YOLOv5-P5 640 Figure (click to expand)</summary>
|
81 |
+
|
82 |
+
<p align="center"><img width="800" src="https://user-images.githubusercontent.com/26833433/114313219-f1d70e00-9af5-11eb-9973-52b1f98d321a.png"></p>
|
83 |
+
</details>
|
84 |
+
<details>
|
85 |
+
<summary>Figure Notes (click to expand)</summary>
|
86 |
+
|
87 |
+
* GPU Speed measures end-to-end time per image averaged over 5000 COCO val2017 images using a V100 GPU with batch size
|
88 |
+
32, and includes image preprocessing, PyTorch FP16 inference, postprocessing and NMS.
|
89 |
+
* EfficientDet data from [google/automl](https://github.com/google/automl) at batch size 8.
|
90 |
+
* **Reproduce** by
|
91 |
+
`python val.py --task study --data coco.yaml --iou 0.7 --weights yolov5s6.pt yolov5m6.pt yolov5l6.pt yolov5x6.pt`
|
92 |
+
|
93 |
+
</details>
|
94 |
+
|
95 |
+
### Pretrained Checkpoints
|
96 |
+
|
97 |
+
[assets]: https://github.com/ultralytics/yolov5/releases
|
98 |
+
|
99 |
+
|Model |size<br><sup>(pixels) |mAP<sup>val<br>0.5:0.95 |mAP<sup>test<br>0.5:0.95 |mAP<sup>val<br>0.5 |Speed<br><sup>V100 (ms) | |params<br><sup>(M) |FLOPs<br><sup>640 (B)
|
100 |
+
|--- |--- |--- |--- |--- |--- |---|--- |---
|
101 |
+
|[YOLOv5s][assets] |640 |36.7 |36.7 |55.4 |**2.0** | |7.3 |17.0
|
102 |
+
|[YOLOv5m][assets] |640 |44.5 |44.5 |63.1 |2.7 | |21.4 |51.3
|
103 |
+
|[YOLOv5l][assets] |640 |48.2 |48.2 |66.9 |3.8 | |47.0 |115.4
|
104 |
+
|[YOLOv5x][assets] |640 |**50.4** |**50.4** |**68.8** |6.1 | |87.7 |218.8
|
105 |
+
| | | | | | | | |
|
106 |
+
|[YOLOv5s6][assets] |1280 |43.3 |43.3 |61.9 |**4.3** | |12.7 |17.4
|
107 |
+
|[YOLOv5m6][assets] |1280 |50.5 |50.5 |68.7 |8.4 | |35.9 |52.4
|
108 |
+
|[YOLOv5l6][assets] |1280 |53.4 |53.4 |71.1 |12.3 | |77.2 |117.7
|
109 |
+
|[YOLOv5x6][assets] |1280 |**54.4** |**54.4** |**72.0** |22.4 | |141.8 |222.9
|
110 |
+
| | | | | | | | |
|
111 |
+
|[YOLOv5x6][assets] TTA |1280 |**55.0** |**55.0** |**72.0** |70.8 | |- |-
|
112 |
+
|
113 |
+
<details>
|
114 |
+
<summary>Table Notes (click to expand)</summary>
|
115 |
+
|
116 |
+
* AP<sup>test</sup> denotes COCO [test-dev2017](http://cocodataset.org/#upload) server results, all other AP results
|
117 |
+
denote val2017 accuracy.
|
118 |
+
* AP values are for single-model single-scale unless otherwise noted. **Reproduce mAP**
|
119 |
+
by `python val.py --data coco.yaml --img 640 --conf 0.001 --iou 0.65`
|
120 |
+
* Speed<sub>GPU</sub> averaged over 5000 COCO val2017 images using a
|
121 |
+
GCP [n1-standard-16](https://cloud.google.com/compute/docs/machine-types#n1_standard_machine_types) V100 instance, and
|
122 |
+
includes FP16 inference, postprocessing and NMS. **Reproduce speed**
|
123 |
+
by `python val.py --data coco.yaml --img 640 --conf 0.25 --iou 0.45 --half`
|
124 |
+
* All checkpoints are trained to 300 epochs with default settings and hyperparameters (no autoaugmentation).
|
125 |
+
* Test Time Augmentation ([TTA](https://github.com/ultralytics/yolov5/issues/303)) includes reflection and scale
|
126 |
+
augmentation. **Reproduce TTA** by `python val.py --data coco.yaml --img 1536 --iou 0.7 --augment`
|
127 |
+
|
128 |
+
</details>
|
129 |
+
|
130 |
+
## <div align="center">Contribute</div>
|
131 |
+
|
132 |
+
We love your input! We want to make contributing to YOLOv5 as easy and transparent as possible. Please see
|
133 |
+
our [Contributing Guide](CONTRIBUTING.md) to get started.
|
134 |
+
|
135 |
+
## <div align="center">Contact</div>
|
136 |
+
|
137 |
+
For issues running YOLOv5 please visit [GitHub Issues](https://github.com/ultralytics/yolov5/issues). For business or
|
138 |
+
professional support requests please visit [https://ultralytics.com/contact](https://ultralytics.com/contact).
|
139 |
+
|
140 |
+
<br>
|
141 |
+
|
142 |
+
<div align="center">
|
143 |
+
<a href="https://github.com/ultralytics">
|
144 |
+
<img src="https://github.com/ultralytics/yolov5/releases/download/v1.0/logo-social-github.png" width="3%"/>
|
145 |
+
</a>
|
146 |
+
<img width="3%" />
|
147 |
+
<a href="https://www.linkedin.com/company/ultralytics">
|
148 |
+
<img src="https://github.com/ultralytics/yolov5/releases/download/v1.0/logo-social-linkedin.png" width="3%"/>
|
149 |
+
</a>
|
150 |
+
<img width="3%" />
|
151 |
+
<a href="https://twitter.com/ultralytics">
|
152 |
+
<img src="https://github.com/ultralytics/yolov5/releases/download/v1.0/logo-social-twitter.png" width="3%"/>
|
153 |
+
</a>
|
154 |
+
<img width="3%" />
|
155 |
+
<a href="https://youtube.com/ultralytics">
|
156 |
+
<img src="https://github.com/ultralytics/yolov5/releases/download/v1.0/logo-social-youtube.png" width="3%"/>
|
157 |
+
</a>
|
158 |
+
<img width="3%" />
|
159 |
+
<a href="https://www.facebook.com/ultralytics">
|
160 |
+
<img src="https://github.com/ultralytics/yolov5/releases/download/v1.0/logo-social-facebook.png" width="3%"/>
|
161 |
+
</a>
|
162 |
+
<img width="3%" />
|
163 |
+
<a href="https://www.instagram.com/ultralytics/">
|
164 |
+
<img src="https://github.com/ultralytics/yolov5/releases/download/v1.0/logo-social-instagram.png" width="3%"/>
|
165 |
+
</a>
|
166 |
+
</div>
|
_requirements.txt
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# pip install -r requirements.txt
|
2 |
+
streamlit
|
3 |
+
# base ----------------------------------------
|
4 |
+
# matplotlib>=3.2.2
|
5 |
+
numpy>=1.18.5
|
6 |
+
# opencv-python>=4.1.2
|
7 |
+
# http://download.pytorch.org/whl/cpu/torch-1.7.1%2Bcpu-cp39-cp39-linux_x86_64.whl
|
8 |
+
# gunicorn == 19.9.0
|
9 |
+
# torchvision==0.2.2
|
10 |
+
opencv-python-headless>=4.1.2
|
11 |
+
Pillow>=8.0.0
|
12 |
+
PyYAML>=5.3.1
|
13 |
+
scipy>=1.4.1
|
14 |
+
torch>=1.7.0
|
15 |
+
torchvision>=0.8.1
|
16 |
+
tqdm>=4.41.0
|
17 |
+
|
18 |
+
# logging -------------------------------------
|
19 |
+
# tensorboard>=2.4.1
|
20 |
+
# wandb
|
21 |
+
|
22 |
+
# plotting ------------------------------------
|
23 |
+
# seaborn>=0.11.0
|
24 |
+
pandas
|
25 |
+
|
26 |
+
# export --------------------------------------
|
27 |
+
# coremltools>=4.1
|
28 |
+
# onnx>=1.9.0
|
29 |
+
# scikit-learn==0.19.2 # for coreml quantization
|
30 |
+
# tensorflow==2.4.1 # for TFLite export
|
31 |
+
|
32 |
+
# extras --------------------------------------
|
33 |
+
# Cython # for pycocotools https://github.com/cocodataset/cocoapi/issues/172
|
34 |
+
# pycocotools>=2.0 # COCO mAP
|
35 |
+
# albumentations>=1.0.3
|
36 |
+
# thop # FLOPs computation
|
app_test.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
detect.py
ADDED
@@ -0,0 +1,460 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
2 |
+
"""
|
3 |
+
Run YOLOv5 detection inference on images, videos, directories, globs, YouTube, webcam, streams, etc.
|
4 |
+
|
5 |
+
Usage - sources:
|
6 |
+
$ python detect.py --weights yolov5s.pt --source 0 # webcam
|
7 |
+
img.jpg # image
|
8 |
+
vid.mp4 # video
|
9 |
+
screen # screenshot
|
10 |
+
path/ # directory
|
11 |
+
list.txt # list of images
|
12 |
+
list.streams # list of streams
|
13 |
+
'path/*.jpg' # glob
|
14 |
+
'https://youtu.be/Zgi9g1ksQHc' # YouTube
|
15 |
+
'rtsp://example.com/media.mp4' # RTSP, RTMP, HTTP stream
|
16 |
+
|
17 |
+
Usage - formats:
|
18 |
+
$ python detect.py --weights yolov5s.pt # PyTorch
|
19 |
+
yolov5s.torchscript # TorchScript
|
20 |
+
yolov5s.onnx # ONNX Runtime or OpenCV DNN with --dnn
|
21 |
+
yolov5s_openvino_model # OpenVINO
|
22 |
+
yolov5s.engine # TensorRT
|
23 |
+
yolov5s.mlmodel # CoreML (macOS-only)
|
24 |
+
yolov5s_saved_model # TensorFlow SavedModel
|
25 |
+
yolov5s.pb # TensorFlow GraphDef
|
26 |
+
yolov5s.tflite # TensorFlow Lite
|
27 |
+
yolov5s_edgetpu.tflite # TensorFlow Edge TPU
|
28 |
+
yolov5s_paddle_model # PaddlePaddle
|
29 |
+
"""
|
30 |
+
|
31 |
+
import argparse
|
32 |
+
import os
|
33 |
+
import platform
|
34 |
+
import sys
|
35 |
+
from pathlib import Path
|
36 |
+
|
37 |
+
import torch
|
38 |
+
|
39 |
+
FILE = Path(__file__).resolve()
|
40 |
+
ROOT = FILE.parents[0] # YOLOv5 root directory
|
41 |
+
if str(ROOT) not in sys.path:
|
42 |
+
sys.path.append(str(ROOT)) # add ROOT to PATH
|
43 |
+
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
44 |
+
|
45 |
+
from models.common import DetectMultiBackend
|
46 |
+
from utils.dataloaders import (
|
47 |
+
IMG_FORMATS,
|
48 |
+
VID_FORMATS,
|
49 |
+
LoadImages,
|
50 |
+
LoadScreenshots,
|
51 |
+
LoadStreams,
|
52 |
+
)
|
53 |
+
from utils.general import (
|
54 |
+
LOGGER,
|
55 |
+
Profile,
|
56 |
+
check_file,
|
57 |
+
check_img_size,
|
58 |
+
check_imshow,
|
59 |
+
check_requirements,
|
60 |
+
colorstr,
|
61 |
+
cv2,
|
62 |
+
increment_path,
|
63 |
+
non_max_suppression,
|
64 |
+
print_args,
|
65 |
+
scale_boxes,
|
66 |
+
strip_optimizer,
|
67 |
+
xyxy2xywh,
|
68 |
+
)
|
69 |
+
from utils.plots import Annotator, colors, save_one_box
|
70 |
+
from utils.torch_utils import select_device, smart_inference_mode
|
71 |
+
|
72 |
+
|
73 |
+
@smart_inference_mode()
|
74 |
+
def run(
|
75 |
+
weights=ROOT / "yolov5s.pt", # model path or triton URL
|
76 |
+
source=ROOT / "data/images", # file/dir/URL/glob/screen/0(webcam)
|
77 |
+
data=ROOT / "data/coco128.yaml", # dataset.yaml path
|
78 |
+
imgsz=(640, 640), # inference size (height, width)
|
79 |
+
conf_thres=0.25, # confidence threshold
|
80 |
+
iou_thres=0.45, # NMS IOU threshold
|
81 |
+
max_det=1000, # maximum detections per image
|
82 |
+
device="", # cuda device, i.e. 0 or 0,1,2,3 or cpu
|
83 |
+
view_img=False, # show results
|
84 |
+
save_txt=False, # save results to *.txt
|
85 |
+
save_conf=False, # save confidences in --save-txt labels
|
86 |
+
save_crop=False, # save cropped prediction boxes
|
87 |
+
nosave=False, # do not save images/videos
|
88 |
+
classes=None, # filter by class: --class 0, or --class 0 2 3
|
89 |
+
agnostic_nms=False, # class-agnostic NMS
|
90 |
+
augment=False, # augmented inference
|
91 |
+
visualize=False, # visualize features
|
92 |
+
update=False, # update all models
|
93 |
+
project=ROOT / "runs/detect", # save results to project/name
|
94 |
+
name="exp", # save results to project/name
|
95 |
+
exist_ok=False, # existing project/name ok, do not increment
|
96 |
+
line_thickness=3, # bounding box thickness (pixels)
|
97 |
+
hide_labels=False, # hide labels
|
98 |
+
hide_conf=False, # hide confidences
|
99 |
+
half=False, # use FP16 half-precision inference
|
100 |
+
dnn=False, # use OpenCV DNN for ONNX inference
|
101 |
+
vid_stride=1, # video frame-rate stride
|
102 |
+
):
|
103 |
+
source = str(source)
|
104 |
+
save_img = not nosave and not source.endswith(
|
105 |
+
".txt"
|
106 |
+
) # save inference images
|
107 |
+
is_file = Path(source).suffix[1:] in (IMG_FORMATS + VID_FORMATS)
|
108 |
+
is_url = source.lower().startswith(
|
109 |
+
("rtsp://", "rtmp://", "http://", "https://")
|
110 |
+
)
|
111 |
+
webcam = (
|
112 |
+
source.isnumeric()
|
113 |
+
or source.endswith(".streams")
|
114 |
+
or (is_url and not is_file)
|
115 |
+
)
|
116 |
+
screenshot = source.lower().startswith("screen")
|
117 |
+
if is_url and is_file:
|
118 |
+
source = check_file(source) # download
|
119 |
+
|
120 |
+
# Directories
|
121 |
+
save_dir = increment_path(
|
122 |
+
Path(project) / name, exist_ok=exist_ok
|
123 |
+
) # increment run
|
124 |
+
(save_dir / "labels" if save_txt else save_dir).mkdir(
|
125 |
+
parents=True, exist_ok=True
|
126 |
+
) # make dir
|
127 |
+
|
128 |
+
# Load model
|
129 |
+
device = select_device(device)
|
130 |
+
model = DetectMultiBackend(
|
131 |
+
weights, device=device, dnn=dnn, data=data, fp16=half
|
132 |
+
)
|
133 |
+
stride, names, pt = model.stride, model.names, model.pt
|
134 |
+
imgsz = check_img_size(imgsz, s=stride) # check image size
|
135 |
+
|
136 |
+
# Dataloader
|
137 |
+
bs = 1 # batch_size
|
138 |
+
if webcam:
|
139 |
+
view_img = check_imshow(warn=True)
|
140 |
+
dataset = LoadStreams(
|
141 |
+
source,
|
142 |
+
img_size=imgsz,
|
143 |
+
stride=stride,
|
144 |
+
auto=pt,
|
145 |
+
vid_stride=vid_stride,
|
146 |
+
)
|
147 |
+
bs = len(dataset)
|
148 |
+
elif screenshot:
|
149 |
+
dataset = LoadScreenshots(
|
150 |
+
source, img_size=imgsz, stride=stride, auto=pt
|
151 |
+
)
|
152 |
+
else:
|
153 |
+
dataset = LoadImages(
|
154 |
+
source,
|
155 |
+
img_size=imgsz,
|
156 |
+
stride=stride,
|
157 |
+
auto=pt,
|
158 |
+
vid_stride=vid_stride,
|
159 |
+
)
|
160 |
+
vid_path, vid_writer = [None] * bs, [None] * bs
|
161 |
+
|
162 |
+
# Run inference
|
163 |
+
model.warmup(imgsz=(1 if pt or model.triton else bs, 3, *imgsz)) # warmup
|
164 |
+
seen, windows, dt = 0, [], (Profile(), Profile(), Profile())
|
165 |
+
for path, im, im0s, vid_cap, s in dataset:
|
166 |
+
with dt[0]:
|
167 |
+
im = torch.from_numpy(im).to(model.device)
|
168 |
+
im = im.half() if model.fp16 else im.float() # uint8 to fp16/32
|
169 |
+
im /= 255 # 0 - 255 to 0.0 - 1.0
|
170 |
+
if len(im.shape) == 3:
|
171 |
+
im = im[None] # expand for batch dim
|
172 |
+
|
173 |
+
# Inference
|
174 |
+
with dt[1]:
|
175 |
+
visualize = (
|
176 |
+
increment_path(save_dir / Path(path).stem, mkdir=True)
|
177 |
+
if visualize
|
178 |
+
else False
|
179 |
+
)
|
180 |
+
pred = model(im, augment=augment, visualize=visualize)
|
181 |
+
|
182 |
+
# NMS
|
183 |
+
with dt[2]:
|
184 |
+
pred = non_max_suppression(
|
185 |
+
pred,
|
186 |
+
conf_thres,
|
187 |
+
iou_thres,
|
188 |
+
classes,
|
189 |
+
agnostic_nms,
|
190 |
+
max_det=max_det,
|
191 |
+
)
|
192 |
+
|
193 |
+
# Second-stage classifier (optional)
|
194 |
+
# pred = utils.general.apply_classifier(pred, classifier_model, im, im0s)
|
195 |
+
|
196 |
+
# Process predictions
|
197 |
+
for i, det in enumerate(pred): # per image
|
198 |
+
seen += 1
|
199 |
+
if webcam: # batch_size >= 1
|
200 |
+
p, im0, frame = path[i], im0s[i].copy(), dataset.count
|
201 |
+
s += f"{i}: "
|
202 |
+
else:
|
203 |
+
p, im0, frame = path, im0s.copy(), getattr(dataset, "frame", 0)
|
204 |
+
|
205 |
+
p = Path(p) # to Path
|
206 |
+
save_path = str(save_dir / p.name) # im.jpg
|
207 |
+
txt_path = str(save_dir / "labels" / p.stem) + (
|
208 |
+
"" if dataset.mode == "image" else f"_{frame}"
|
209 |
+
) # im.txt
|
210 |
+
s += "%gx%g " % im.shape[2:] # print string
|
211 |
+
gn = torch.tensor(im0.shape)[
|
212 |
+
[1, 0, 1, 0]
|
213 |
+
] # normalization gain whwh
|
214 |
+
imc = im0.copy() if save_crop else im0 # for save_crop
|
215 |
+
annotator = Annotator(
|
216 |
+
im0, line_width=line_thickness, example=str(names)
|
217 |
+
)
|
218 |
+
if len(det):
|
219 |
+
# Rescale boxes from img_size to im0 size
|
220 |
+
det[:, :4] = scale_boxes(
|
221 |
+
im.shape[2:], det[:, :4], im0.shape
|
222 |
+
).round()
|
223 |
+
|
224 |
+
# Print results
|
225 |
+
for c in det[:, 5].unique():
|
226 |
+
n = (det[:, 5] == c).sum() # detections per class
|
227 |
+
s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string
|
228 |
+
|
229 |
+
# Write results
|
230 |
+
for *xyxy, conf, cls in reversed(det):
|
231 |
+
if save_txt: # Write to file
|
232 |
+
xywh = (
|
233 |
+
(xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn)
|
234 |
+
.view(-1)
|
235 |
+
.tolist()
|
236 |
+
) # normalized xywh
|
237 |
+
line = (
|
238 |
+
(cls, *xywh, conf) if save_conf else (cls, *xywh)
|
239 |
+
) # label format
|
240 |
+
with open(f"{txt_path}.txt", "a") as f:
|
241 |
+
f.write(("%g " * len(line)).rstrip() % line + "\n")
|
242 |
+
|
243 |
+
if save_img or save_crop or view_img: # Add bbox to image
|
244 |
+
c = int(cls) # integer class
|
245 |
+
label = (
|
246 |
+
None
|
247 |
+
if hide_labels
|
248 |
+
else (
|
249 |
+
names[c]
|
250 |
+
if hide_conf
|
251 |
+
else f"{names[c]} {conf:.2f}"
|
252 |
+
)
|
253 |
+
)
|
254 |
+
annotator.box_label(xyxy, label, color=colors(c, True))
|
255 |
+
if save_crop:
|
256 |
+
save_one_box(
|
257 |
+
xyxy,
|
258 |
+
imc,
|
259 |
+
file=save_dir
|
260 |
+
/ "crops"
|
261 |
+
/ names[c]
|
262 |
+
/ f"{p.stem}.jpg",
|
263 |
+
BGR=True,
|
264 |
+
)
|
265 |
+
|
266 |
+
# Stream results
|
267 |
+
im0 = annotator.result()
|
268 |
+
if view_img:
|
269 |
+
if platform.system() == "Linux" and p not in windows:
|
270 |
+
windows.append(p)
|
271 |
+
cv2.namedWindow(
|
272 |
+
str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO
|
273 |
+
) # allow window resize (Linux)
|
274 |
+
cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0])
|
275 |
+
cv2.imshow(str(p), im0)
|
276 |
+
cv2.waitKey(1) # 1 millisecond
|
277 |
+
|
278 |
+
# Save results (image with detections)
|
279 |
+
if save_img:
|
280 |
+
if dataset.mode == "image":
|
281 |
+
cv2.imwrite(save_path, im0)
|
282 |
+
else: # 'video' or 'stream'
|
283 |
+
if vid_path[i] != save_path: # new video
|
284 |
+
vid_path[i] = save_path
|
285 |
+
if isinstance(vid_writer[i], cv2.VideoWriter):
|
286 |
+
vid_writer[
|
287 |
+
i
|
288 |
+
].release() # release previous video writer
|
289 |
+
if vid_cap: # video
|
290 |
+
fps = vid_cap.get(cv2.CAP_PROP_FPS)
|
291 |
+
w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
292 |
+
h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
293 |
+
else: # stream
|
294 |
+
fps, w, h = 30, im0.shape[1], im0.shape[0]
|
295 |
+
save_path = str(
|
296 |
+
Path(save_path).with_suffix(".mp4")
|
297 |
+
) # force *.mp4 suffix on results videos
|
298 |
+
vid_writer[i] = cv2.VideoWriter(
|
299 |
+
save_path,
|
300 |
+
cv2.VideoWriter_fourcc(*"mp4v"),
|
301 |
+
fps,
|
302 |
+
(w, h),
|
303 |
+
)
|
304 |
+
vid_writer[i].write(im0)
|
305 |
+
|
306 |
+
# Print time (inference-only)
|
307 |
+
LOGGER.info(
|
308 |
+
f"{s}{'' if len(det) else '(no detections), '}{dt[1].dt * 1E3:.1f}ms"
|
309 |
+
)
|
310 |
+
|
311 |
+
# Print results
|
312 |
+
t = tuple(x.t / seen * 1e3 for x in dt) # speeds per image
|
313 |
+
LOGGER.info(
|
314 |
+
f"Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}"
|
315 |
+
% t
|
316 |
+
)
|
317 |
+
if save_txt or save_img:
|
318 |
+
s = (
|
319 |
+
f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}"
|
320 |
+
if save_txt
|
321 |
+
else ""
|
322 |
+
)
|
323 |
+
LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}{s}")
|
324 |
+
if update:
|
325 |
+
strip_optimizer(
|
326 |
+
weights[0]
|
327 |
+
) # update model (to fix SourceChangeWarning)
|
328 |
+
|
329 |
+
|
330 |
+
def parse_opt():
|
331 |
+
parser = argparse.ArgumentParser()
|
332 |
+
parser.add_argument(
|
333 |
+
"--weights",
|
334 |
+
nargs="+",
|
335 |
+
type=str,
|
336 |
+
default=ROOT / "yolov5s.pt",
|
337 |
+
help="model path or triton URL",
|
338 |
+
)
|
339 |
+
parser.add_argument(
|
340 |
+
"--source",
|
341 |
+
type=str,
|
342 |
+
default=ROOT / "data/images",
|
343 |
+
help="file/dir/URL/glob/screen/0(webcam)",
|
344 |
+
)
|
345 |
+
parser.add_argument(
|
346 |
+
"--data",
|
347 |
+
type=str,
|
348 |
+
default=ROOT / "data/coco128.yaml",
|
349 |
+
help="(optional) dataset.yaml path",
|
350 |
+
)
|
351 |
+
parser.add_argument(
|
352 |
+
"--imgsz",
|
353 |
+
"--img",
|
354 |
+
"--img-size",
|
355 |
+
nargs="+",
|
356 |
+
type=int,
|
357 |
+
default=[640],
|
358 |
+
help="inference size h,w",
|
359 |
+
)
|
360 |
+
parser.add_argument(
|
361 |
+
"--conf-thres", type=float, default=0.25, help="confidence threshold"
|
362 |
+
)
|
363 |
+
parser.add_argument(
|
364 |
+
"--iou-thres", type=float, default=0.45, help="NMS IoU threshold"
|
365 |
+
)
|
366 |
+
parser.add_argument(
|
367 |
+
"--max-det",
|
368 |
+
type=int,
|
369 |
+
default=1000,
|
370 |
+
help="maximum detections per image",
|
371 |
+
)
|
372 |
+
parser.add_argument(
|
373 |
+
"--device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu"
|
374 |
+
)
|
375 |
+
parser.add_argument("--view-img", action="store_true", help="show results")
|
376 |
+
parser.add_argument(
|
377 |
+
"--save-txt", action="store_true", help="save results to *.txt"
|
378 |
+
)
|
379 |
+
parser.add_argument(
|
380 |
+
"--save-conf",
|
381 |
+
action="store_true",
|
382 |
+
help="save confidences in --save-txt labels",
|
383 |
+
)
|
384 |
+
parser.add_argument(
|
385 |
+
"--save-crop",
|
386 |
+
action="store_true",
|
387 |
+
help="save cropped prediction boxes",
|
388 |
+
)
|
389 |
+
parser.add_argument(
|
390 |
+
"--nosave", action="store_true", help="do not save images/videos"
|
391 |
+
)
|
392 |
+
parser.add_argument(
|
393 |
+
"--classes",
|
394 |
+
nargs="+",
|
395 |
+
type=int,
|
396 |
+
help="filter by class: --classes 0, or --classes 0 2 3",
|
397 |
+
)
|
398 |
+
parser.add_argument(
|
399 |
+
"--agnostic-nms", action="store_true", help="class-agnostic NMS"
|
400 |
+
)
|
401 |
+
parser.add_argument(
|
402 |
+
"--augment", action="store_true", help="augmented inference"
|
403 |
+
)
|
404 |
+
parser.add_argument(
|
405 |
+
"--visualize", action="store_true", help="visualize features"
|
406 |
+
)
|
407 |
+
parser.add_argument(
|
408 |
+
"--update", action="store_true", help="update all models"
|
409 |
+
)
|
410 |
+
parser.add_argument(
|
411 |
+
"--project",
|
412 |
+
default=ROOT / "runs/detect",
|
413 |
+
help="save results to project/name",
|
414 |
+
)
|
415 |
+
parser.add_argument(
|
416 |
+
"--name", default="exp", help="save results to project/name"
|
417 |
+
)
|
418 |
+
parser.add_argument(
|
419 |
+
"--exist-ok",
|
420 |
+
action="store_true",
|
421 |
+
help="existing project/name ok, do not increment",
|
422 |
+
)
|
423 |
+
parser.add_argument(
|
424 |
+
"--line-thickness",
|
425 |
+
default=3,
|
426 |
+
type=int,
|
427 |
+
help="bounding box thickness (pixels)",
|
428 |
+
)
|
429 |
+
parser.add_argument(
|
430 |
+
"--hide-labels", default=False, action="store_true", help="hide labels"
|
431 |
+
)
|
432 |
+
parser.add_argument(
|
433 |
+
"--hide-conf",
|
434 |
+
default=False,
|
435 |
+
action="store_true",
|
436 |
+
help="hide confidences",
|
437 |
+
)
|
438 |
+
parser.add_argument(
|
439 |
+
"--half", action="store_true", help="use FP16 half-precision inference"
|
440 |
+
)
|
441 |
+
parser.add_argument(
|
442 |
+
"--dnn", action="store_true", help="use OpenCV DNN for ONNX inference"
|
443 |
+
)
|
444 |
+
parser.add_argument(
|
445 |
+
"--vid-stride", type=int, default=1, help="video frame-rate stride"
|
446 |
+
)
|
447 |
+
opt = parser.parse_args()
|
448 |
+
opt.imgsz *= 2 if len(opt.imgsz) == 1 else 1 # expand
|
449 |
+
print_args(vars(opt))
|
450 |
+
return opt
|
451 |
+
|
452 |
+
|
453 |
+
def main(opt):
|
454 |
+
check_requirements(exclude=("tensorboard", "thop"))
|
455 |
+
run(**vars(opt))
|
456 |
+
|
457 |
+
|
458 |
+
if __name__ == "__main__":
|
459 |
+
opt = parse_opt()
|
460 |
+
main(opt)
|
export.py
ADDED
@@ -0,0 +1,1013 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
2 |
+
"""
|
3 |
+
Export a YOLOv5 PyTorch model to other formats. TensorFlow exports authored by https://github.com/zldrobit
|
4 |
+
|
5 |
+
Format | `export.py --include` | Model
|
6 |
+
--- | --- | ---
|
7 |
+
PyTorch | - | yolov5s.pt
|
8 |
+
TorchScript | `torchscript` | yolov5s.torchscript
|
9 |
+
ONNX | `onnx` | yolov5s.onnx
|
10 |
+
OpenVINO | `openvino` | yolov5s_openvino_model/
|
11 |
+
TensorRT | `engine` | yolov5s.engine
|
12 |
+
CoreML | `coreml` | yolov5s.mlmodel
|
13 |
+
TensorFlow SavedModel | `saved_model` | yolov5s_saved_model/
|
14 |
+
TensorFlow GraphDef | `pb` | yolov5s.pb
|
15 |
+
TensorFlow Lite | `tflite` | yolov5s.tflite
|
16 |
+
TensorFlow Edge TPU | `edgetpu` | yolov5s_edgetpu.tflite
|
17 |
+
TensorFlow.js | `tfjs` | yolov5s_web_model/
|
18 |
+
PaddlePaddle | `paddle` | yolov5s_paddle_model/
|
19 |
+
|
20 |
+
Requirements:
|
21 |
+
$ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime openvino-dev tensorflow-cpu # CPU
|
22 |
+
$ pip install -r requirements.txt coremltools onnx onnx-simplifier onnxruntime-gpu openvino-dev tensorflow # GPU
|
23 |
+
|
24 |
+
Usage:
|
25 |
+
$ python export.py --weights yolov5s.pt --include torchscript onnx openvino engine coreml tflite ...
|
26 |
+
|
27 |
+
Inference:
|
28 |
+
$ python detect.py --weights yolov5s.pt # PyTorch
|
29 |
+
yolov5s.torchscript # TorchScript
|
30 |
+
yolov5s.onnx # ONNX Runtime or OpenCV DNN with --dnn
|
31 |
+
yolov5s_openvino_model # OpenVINO
|
32 |
+
yolov5s.engine # TensorRT
|
33 |
+
yolov5s.mlmodel # CoreML (macOS-only)
|
34 |
+
yolov5s_saved_model # TensorFlow SavedModel
|
35 |
+
yolov5s.pb # TensorFlow GraphDef
|
36 |
+
yolov5s.tflite # TensorFlow Lite
|
37 |
+
yolov5s_edgetpu.tflite # TensorFlow Edge TPU
|
38 |
+
yolov5s_paddle_model # PaddlePaddle
|
39 |
+
|
40 |
+
TensorFlow.js:
|
41 |
+
$ cd .. && git clone https://github.com/zldrobit/tfjs-yolov5-example.git && cd tfjs-yolov5-example
|
42 |
+
$ npm install
|
43 |
+
$ ln -s ../../yolov5/yolov5s_web_model public/yolov5s_web_model
|
44 |
+
$ npm start
|
45 |
+
"""
|
46 |
+
|
47 |
+
import argparse
|
48 |
+
import contextlib
|
49 |
+
import json
|
50 |
+
import os
|
51 |
+
import platform
|
52 |
+
import re
|
53 |
+
import subprocess
|
54 |
+
import sys
|
55 |
+
import time
|
56 |
+
import warnings
|
57 |
+
from pathlib import Path
|
58 |
+
|
59 |
+
import pandas as pd
|
60 |
+
import torch
|
61 |
+
from torch.utils.mobile_optimizer import optimize_for_mobile
|
62 |
+
|
63 |
+
FILE = Path(__file__).resolve()
|
64 |
+
ROOT = FILE.parents[0] # YOLOv5 root directory
|
65 |
+
if str(ROOT) not in sys.path:
|
66 |
+
sys.path.append(str(ROOT)) # add ROOT to PATH
|
67 |
+
if platform.system() != "Windows":
|
68 |
+
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
|
69 |
+
|
70 |
+
from models.experimental import attempt_load
|
71 |
+
from models.yolo import ClassificationModel, Detect, DetectionModel, SegmentationModel
|
72 |
+
from utils.dataloaders import LoadImages
|
73 |
+
from utils.general import (
|
74 |
+
LOGGER,
|
75 |
+
Profile,
|
76 |
+
check_dataset,
|
77 |
+
check_img_size,
|
78 |
+
check_requirements,
|
79 |
+
check_version,
|
80 |
+
check_yaml,
|
81 |
+
colorstr,
|
82 |
+
file_size,
|
83 |
+
get_default_args,
|
84 |
+
print_args,
|
85 |
+
url2file,
|
86 |
+
yaml_save,
|
87 |
+
)
|
88 |
+
from utils.torch_utils import select_device, smart_inference_mode
|
89 |
+
|
90 |
+
MACOS = platform.system() == "Darwin" # macOS environment
|
91 |
+
|
92 |
+
|
93 |
+
def export_formats():
|
94 |
+
# YOLOv5 export formats
|
95 |
+
x = [
|
96 |
+
["PyTorch", "-", ".pt", True, True],
|
97 |
+
["TorchScript", "torchscript", ".torchscript", True, True],
|
98 |
+
["ONNX", "onnx", ".onnx", True, True],
|
99 |
+
["OpenVINO", "openvino", "_openvino_model", True, False],
|
100 |
+
["TensorRT", "engine", ".engine", False, True],
|
101 |
+
["CoreML", "coreml", ".mlmodel", True, False],
|
102 |
+
["TensorFlow SavedModel", "saved_model", "_saved_model", True, True],
|
103 |
+
["TensorFlow GraphDef", "pb", ".pb", True, True],
|
104 |
+
["TensorFlow Lite", "tflite", ".tflite", True, False],
|
105 |
+
["TensorFlow Edge TPU", "edgetpu", "_edgetpu.tflite", False, False],
|
106 |
+
["TensorFlow.js", "tfjs", "_web_model", False, False],
|
107 |
+
["PaddlePaddle", "paddle", "_paddle_model", True, True],
|
108 |
+
]
|
109 |
+
return pd.DataFrame(
|
110 |
+
x, columns=["Format", "Argument", "Suffix", "CPU", "GPU"]
|
111 |
+
)
|
112 |
+
|
113 |
+
|
114 |
+
def try_export(inner_func):
|
115 |
+
# YOLOv5 export decorator, i..e @try_export
|
116 |
+
inner_args = get_default_args(inner_func)
|
117 |
+
|
118 |
+
def outer_func(*args, **kwargs):
|
119 |
+
prefix = inner_args["prefix"]
|
120 |
+
try:
|
121 |
+
with Profile() as dt:
|
122 |
+
f, model = inner_func(*args, **kwargs)
|
123 |
+
LOGGER.info(
|
124 |
+
f"{prefix} export success ✅ {dt.t:.1f}s, saved as {f} ({file_size(f):.1f} MB)"
|
125 |
+
)
|
126 |
+
return f, model
|
127 |
+
except Exception as e:
|
128 |
+
LOGGER.info(f"{prefix} export failure ❌ {dt.t:.1f}s: {e}")
|
129 |
+
return None, None
|
130 |
+
|
131 |
+
return outer_func
|
132 |
+
|
133 |
+
|
134 |
+
@try_export
|
135 |
+
def export_torchscript(
|
136 |
+
model, im, file, optimize, prefix=colorstr("TorchScript:")
|
137 |
+
):
|
138 |
+
# YOLOv5 TorchScript model export
|
139 |
+
LOGGER.info(
|
140 |
+
f"\n{prefix} starting export with torch {torch.__version__}..."
|
141 |
+
)
|
142 |
+
f = file.with_suffix(".torchscript")
|
143 |
+
|
144 |
+
ts = torch.jit.trace(model, im, strict=False)
|
145 |
+
d = {
|
146 |
+
"shape": im.shape,
|
147 |
+
"stride": int(max(model.stride)),
|
148 |
+
"names": model.names,
|
149 |
+
}
|
150 |
+
extra_files = {"config.txt": json.dumps(d)} # torch._C.ExtraFilesMap()
|
151 |
+
if (
|
152 |
+
optimize
|
153 |
+
): # https://pytorch.org/tutorials/recipes/mobile_interpreter.html
|
154 |
+
optimize_for_mobile(ts)._save_for_lite_interpreter(
|
155 |
+
str(f), _extra_files=extra_files
|
156 |
+
)
|
157 |
+
else:
|
158 |
+
ts.save(str(f), _extra_files=extra_files)
|
159 |
+
return f, None
|
160 |
+
|
161 |
+
|
162 |
+
@try_export
|
163 |
+
def export_onnx(
|
164 |
+
model, im, file, opset, dynamic, simplify, prefix=colorstr("ONNX:")
|
165 |
+
):
|
166 |
+
# YOLOv5 ONNX export
|
167 |
+
check_requirements("onnx>=1.12.0")
|
168 |
+
import onnx
|
169 |
+
|
170 |
+
LOGGER.info(f"\n{prefix} starting export with onnx {onnx.__version__}...")
|
171 |
+
f = file.with_suffix(".onnx")
|
172 |
+
|
173 |
+
output_names = (
|
174 |
+
["output0", "output1"]
|
175 |
+
if isinstance(model, SegmentationModel)
|
176 |
+
else ["output0"]
|
177 |
+
)
|
178 |
+
if dynamic:
|
179 |
+
dynamic = {
|
180 |
+
"images": {0: "batch", 2: "height", 3: "width"}
|
181 |
+
} # shape(1,3,640,640)
|
182 |
+
if isinstance(model, SegmentationModel):
|
183 |
+
dynamic["output0"] = {
|
184 |
+
0: "batch",
|
185 |
+
1: "anchors",
|
186 |
+
} # shape(1,25200,85)
|
187 |
+
dynamic["output1"] = {
|
188 |
+
0: "batch",
|
189 |
+
2: "mask_height",
|
190 |
+
3: "mask_width",
|
191 |
+
} # shape(1,32,160,160)
|
192 |
+
elif isinstance(model, DetectionModel):
|
193 |
+
dynamic["output0"] = {
|
194 |
+
0: "batch",
|
195 |
+
1: "anchors",
|
196 |
+
} # shape(1,25200,85)
|
197 |
+
|
198 |
+
torch.onnx.export(
|
199 |
+
model.cpu()
|
200 |
+
if dynamic
|
201 |
+
else model, # --dynamic only compatible with cpu
|
202 |
+
im.cpu() if dynamic else im,
|
203 |
+
f,
|
204 |
+
verbose=False,
|
205 |
+
opset_version=opset,
|
206 |
+
do_constant_folding=True, # WARNING: DNN inference with torch>=1.12 may require do_constant_folding=False
|
207 |
+
input_names=["images"],
|
208 |
+
output_names=output_names,
|
209 |
+
dynamic_axes=dynamic or None,
|
210 |
+
)
|
211 |
+
|
212 |
+
# Checks
|
213 |
+
model_onnx = onnx.load(f) # load onnx model
|
214 |
+
onnx.checker.check_model(model_onnx) # check onnx model
|
215 |
+
|
216 |
+
# Metadata
|
217 |
+
d = {"stride": int(max(model.stride)), "names": model.names}
|
218 |
+
for k, v in d.items():
|
219 |
+
meta = model_onnx.metadata_props.add()
|
220 |
+
meta.key, meta.value = k, str(v)
|
221 |
+
onnx.save(model_onnx, f)
|
222 |
+
|
223 |
+
# Simplify
|
224 |
+
if simplify:
|
225 |
+
try:
|
226 |
+
cuda = torch.cuda.is_available()
|
227 |
+
check_requirements(
|
228 |
+
(
|
229 |
+
"onnxruntime-gpu" if cuda else "onnxruntime",
|
230 |
+
"onnx-simplifier>=0.4.1",
|
231 |
+
)
|
232 |
+
)
|
233 |
+
import onnxsim
|
234 |
+
|
235 |
+
LOGGER.info(
|
236 |
+
f"{prefix} simplifying with onnx-simplifier {onnxsim.__version__}..."
|
237 |
+
)
|
238 |
+
model_onnx, check = onnxsim.simplify(model_onnx)
|
239 |
+
assert check, "assert check failed"
|
240 |
+
onnx.save(model_onnx, f)
|
241 |
+
except Exception as e:
|
242 |
+
LOGGER.info(f"{prefix} simplifier failure: {e}")
|
243 |
+
return f, model_onnx
|
244 |
+
|
245 |
+
|
246 |
+
@try_export
|
247 |
+
def export_openvino(file, metadata, half, prefix=colorstr("OpenVINO:")):
|
248 |
+
# YOLOv5 OpenVINO export
|
249 |
+
check_requirements(
|
250 |
+
"openvino-dev"
|
251 |
+
) # requires openvino-dev: https://pypi.org/project/openvino-dev/
|
252 |
+
import openvino.inference_engine as ie
|
253 |
+
|
254 |
+
LOGGER.info(
|
255 |
+
f"\n{prefix} starting export with openvino {ie.__version__}..."
|
256 |
+
)
|
257 |
+
f = str(file).replace(".pt", f"_openvino_model{os.sep}")
|
258 |
+
|
259 |
+
cmd = f"mo --input_model {file.with_suffix('.onnx')} --output_dir {f} --data_type {'FP16' if half else 'FP32'}"
|
260 |
+
subprocess.run(cmd.split(), check=True, env=os.environ) # export
|
261 |
+
yaml_save(
|
262 |
+
Path(f) / file.with_suffix(".yaml").name, metadata
|
263 |
+
) # add metadata.yaml
|
264 |
+
return f, None
|
265 |
+
|
266 |
+
|
267 |
+
@try_export
|
268 |
+
def export_paddle(model, im, file, metadata, prefix=colorstr("PaddlePaddle:")):
|
269 |
+
# YOLOv5 Paddle export
|
270 |
+
check_requirements(("paddlepaddle", "x2paddle"))
|
271 |
+
import x2paddle
|
272 |
+
from x2paddle.convert import pytorch2paddle
|
273 |
+
|
274 |
+
LOGGER.info(
|
275 |
+
f"\n{prefix} starting export with X2Paddle {x2paddle.__version__}..."
|
276 |
+
)
|
277 |
+
f = str(file).replace(".pt", f"_paddle_model{os.sep}")
|
278 |
+
|
279 |
+
pytorch2paddle(
|
280 |
+
module=model, save_dir=f, jit_type="trace", input_examples=[im]
|
281 |
+
) # export
|
282 |
+
yaml_save(
|
283 |
+
Path(f) / file.with_suffix(".yaml").name, metadata
|
284 |
+
) # add metadata.yaml
|
285 |
+
return f, None
|
286 |
+
|
287 |
+
|
288 |
+
@try_export
|
289 |
+
def export_coreml(model, im, file, int8, half, prefix=colorstr("CoreML:")):
|
290 |
+
# YOLOv5 CoreML export
|
291 |
+
check_requirements("coremltools")
|
292 |
+
import coremltools as ct
|
293 |
+
|
294 |
+
LOGGER.info(
|
295 |
+
f"\n{prefix} starting export with coremltools {ct.__version__}..."
|
296 |
+
)
|
297 |
+
f = file.with_suffix(".mlmodel")
|
298 |
+
|
299 |
+
ts = torch.jit.trace(model, im, strict=False) # TorchScript model
|
300 |
+
ct_model = ct.convert(
|
301 |
+
ts,
|
302 |
+
inputs=[
|
303 |
+
ct.ImageType(
|
304 |
+
"image", shape=im.shape, scale=1 / 255, bias=[0, 0, 0]
|
305 |
+
)
|
306 |
+
],
|
307 |
+
)
|
308 |
+
bits, mode = (
|
309 |
+
(8, "kmeans_lut") if int8 else (16, "linear") if half else (32, None)
|
310 |
+
)
|
311 |
+
if bits < 32:
|
312 |
+
if MACOS: # quantization only supported on macOS
|
313 |
+
with warnings.catch_warnings():
|
314 |
+
warnings.filterwarnings(
|
315 |
+
"ignore", category=DeprecationWarning
|
316 |
+
) # suppress numpy==1.20 float warning
|
317 |
+
ct_model = ct.models.neural_network.quantization_utils.quantize_weights(
|
318 |
+
ct_model, bits, mode
|
319 |
+
)
|
320 |
+
else:
|
321 |
+
print(
|
322 |
+
f"{prefix} quantization only supported on macOS, skipping..."
|
323 |
+
)
|
324 |
+
ct_model.save(f)
|
325 |
+
return f, ct_model
|
326 |
+
|
327 |
+
|
328 |
+
@try_export
|
329 |
+
def export_engine(
|
330 |
+
model,
|
331 |
+
im,
|
332 |
+
file,
|
333 |
+
half,
|
334 |
+
dynamic,
|
335 |
+
simplify,
|
336 |
+
workspace=4,
|
337 |
+
verbose=False,
|
338 |
+
prefix=colorstr("TensorRT:"),
|
339 |
+
):
|
340 |
+
# YOLOv5 TensorRT export https://developer.nvidia.com/tensorrt
|
341 |
+
assert (
|
342 |
+
im.device.type != "cpu"
|
343 |
+
), "export running on CPU but must be on GPU, i.e. `python export.py --device 0`"
|
344 |
+
try:
|
345 |
+
import tensorrt as trt
|
346 |
+
except Exception:
|
347 |
+
if platform.system() == "Linux":
|
348 |
+
check_requirements(
|
349 |
+
"nvidia-tensorrt",
|
350 |
+
cmds="-U --index-url https://pypi.ngc.nvidia.com",
|
351 |
+
)
|
352 |
+
import tensorrt as trt
|
353 |
+
|
354 |
+
if (
|
355 |
+
trt.__version__[0] == "7"
|
356 |
+
): # TensorRT 7 handling https://github.com/ultralytics/yolov5/issues/6012
|
357 |
+
grid = model.model[-1].anchor_grid
|
358 |
+
model.model[-1].anchor_grid = [a[..., :1, :1, :] for a in grid]
|
359 |
+
export_onnx(model, im, file, 12, dynamic, simplify) # opset 12
|
360 |
+
model.model[-1].anchor_grid = grid
|
361 |
+
else: # TensorRT >= 8
|
362 |
+
check_version(
|
363 |
+
trt.__version__, "8.0.0", hard=True
|
364 |
+
) # require tensorrt>=8.0.0
|
365 |
+
export_onnx(model, im, file, 12, dynamic, simplify) # opset 12
|
366 |
+
onnx = file.with_suffix(".onnx")
|
367 |
+
|
368 |
+
LOGGER.info(
|
369 |
+
f"\n{prefix} starting export with TensorRT {trt.__version__}..."
|
370 |
+
)
|
371 |
+
assert onnx.exists(), f"failed to export ONNX file: {onnx}"
|
372 |
+
f = file.with_suffix(".engine") # TensorRT engine file
|
373 |
+
logger = trt.Logger(trt.Logger.INFO)
|
374 |
+
if verbose:
|
375 |
+
logger.min_severity = trt.Logger.Severity.VERBOSE
|
376 |
+
|
377 |
+
builder = trt.Builder(logger)
|
378 |
+
config = builder.create_builder_config()
|
379 |
+
config.max_workspace_size = workspace * 1 << 30
|
380 |
+
# config.set_memory_pool_limit(trt.MemoryPoolType.WORKSPACE, workspace << 30) # fix TRT 8.4 deprecation notice
|
381 |
+
|
382 |
+
flag = 1 << int(trt.NetworkDefinitionCreationFlag.EXPLICIT_BATCH)
|
383 |
+
network = builder.create_network(flag)
|
384 |
+
parser = trt.OnnxParser(network, logger)
|
385 |
+
if not parser.parse_from_file(str(onnx)):
|
386 |
+
raise RuntimeError(f"failed to load ONNX file: {onnx}")
|
387 |
+
|
388 |
+
inputs = [network.get_input(i) for i in range(network.num_inputs)]
|
389 |
+
outputs = [network.get_output(i) for i in range(network.num_outputs)]
|
390 |
+
for inp in inputs:
|
391 |
+
LOGGER.info(
|
392 |
+
f'{prefix} input "{inp.name}" with shape{inp.shape} {inp.dtype}'
|
393 |
+
)
|
394 |
+
for out in outputs:
|
395 |
+
LOGGER.info(
|
396 |
+
f'{prefix} output "{out.name}" with shape{out.shape} {out.dtype}'
|
397 |
+
)
|
398 |
+
|
399 |
+
if dynamic:
|
400 |
+
if im.shape[0] <= 1:
|
401 |
+
LOGGER.warning(
|
402 |
+
f"{prefix} WARNING ⚠️ --dynamic model requires maximum --batch-size argument"
|
403 |
+
)
|
404 |
+
profile = builder.create_optimization_profile()
|
405 |
+
for inp in inputs:
|
406 |
+
profile.set_shape(
|
407 |
+
inp.name,
|
408 |
+
(1, *im.shape[1:]),
|
409 |
+
(max(1, im.shape[0] // 2), *im.shape[1:]),
|
410 |
+
im.shape,
|
411 |
+
)
|
412 |
+
config.add_optimization_profile(profile)
|
413 |
+
|
414 |
+
LOGGER.info(
|
415 |
+
f"{prefix} building FP{16 if builder.platform_has_fast_fp16 and half else 32} engine as {f}"
|
416 |
+
)
|
417 |
+
if builder.platform_has_fast_fp16 and half:
|
418 |
+
config.set_flag(trt.BuilderFlag.FP16)
|
419 |
+
with builder.build_engine(network, config) as engine, open(f, "wb") as t:
|
420 |
+
t.write(engine.serialize())
|
421 |
+
return f, None
|
422 |
+
|
423 |
+
|
424 |
+
@try_export
|
425 |
+
def export_saved_model(
|
426 |
+
model,
|
427 |
+
im,
|
428 |
+
file,
|
429 |
+
dynamic,
|
430 |
+
tf_nms=False,
|
431 |
+
agnostic_nms=False,
|
432 |
+
topk_per_class=100,
|
433 |
+
topk_all=100,
|
434 |
+
iou_thres=0.45,
|
435 |
+
conf_thres=0.25,
|
436 |
+
keras=False,
|
437 |
+
prefix=colorstr("TensorFlow SavedModel:"),
|
438 |
+
):
|
439 |
+
# YOLOv5 TensorFlow SavedModel export
|
440 |
+
try:
|
441 |
+
import tensorflow as tf
|
442 |
+
except Exception:
|
443 |
+
check_requirements(
|
444 |
+
f"tensorflow{'' if torch.cuda.is_available() else '-macos' if MACOS else '-cpu'}"
|
445 |
+
)
|
446 |
+
import tensorflow as tf
|
447 |
+
from tensorflow.python.framework.convert_to_constants import (
|
448 |
+
convert_variables_to_constants_v2,
|
449 |
+
)
|
450 |
+
|
451 |
+
from models.tf import TFModel
|
452 |
+
|
453 |
+
LOGGER.info(
|
454 |
+
f"\n{prefix} starting export with tensorflow {tf.__version__}..."
|
455 |
+
)
|
456 |
+
f = str(file).replace(".pt", "_saved_model")
|
457 |
+
batch_size, ch, *imgsz = list(im.shape) # BCHW
|
458 |
+
|
459 |
+
tf_model = TFModel(cfg=model.yaml, model=model, nc=model.nc, imgsz=imgsz)
|
460 |
+
im = tf.zeros((batch_size, *imgsz, ch)) # BHWC order for TensorFlow
|
461 |
+
_ = tf_model.predict(
|
462 |
+
im,
|
463 |
+
tf_nms,
|
464 |
+
agnostic_nms,
|
465 |
+
topk_per_class,
|
466 |
+
topk_all,
|
467 |
+
iou_thres,
|
468 |
+
conf_thres,
|
469 |
+
)
|
470 |
+
inputs = tf.keras.Input(
|
471 |
+
shape=(*imgsz, ch), batch_size=None if dynamic else batch_size
|
472 |
+
)
|
473 |
+
outputs = tf_model.predict(
|
474 |
+
inputs,
|
475 |
+
tf_nms,
|
476 |
+
agnostic_nms,
|
477 |
+
topk_per_class,
|
478 |
+
topk_all,
|
479 |
+
iou_thres,
|
480 |
+
conf_thres,
|
481 |
+
)
|
482 |
+
keras_model = tf.keras.Model(inputs=inputs, outputs=outputs)
|
483 |
+
keras_model.trainable = False
|
484 |
+
keras_model.summary()
|
485 |
+
if keras:
|
486 |
+
keras_model.save(f, save_format="tf")
|
487 |
+
else:
|
488 |
+
spec = tf.TensorSpec(
|
489 |
+
keras_model.inputs[0].shape, keras_model.inputs[0].dtype
|
490 |
+
)
|
491 |
+
m = tf.function(lambda x: keras_model(x)) # full model
|
492 |
+
m = m.get_concrete_function(spec)
|
493 |
+
frozen_func = convert_variables_to_constants_v2(m)
|
494 |
+
tfm = tf.Module()
|
495 |
+
tfm.__call__ = tf.function(
|
496 |
+
lambda x: frozen_func(x)[:4] if tf_nms else frozen_func(x), [spec]
|
497 |
+
)
|
498 |
+
tfm.__call__(im)
|
499 |
+
tf.saved_model.save(
|
500 |
+
tfm,
|
501 |
+
f,
|
502 |
+
options=tf.saved_model.SaveOptions(
|
503 |
+
experimental_custom_gradients=False
|
504 |
+
)
|
505 |
+
if check_version(tf.__version__, "2.6")
|
506 |
+
else tf.saved_model.SaveOptions(),
|
507 |
+
)
|
508 |
+
return f, keras_model
|
509 |
+
|
510 |
+
|
511 |
+
@try_export
|
512 |
+
def export_pb(keras_model, file, prefix=colorstr("TensorFlow GraphDef:")):
|
513 |
+
# YOLOv5 TensorFlow GraphDef *.pb export https://github.com/leimao/Frozen_Graph_TensorFlow
|
514 |
+
import tensorflow as tf
|
515 |
+
from tensorflow.python.framework.convert_to_constants import (
|
516 |
+
convert_variables_to_constants_v2,
|
517 |
+
)
|
518 |
+
|
519 |
+
LOGGER.info(
|
520 |
+
f"\n{prefix} starting export with tensorflow {tf.__version__}..."
|
521 |
+
)
|
522 |
+
f = file.with_suffix(".pb")
|
523 |
+
|
524 |
+
m = tf.function(lambda x: keras_model(x)) # full model
|
525 |
+
m = m.get_concrete_function(
|
526 |
+
tf.TensorSpec(keras_model.inputs[0].shape, keras_model.inputs[0].dtype)
|
527 |
+
)
|
528 |
+
frozen_func = convert_variables_to_constants_v2(m)
|
529 |
+
frozen_func.graph.as_graph_def()
|
530 |
+
tf.io.write_graph(
|
531 |
+
graph_or_graph_def=frozen_func.graph,
|
532 |
+
logdir=str(f.parent),
|
533 |
+
name=f.name,
|
534 |
+
as_text=False,
|
535 |
+
)
|
536 |
+
return f, None
|
537 |
+
|
538 |
+
|
539 |
+
@try_export
|
540 |
+
def export_tflite(
|
541 |
+
keras_model,
|
542 |
+
im,
|
543 |
+
file,
|
544 |
+
int8,
|
545 |
+
data,
|
546 |
+
nms,
|
547 |
+
agnostic_nms,
|
548 |
+
prefix=colorstr("TensorFlow Lite:"),
|
549 |
+
):
|
550 |
+
# YOLOv5 TensorFlow Lite export
|
551 |
+
import tensorflow as tf
|
552 |
+
|
553 |
+
LOGGER.info(
|
554 |
+
f"\n{prefix} starting export with tensorflow {tf.__version__}..."
|
555 |
+
)
|
556 |
+
batch_size, ch, *imgsz = list(im.shape) # BCHW
|
557 |
+
f = str(file).replace(".pt", "-fp16.tflite")
|
558 |
+
|
559 |
+
converter = tf.lite.TFLiteConverter.from_keras_model(keras_model)
|
560 |
+
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS]
|
561 |
+
converter.target_spec.supported_types = [tf.float16]
|
562 |
+
converter.optimizations = [tf.lite.Optimize.DEFAULT]
|
563 |
+
if int8:
|
564 |
+
from models.tf import representative_dataset_gen
|
565 |
+
|
566 |
+
dataset = LoadImages(
|
567 |
+
check_dataset(check_yaml(data))["train"],
|
568 |
+
img_size=imgsz,
|
569 |
+
auto=False,
|
570 |
+
)
|
571 |
+
converter.representative_dataset = lambda: representative_dataset_gen(
|
572 |
+
dataset, ncalib=100
|
573 |
+
)
|
574 |
+
converter.target_spec.supported_ops = [
|
575 |
+
tf.lite.OpsSet.TFLITE_BUILTINS_INT8
|
576 |
+
]
|
577 |
+
converter.target_spec.supported_types = []
|
578 |
+
converter.inference_input_type = tf.uint8 # or tf.int8
|
579 |
+
converter.inference_output_type = tf.uint8 # or tf.int8
|
580 |
+
converter.experimental_new_quantizer = True
|
581 |
+
f = str(file).replace(".pt", "-int8.tflite")
|
582 |
+
if nms or agnostic_nms:
|
583 |
+
converter.target_spec.supported_ops.append(
|
584 |
+
tf.lite.OpsSet.SELECT_TF_OPS
|
585 |
+
)
|
586 |
+
|
587 |
+
tflite_model = converter.convert()
|
588 |
+
open(f, "wb").write(tflite_model)
|
589 |
+
return f, None
|
590 |
+
|
591 |
+
|
592 |
+
@try_export
|
593 |
+
def export_edgetpu(file, prefix=colorstr("Edge TPU:")):
|
594 |
+
# YOLOv5 Edge TPU export https://coral.ai/docs/edgetpu/models-intro/
|
595 |
+
cmd = "edgetpu_compiler --version"
|
596 |
+
help_url = "https://coral.ai/docs/edgetpu/compiler/"
|
597 |
+
assert (
|
598 |
+
platform.system() == "Linux"
|
599 |
+
), f"export only supported on Linux. See {help_url}"
|
600 |
+
if subprocess.run(f"{cmd} >/dev/null", shell=True).returncode != 0:
|
601 |
+
LOGGER.info(
|
602 |
+
f"\n{prefix} export requires Edge TPU compiler. Attempting install from {help_url}"
|
603 |
+
)
|
604 |
+
sudo = (
|
605 |
+
subprocess.run("sudo --version >/dev/null", shell=True).returncode
|
606 |
+
== 0
|
607 |
+
) # sudo installed on system
|
608 |
+
for c in (
|
609 |
+
"curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo apt-key add -",
|
610 |
+
'echo "deb https://packages.cloud.google.com/apt coral-edgetpu-stable main" | sudo tee /etc/apt/sources.list.d/coral-edgetpu.list',
|
611 |
+
"sudo apt-get update",
|
612 |
+
"sudo apt-get install edgetpu-compiler",
|
613 |
+
):
|
614 |
+
subprocess.run(
|
615 |
+
c if sudo else c.replace("sudo ", ""), shell=True, check=True
|
616 |
+
)
|
617 |
+
ver = (
|
618 |
+
subprocess.run(cmd, shell=True, capture_output=True, check=True)
|
619 |
+
.stdout.decode()
|
620 |
+
.split()[-1]
|
621 |
+
)
|
622 |
+
|
623 |
+
LOGGER.info(f"\n{prefix} starting export with Edge TPU compiler {ver}...")
|
624 |
+
f = str(file).replace(".pt", "-int8_edgetpu.tflite") # Edge TPU model
|
625 |
+
f_tfl = str(file).replace(".pt", "-int8.tflite") # TFLite model
|
626 |
+
|
627 |
+
cmd = f"edgetpu_compiler -s -d -k 10 --out_dir {file.parent} {f_tfl}"
|
628 |
+
subprocess.run(cmd.split(), check=True)
|
629 |
+
return f, None
|
630 |
+
|
631 |
+
|
632 |
+
@try_export
|
633 |
+
def export_tfjs(file, prefix=colorstr("TensorFlow.js:")):
|
634 |
+
# YOLOv5 TensorFlow.js export
|
635 |
+
check_requirements("tensorflowjs")
|
636 |
+
import tensorflowjs as tfjs
|
637 |
+
|
638 |
+
LOGGER.info(
|
639 |
+
f"\n{prefix} starting export with tensorflowjs {tfjs.__version__}..."
|
640 |
+
)
|
641 |
+
f = str(file).replace(".pt", "_web_model") # js dir
|
642 |
+
f_pb = file.with_suffix(".pb") # *.pb path
|
643 |
+
f_json = f"{f}/model.json" # *.json path
|
644 |
+
|
645 |
+
cmd = (
|
646 |
+
f"tensorflowjs_converter --input_format=tf_frozen_model "
|
647 |
+
f"--output_node_names=Identity,Identity_1,Identity_2,Identity_3 {f_pb} {f}"
|
648 |
+
)
|
649 |
+
subprocess.run(cmd.split())
|
650 |
+
|
651 |
+
json = Path(f_json).read_text()
|
652 |
+
with open(f_json, "w") as j: # sort JSON Identity_* in ascending order
|
653 |
+
subst = re.sub(
|
654 |
+
r'{"outputs": {"Identity.?.?": {"name": "Identity.?.?"}, '
|
655 |
+
r'"Identity.?.?": {"name": "Identity.?.?"}, '
|
656 |
+
r'"Identity.?.?": {"name": "Identity.?.?"}, '
|
657 |
+
r'"Identity.?.?": {"name": "Identity.?.?"}}}',
|
658 |
+
r'{"outputs": {"Identity": {"name": "Identity"}, '
|
659 |
+
r'"Identity_1": {"name": "Identity_1"}, '
|
660 |
+
r'"Identity_2": {"name": "Identity_2"}, '
|
661 |
+
r'"Identity_3": {"name": "Identity_3"}}}',
|
662 |
+
json,
|
663 |
+
)
|
664 |
+
j.write(subst)
|
665 |
+
return f, None
|
666 |
+
|
667 |
+
|
668 |
+
def add_tflite_metadata(file, metadata, num_outputs):
|
669 |
+
# Add metadata to *.tflite models per https://www.tensorflow.org/lite/models/convert/metadata
|
670 |
+
with contextlib.suppress(ImportError):
|
671 |
+
# check_requirements('tflite_support')
|
672 |
+
from tflite_support import flatbuffers
|
673 |
+
from tflite_support import metadata as _metadata
|
674 |
+
from tflite_support import metadata_schema_py_generated as _metadata_fb
|
675 |
+
|
676 |
+
tmp_file = Path("/tmp/meta.txt")
|
677 |
+
with open(tmp_file, "w") as meta_f:
|
678 |
+
meta_f.write(str(metadata))
|
679 |
+
|
680 |
+
model_meta = _metadata_fb.ModelMetadataT()
|
681 |
+
label_file = _metadata_fb.AssociatedFileT()
|
682 |
+
label_file.name = tmp_file.name
|
683 |
+
model_meta.associatedFiles = [label_file]
|
684 |
+
|
685 |
+
subgraph = _metadata_fb.SubGraphMetadataT()
|
686 |
+
subgraph.inputTensorMetadata = [_metadata_fb.TensorMetadataT()]
|
687 |
+
subgraph.outputTensorMetadata = [
|
688 |
+
_metadata_fb.TensorMetadataT()
|
689 |
+
] * num_outputs
|
690 |
+
model_meta.subgraphMetadata = [subgraph]
|
691 |
+
|
692 |
+
b = flatbuffers.Builder(0)
|
693 |
+
b.Finish(
|
694 |
+
model_meta.Pack(b),
|
695 |
+
_metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER,
|
696 |
+
)
|
697 |
+
metadata_buf = b.Output()
|
698 |
+
|
699 |
+
populator = _metadata.MetadataPopulator.with_model_file(file)
|
700 |
+
populator.load_metadata_buffer(metadata_buf)
|
701 |
+
populator.load_associated_files([str(tmp_file)])
|
702 |
+
populator.populate()
|
703 |
+
tmp_file.unlink()
|
704 |
+
|
705 |
+
|
706 |
+
@smart_inference_mode()
|
707 |
+
def run(
|
708 |
+
data=ROOT / "data/coco128.yaml", # 'dataset.yaml path'
|
709 |
+
weights=ROOT / "yolov5s.pt", # weights path
|
710 |
+
imgsz=(640, 640), # image (height, width)
|
711 |
+
batch_size=1, # batch size
|
712 |
+
device="cpu", # cuda device, i.e. 0 or 0,1,2,3 or cpu
|
713 |
+
include=("torchscript", "onnx"), # include formats
|
714 |
+
half=False, # FP16 half-precision export
|
715 |
+
inplace=False, # set YOLOv5 Detect() inplace=True
|
716 |
+
keras=False, # use Keras
|
717 |
+
optimize=False, # TorchScript: optimize for mobile
|
718 |
+
int8=False, # CoreML/TF INT8 quantization
|
719 |
+
dynamic=False, # ONNX/TF/TensorRT: dynamic axes
|
720 |
+
simplify=False, # ONNX: simplify model
|
721 |
+
opset=12, # ONNX: opset version
|
722 |
+
verbose=False, # TensorRT: verbose log
|
723 |
+
workspace=4, # TensorRT: workspace size (GB)
|
724 |
+
nms=False, # TF: add NMS to model
|
725 |
+
agnostic_nms=False, # TF: add agnostic NMS to model
|
726 |
+
topk_per_class=100, # TF.js NMS: topk per class to keep
|
727 |
+
topk_all=100, # TF.js NMS: topk for all classes to keep
|
728 |
+
iou_thres=0.45, # TF.js NMS: IoU threshold
|
729 |
+
conf_thres=0.25, # TF.js NMS: confidence threshold
|
730 |
+
):
|
731 |
+
t = time.time()
|
732 |
+
include = [x.lower() for x in include] # to lowercase
|
733 |
+
fmts = tuple(export_formats()["Argument"][1:]) # --include arguments
|
734 |
+
flags = [x in include for x in fmts]
|
735 |
+
assert sum(flags) == len(
|
736 |
+
include
|
737 |
+
), f"ERROR: Invalid --include {include}, valid --include arguments are {fmts}"
|
738 |
+
(
|
739 |
+
jit,
|
740 |
+
onnx,
|
741 |
+
xml,
|
742 |
+
engine,
|
743 |
+
coreml,
|
744 |
+
saved_model,
|
745 |
+
pb,
|
746 |
+
tflite,
|
747 |
+
edgetpu,
|
748 |
+
tfjs,
|
749 |
+
paddle,
|
750 |
+
) = flags # export booleans
|
751 |
+
file = Path(
|
752 |
+
url2file(weights)
|
753 |
+
if str(weights).startswith(("http:/", "https:/"))
|
754 |
+
else weights
|
755 |
+
) # PyTorch weights
|
756 |
+
|
757 |
+
# Load PyTorch model
|
758 |
+
device = select_device(device)
|
759 |
+
if half:
|
760 |
+
assert (
|
761 |
+
device.type != "cpu" or coreml
|
762 |
+
), "--half only compatible with GPU export, i.e. use --device 0"
|
763 |
+
assert (
|
764 |
+
not dynamic
|
765 |
+
), "--half not compatible with --dynamic, i.e. use either --half or --dynamic but not both"
|
766 |
+
model = attempt_load(
|
767 |
+
weights, device=device, inplace=True, fuse=True
|
768 |
+
) # load FP32 model
|
769 |
+
|
770 |
+
# Checks
|
771 |
+
imgsz *= 2 if len(imgsz) == 1 else 1 # expand
|
772 |
+
if optimize:
|
773 |
+
assert (
|
774 |
+
device.type == "cpu"
|
775 |
+
), "--optimize not compatible with cuda devices, i.e. use --device cpu"
|
776 |
+
|
777 |
+
# Input
|
778 |
+
gs = int(max(model.stride)) # grid size (max stride)
|
779 |
+
imgsz = [
|
780 |
+
check_img_size(x, gs) for x in imgsz
|
781 |
+
] # verify img_size are gs-multiples
|
782 |
+
im = torch.zeros(batch_size, 3, *imgsz).to(
|
783 |
+
device
|
784 |
+
) # image size(1,3,320,192) BCHW iDetection
|
785 |
+
|
786 |
+
# Update model
|
787 |
+
model.eval()
|
788 |
+
for k, m in model.named_modules():
|
789 |
+
if isinstance(m, Detect):
|
790 |
+
m.inplace = inplace
|
791 |
+
m.dynamic = dynamic
|
792 |
+
m.export = True
|
793 |
+
|
794 |
+
for _ in range(2):
|
795 |
+
y = model(im) # dry runs
|
796 |
+
if half and not coreml:
|
797 |
+
im, model = im.half(), model.half() # to FP16
|
798 |
+
shape = tuple(
|
799 |
+
(y[0] if isinstance(y, tuple) else y).shape
|
800 |
+
) # model output shape
|
801 |
+
metadata = {
|
802 |
+
"stride": int(max(model.stride)),
|
803 |
+
"names": model.names,
|
804 |
+
} # model metadata
|
805 |
+
LOGGER.info(
|
806 |
+
f"\n{colorstr('PyTorch:')} starting from {file} with output shape {shape} ({file_size(file):.1f} MB)"
|
807 |
+
)
|
808 |
+
|
809 |
+
# Exports
|
810 |
+
f = [""] * len(fmts) # exported filenames
|
811 |
+
warnings.filterwarnings(
|
812 |
+
action="ignore", category=torch.jit.TracerWarning
|
813 |
+
) # suppress TracerWarning
|
814 |
+
if jit: # TorchScript
|
815 |
+
f[0], _ = export_torchscript(model, im, file, optimize)
|
816 |
+
if engine: # TensorRT required before ONNX
|
817 |
+
f[1], _ = export_engine(
|
818 |
+
model, im, file, half, dynamic, simplify, workspace, verbose
|
819 |
+
)
|
820 |
+
if onnx or xml: # OpenVINO requires ONNX
|
821 |
+
f[2], _ = export_onnx(model, im, file, opset, dynamic, simplify)
|
822 |
+
if xml: # OpenVINO
|
823 |
+
f[3], _ = export_openvino(file, metadata, half)
|
824 |
+
if coreml: # CoreML
|
825 |
+
f[4], _ = export_coreml(model, im, file, int8, half)
|
826 |
+
if any((saved_model, pb, tflite, edgetpu, tfjs)): # TensorFlow formats
|
827 |
+
assert (
|
828 |
+
not tflite or not tfjs
|
829 |
+
), "TFLite and TF.js models must be exported separately, please pass only one type."
|
830 |
+
assert not isinstance(
|
831 |
+
model, ClassificationModel
|
832 |
+
), "ClassificationModel export to TF formats not yet supported."
|
833 |
+
f[5], s_model = export_saved_model(
|
834 |
+
model.cpu(),
|
835 |
+
im,
|
836 |
+
file,
|
837 |
+
dynamic,
|
838 |
+
tf_nms=nms or agnostic_nms or tfjs,
|
839 |
+
agnostic_nms=agnostic_nms or tfjs,
|
840 |
+
topk_per_class=topk_per_class,
|
841 |
+
topk_all=topk_all,
|
842 |
+
iou_thres=iou_thres,
|
843 |
+
conf_thres=conf_thres,
|
844 |
+
keras=keras,
|
845 |
+
)
|
846 |
+
if pb or tfjs: # pb prerequisite to tfjs
|
847 |
+
f[6], _ = export_pb(s_model, file)
|
848 |
+
if tflite or edgetpu:
|
849 |
+
f[7], _ = export_tflite(
|
850 |
+
s_model,
|
851 |
+
im,
|
852 |
+
file,
|
853 |
+
int8 or edgetpu,
|
854 |
+
data=data,
|
855 |
+
nms=nms,
|
856 |
+
agnostic_nms=agnostic_nms,
|
857 |
+
)
|
858 |
+
if edgetpu:
|
859 |
+
f[8], _ = export_edgetpu(file)
|
860 |
+
add_tflite_metadata(
|
861 |
+
f[8] or f[7], metadata, num_outputs=len(s_model.outputs)
|
862 |
+
)
|
863 |
+
if tfjs:
|
864 |
+
f[9], _ = export_tfjs(file)
|
865 |
+
if paddle: # PaddlePaddle
|
866 |
+
f[10], _ = export_paddle(model, im, file, metadata)
|
867 |
+
|
868 |
+
# Finish
|
869 |
+
f = [str(x) for x in f if x] # filter out '' and None
|
870 |
+
if any(f):
|
871 |
+
cls, det, seg = (
|
872 |
+
isinstance(model, x)
|
873 |
+
for x in (ClassificationModel, DetectionModel, SegmentationModel)
|
874 |
+
) # type
|
875 |
+
det &= (
|
876 |
+
not seg
|
877 |
+
) # segmentation models inherit from SegmentationModel(DetectionModel)
|
878 |
+
dir = Path("segment" if seg else "classify" if cls else "")
|
879 |
+
h = "--half" if half else "" # --half FP16 inference arg
|
880 |
+
s = (
|
881 |
+
"# WARNING ⚠️ ClassificationModel not yet supported for PyTorch Hub AutoShape inference"
|
882 |
+
if cls
|
883 |
+
else "# WARNING ⚠️ SegmentationModel not yet supported for PyTorch Hub AutoShape inference"
|
884 |
+
if seg
|
885 |
+
else ""
|
886 |
+
)
|
887 |
+
LOGGER.info(
|
888 |
+
f"\nExport complete ({time.time() - t:.1f}s)"
|
889 |
+
f"\nResults saved to {colorstr('bold', file.parent.resolve())}"
|
890 |
+
f"\nDetect: python {dir / ('detect.py' if det else 'predict.py')} --weights {f[-1]} {h}"
|
891 |
+
f"\nValidate: python {dir / 'val.py'} --weights {f[-1]} {h}"
|
892 |
+
f"\nPyTorch Hub: model = torch.hub.load('ultralytics/yolov5', 'custom', '{f[-1]}') {s}"
|
893 |
+
f"\nVisualize: https://netron.app"
|
894 |
+
)
|
895 |
+
return f # return list of exported files/dirs
|
896 |
+
|
897 |
+
|
898 |
+
def parse_opt():
|
899 |
+
parser = argparse.ArgumentParser()
|
900 |
+
parser.add_argument(
|
901 |
+
"--data",
|
902 |
+
type=str,
|
903 |
+
default=ROOT / "data/coco128.yaml",
|
904 |
+
help="dataset.yaml path",
|
905 |
+
)
|
906 |
+
parser.add_argument(
|
907 |
+
"--weights",
|
908 |
+
nargs="+",
|
909 |
+
type=str,
|
910 |
+
default=ROOT / "yolov5s.pt",
|
911 |
+
help="model.pt path(s)",
|
912 |
+
)
|
913 |
+
parser.add_argument(
|
914 |
+
"--imgsz",
|
915 |
+
"--img",
|
916 |
+
"--img-size",
|
917 |
+
nargs="+",
|
918 |
+
type=int,
|
919 |
+
default=[640, 640],
|
920 |
+
help="image (h, w)",
|
921 |
+
)
|
922 |
+
parser.add_argument("--batch-size", type=int, default=1, help="batch size")
|
923 |
+
parser.add_argument(
|
924 |
+
"--device", default="cpu", help="cuda device, i.e. 0 or 0,1,2,3 or cpu"
|
925 |
+
)
|
926 |
+
parser.add_argument(
|
927 |
+
"--half", action="store_true", help="FP16 half-precision export"
|
928 |
+
)
|
929 |
+
parser.add_argument(
|
930 |
+
"--inplace",
|
931 |
+
action="store_true",
|
932 |
+
help="set YOLOv5 Detect() inplace=True",
|
933 |
+
)
|
934 |
+
parser.add_argument("--keras", action="store_true", help="TF: use Keras")
|
935 |
+
parser.add_argument(
|
936 |
+
"--optimize",
|
937 |
+
action="store_true",
|
938 |
+
help="TorchScript: optimize for mobile",
|
939 |
+
)
|
940 |
+
parser.add_argument(
|
941 |
+
"--int8", action="store_true", help="CoreML/TF INT8 quantization"
|
942 |
+
)
|
943 |
+
parser.add_argument(
|
944 |
+
"--dynamic", action="store_true", help="ONNX/TF/TensorRT: dynamic axes"
|
945 |
+
)
|
946 |
+
parser.add_argument(
|
947 |
+
"--simplify", action="store_true", help="ONNX: simplify model"
|
948 |
+
)
|
949 |
+
parser.add_argument(
|
950 |
+
"--opset", type=int, default=17, help="ONNX: opset version"
|
951 |
+
)
|
952 |
+
parser.add_argument(
|
953 |
+
"--verbose", action="store_true", help="TensorRT: verbose log"
|
954 |
+
)
|
955 |
+
parser.add_argument(
|
956 |
+
"--workspace",
|
957 |
+
type=int,
|
958 |
+
default=4,
|
959 |
+
help="TensorRT: workspace size (GB)",
|
960 |
+
)
|
961 |
+
parser.add_argument(
|
962 |
+
"--nms", action="store_true", help="TF: add NMS to model"
|
963 |
+
)
|
964 |
+
parser.add_argument(
|
965 |
+
"--agnostic-nms",
|
966 |
+
action="store_true",
|
967 |
+
help="TF: add agnostic NMS to model",
|
968 |
+
)
|
969 |
+
parser.add_argument(
|
970 |
+
"--topk-per-class",
|
971 |
+
type=int,
|
972 |
+
default=100,
|
973 |
+
help="TF.js NMS: topk per class to keep",
|
974 |
+
)
|
975 |
+
parser.add_argument(
|
976 |
+
"--topk-all",
|
977 |
+
type=int,
|
978 |
+
default=100,
|
979 |
+
help="TF.js NMS: topk for all classes to keep",
|
980 |
+
)
|
981 |
+
parser.add_argument(
|
982 |
+
"--iou-thres",
|
983 |
+
type=float,
|
984 |
+
default=0.45,
|
985 |
+
help="TF.js NMS: IoU threshold",
|
986 |
+
)
|
987 |
+
parser.add_argument(
|
988 |
+
"--conf-thres",
|
989 |
+
type=float,
|
990 |
+
default=0.25,
|
991 |
+
help="TF.js NMS: confidence threshold",
|
992 |
+
)
|
993 |
+
parser.add_argument(
|
994 |
+
"--include",
|
995 |
+
nargs="+",
|
996 |
+
default=["torchscript"],
|
997 |
+
help="torchscript, onnx, openvino, engine, coreml, saved_model, pb, tflite, edgetpu, tfjs, paddle",
|
998 |
+
)
|
999 |
+
opt = parser.parse_args()
|
1000 |
+
print_args(vars(opt))
|
1001 |
+
return opt
|
1002 |
+
|
1003 |
+
|
1004 |
+
def main(opt):
|
1005 |
+
for opt.weights in (
|
1006 |
+
opt.weights if isinstance(opt.weights, list) else [opt.weights]
|
1007 |
+
):
|
1008 |
+
run(**vars(opt))
|
1009 |
+
|
1010 |
+
|
1011 |
+
if __name__ == "__main__":
|
1012 |
+
opt = parse_opt()
|
1013 |
+
main(opt)
|
hubconf.py
ADDED
@@ -0,0 +1,309 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
2 |
+
"""
|
3 |
+
PyTorch Hub models https://pytorch.org/hub/ultralytics_yolov5
|
4 |
+
|
5 |
+
Usage:
|
6 |
+
import torch
|
7 |
+
model = torch.hub.load('ultralytics/yolov5', 'yolov5s') # official model
|
8 |
+
model = torch.hub.load('ultralytics/yolov5:master', 'yolov5s') # from branch
|
9 |
+
model = torch.hub.load('ultralytics/yolov5', 'custom', 'yolov5s.pt') # custom/local model
|
10 |
+
model = torch.hub.load('.', 'custom', 'yolov5s.pt', source='local') # local repo
|
11 |
+
"""
|
12 |
+
|
13 |
+
import torch
|
14 |
+
|
15 |
+
|
16 |
+
def _create(
|
17 |
+
name,
|
18 |
+
pretrained=True,
|
19 |
+
channels=3,
|
20 |
+
classes=80,
|
21 |
+
autoshape=True,
|
22 |
+
verbose=True,
|
23 |
+
device=None,
|
24 |
+
):
|
25 |
+
"""Creates or loads a YOLOv5 model
|
26 |
+
|
27 |
+
Arguments:
|
28 |
+
name (str): model name 'yolov5s' or path 'path/to/best.pt'
|
29 |
+
pretrained (bool): load pretrained weights into the model
|
30 |
+
channels (int): number of input channels
|
31 |
+
classes (int): number of model classes
|
32 |
+
autoshape (bool): apply YOLOv5 .autoshape() wrapper to model
|
33 |
+
verbose (bool): print all information to screen
|
34 |
+
device (str, torch.device, None): device to use for model parameters
|
35 |
+
|
36 |
+
Returns:
|
37 |
+
YOLOv5 model
|
38 |
+
"""
|
39 |
+
from pathlib import Path
|
40 |
+
|
41 |
+
from models.common import AutoShape, DetectMultiBackend
|
42 |
+
from models.experimental import attempt_load
|
43 |
+
from models.yolo import ClassificationModel, DetectionModel, SegmentationModel
|
44 |
+
from utils.downloads import attempt_download
|
45 |
+
from utils.general import LOGGER, check_requirements, intersect_dicts, logging
|
46 |
+
from utils.torch_utils import select_device
|
47 |
+
|
48 |
+
if not verbose:
|
49 |
+
LOGGER.setLevel(logging.WARNING)
|
50 |
+
check_requirements(exclude=("opencv-python", "tensorboard", "thop"))
|
51 |
+
name = Path(name)
|
52 |
+
path = (
|
53 |
+
name.with_suffix(".pt")
|
54 |
+
if name.suffix == "" and not name.is_dir()
|
55 |
+
else name
|
56 |
+
) # checkpoint path
|
57 |
+
try:
|
58 |
+
device = select_device(device)
|
59 |
+
if pretrained and channels == 3 and classes == 80:
|
60 |
+
try:
|
61 |
+
model = DetectMultiBackend(
|
62 |
+
path, device=device, fuse=autoshape
|
63 |
+
) # detection model
|
64 |
+
if autoshape:
|
65 |
+
if model.pt and isinstance(
|
66 |
+
model.model, ClassificationModel
|
67 |
+
):
|
68 |
+
LOGGER.warning(
|
69 |
+
"WARNING ⚠️ YOLOv5 ClassificationModel is not yet AutoShape compatible. "
|
70 |
+
"You must pass torch tensors in BCHW to this model, i.e. shape(1,3,224,224)."
|
71 |
+
)
|
72 |
+
elif model.pt and isinstance(
|
73 |
+
model.model, SegmentationModel
|
74 |
+
):
|
75 |
+
LOGGER.warning(
|
76 |
+
"WARNING ⚠️ YOLOv5 SegmentationModel is not yet AutoShape compatible. "
|
77 |
+
"You will not be able to run inference with this model."
|
78 |
+
)
|
79 |
+
else:
|
80 |
+
model = AutoShape(
|
81 |
+
model
|
82 |
+
) # for file/URI/PIL/cv2/np inputs and NMS
|
83 |
+
except Exception:
|
84 |
+
model = attempt_load(
|
85 |
+
path, device=device, fuse=False
|
86 |
+
) # arbitrary model
|
87 |
+
else:
|
88 |
+
cfg = list(
|
89 |
+
(Path(__file__).parent / "models").rglob(f"{path.stem}.yaml")
|
90 |
+
)[
|
91 |
+
0
|
92 |
+
] # model.yaml path
|
93 |
+
model = DetectionModel(cfg, channels, classes) # create model
|
94 |
+
if pretrained:
|
95 |
+
ckpt = torch.load(
|
96 |
+
attempt_download(path), map_location=device
|
97 |
+
) # load
|
98 |
+
csd = (
|
99 |
+
ckpt["model"].float().state_dict()
|
100 |
+
) # checkpoint state_dict as FP32
|
101 |
+
csd = intersect_dicts(
|
102 |
+
csd, model.state_dict(), exclude=["anchors"]
|
103 |
+
) # intersect
|
104 |
+
model.load_state_dict(csd, strict=False) # load
|
105 |
+
if len(ckpt["model"].names) == classes:
|
106 |
+
model.names = ckpt[
|
107 |
+
"model"
|
108 |
+
].names # set class names attribute
|
109 |
+
if not verbose:
|
110 |
+
LOGGER.setLevel(logging.INFO) # reset to default
|
111 |
+
return model.to(device)
|
112 |
+
|
113 |
+
except Exception as e:
|
114 |
+
help_url = "https://github.com/ultralytics/yolov5/issues/36"
|
115 |
+
s = f"{e}. Cache may be out of date, try `force_reload=True` or see {help_url} for help."
|
116 |
+
raise Exception(s) from e
|
117 |
+
|
118 |
+
|
119 |
+
def custom(
|
120 |
+
path="path/to/model.pt", autoshape=True, _verbose=True, device=None
|
121 |
+
):
|
122 |
+
# YOLOv5 custom or local model
|
123 |
+
return _create(path, autoshape=autoshape, verbose=_verbose, device=device)
|
124 |
+
|
125 |
+
|
126 |
+
def yolov5n(
|
127 |
+
pretrained=True,
|
128 |
+
channels=3,
|
129 |
+
classes=80,
|
130 |
+
autoshape=True,
|
131 |
+
_verbose=True,
|
132 |
+
device=None,
|
133 |
+
):
|
134 |
+
# YOLOv5-nano model https://github.com/ultralytics/yolov5
|
135 |
+
return _create(
|
136 |
+
"yolov5n", pretrained, channels, classes, autoshape, _verbose, device
|
137 |
+
)
|
138 |
+
|
139 |
+
|
140 |
+
def yolov5s(
|
141 |
+
pretrained=True,
|
142 |
+
channels=3,
|
143 |
+
classes=80,
|
144 |
+
autoshape=True,
|
145 |
+
_verbose=True,
|
146 |
+
device=None,
|
147 |
+
):
|
148 |
+
# YOLOv5-small model https://github.com/ultralytics/yolov5
|
149 |
+
return _create(
|
150 |
+
"yolov5s", pretrained, channels, classes, autoshape, _verbose, device
|
151 |
+
)
|
152 |
+
|
153 |
+
|
154 |
+
def yolov5m(
|
155 |
+
pretrained=True,
|
156 |
+
channels=3,
|
157 |
+
classes=80,
|
158 |
+
autoshape=True,
|
159 |
+
_verbose=True,
|
160 |
+
device=None,
|
161 |
+
):
|
162 |
+
# YOLOv5-medium model https://github.com/ultralytics/yolov5
|
163 |
+
return _create(
|
164 |
+
"yolov5m", pretrained, channels, classes, autoshape, _verbose, device
|
165 |
+
)
|
166 |
+
|
167 |
+
|
168 |
+
def yolov5l(
|
169 |
+
pretrained=True,
|
170 |
+
channels=3,
|
171 |
+
classes=80,
|
172 |
+
autoshape=True,
|
173 |
+
_verbose=True,
|
174 |
+
device=None,
|
175 |
+
):
|
176 |
+
# YOLOv5-large model https://github.com/ultralytics/yolov5
|
177 |
+
return _create(
|
178 |
+
"yolov5l", pretrained, channels, classes, autoshape, _verbose, device
|
179 |
+
)
|
180 |
+
|
181 |
+
|
182 |
+
def yolov5x(
|
183 |
+
pretrained=True,
|
184 |
+
channels=3,
|
185 |
+
classes=80,
|
186 |
+
autoshape=True,
|
187 |
+
_verbose=True,
|
188 |
+
device=None,
|
189 |
+
):
|
190 |
+
# YOLOv5-xlarge model https://github.com/ultralytics/yolov5
|
191 |
+
return _create(
|
192 |
+
"yolov5x", pretrained, channels, classes, autoshape, _verbose, device
|
193 |
+
)
|
194 |
+
|
195 |
+
|
196 |
+
def yolov5n6(
|
197 |
+
pretrained=True,
|
198 |
+
channels=3,
|
199 |
+
classes=80,
|
200 |
+
autoshape=True,
|
201 |
+
_verbose=True,
|
202 |
+
device=None,
|
203 |
+
):
|
204 |
+
# YOLOv5-nano-P6 model https://github.com/ultralytics/yolov5
|
205 |
+
return _create(
|
206 |
+
"yolov5n6", pretrained, channels, classes, autoshape, _verbose, device
|
207 |
+
)
|
208 |
+
|
209 |
+
|
210 |
+
def yolov5s6(
|
211 |
+
pretrained=True,
|
212 |
+
channels=3,
|
213 |
+
classes=80,
|
214 |
+
autoshape=True,
|
215 |
+
_verbose=True,
|
216 |
+
device=None,
|
217 |
+
):
|
218 |
+
# YOLOv5-small-P6 model https://github.com/ultralytics/yolov5
|
219 |
+
return _create(
|
220 |
+
"yolov5s6", pretrained, channels, classes, autoshape, _verbose, device
|
221 |
+
)
|
222 |
+
|
223 |
+
|
224 |
+
def yolov5m6(
|
225 |
+
pretrained=True,
|
226 |
+
channels=3,
|
227 |
+
classes=80,
|
228 |
+
autoshape=True,
|
229 |
+
_verbose=True,
|
230 |
+
device=None,
|
231 |
+
):
|
232 |
+
# YOLOv5-medium-P6 model https://github.com/ultralytics/yolov5
|
233 |
+
return _create(
|
234 |
+
"yolov5m6", pretrained, channels, classes, autoshape, _verbose, device
|
235 |
+
)
|
236 |
+
|
237 |
+
|
238 |
+
def yolov5l6(
|
239 |
+
pretrained=True,
|
240 |
+
channels=3,
|
241 |
+
classes=80,
|
242 |
+
autoshape=True,
|
243 |
+
_verbose=True,
|
244 |
+
device=None,
|
245 |
+
):
|
246 |
+
# YOLOv5-large-P6 model https://github.com/ultralytics/yolov5
|
247 |
+
return _create(
|
248 |
+
"yolov5l6", pretrained, channels, classes, autoshape, _verbose, device
|
249 |
+
)
|
250 |
+
|
251 |
+
|
252 |
+
def yolov5x6(
|
253 |
+
pretrained=True,
|
254 |
+
channels=3,
|
255 |
+
classes=80,
|
256 |
+
autoshape=True,
|
257 |
+
_verbose=True,
|
258 |
+
device=None,
|
259 |
+
):
|
260 |
+
# YOLOv5-xlarge-P6 model https://github.com/ultralytics/yolov5
|
261 |
+
return _create(
|
262 |
+
"yolov5x6", pretrained, channels, classes, autoshape, _verbose, device
|
263 |
+
)
|
264 |
+
|
265 |
+
|
266 |
+
if __name__ == "__main__":
|
267 |
+
import argparse
|
268 |
+
from pathlib import Path
|
269 |
+
|
270 |
+
import numpy as np
|
271 |
+
from PIL import Image
|
272 |
+
|
273 |
+
from utils.general import cv2, print_args
|
274 |
+
|
275 |
+
# Argparser
|
276 |
+
parser = argparse.ArgumentParser()
|
277 |
+
parser.add_argument(
|
278 |
+
"--model", type=str, default="yolov5s", help="model name"
|
279 |
+
)
|
280 |
+
opt = parser.parse_args()
|
281 |
+
print_args(vars(opt))
|
282 |
+
|
283 |
+
# Model
|
284 |
+
model = _create(
|
285 |
+
name=opt.model,
|
286 |
+
pretrained=True,
|
287 |
+
channels=3,
|
288 |
+
classes=80,
|
289 |
+
autoshape=True,
|
290 |
+
verbose=True,
|
291 |
+
)
|
292 |
+
# model = custom(path='path/to/model.pt') # custom
|
293 |
+
|
294 |
+
# Images
|
295 |
+
imgs = [
|
296 |
+
"data/images/zidane.jpg", # filename
|
297 |
+
Path("data/images/zidane.jpg"), # Path
|
298 |
+
"https://ultralytics.com/images/zidane.jpg", # URI
|
299 |
+
cv2.imread("data/images/bus.jpg")[:, :, ::-1], # OpenCV
|
300 |
+
Image.open("data/images/bus.jpg"), # PIL
|
301 |
+
np.zeros((320, 640, 3)),
|
302 |
+
] # numpy
|
303 |
+
|
304 |
+
# Inference
|
305 |
+
results = model(imgs, size=320) # batched inference
|
306 |
+
|
307 |
+
# Results
|
308 |
+
results.print()
|
309 |
+
results.save()
|
packages.txt
ADDED
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
ffmpeg
|
2 |
+
libxext6
|
3 |
+
libsm6
|
4 |
+
libxrender1
|
5 |
+
libfontconfig1
|
6 |
+
libice6
|
planogram.yaml
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
|
2 |
+
path: ../planogram_data # dataset root dir
|
3 |
+
train: images/train # train images (relative to 'path') 128 images
|
4 |
+
val: images/val # val images (relative to 'path') 128 images
|
5 |
+
test: images/test # test images (optional)
|
6 |
+
|
7 |
+
# Classes
|
8 |
+
nc: 46 # number of classes
|
9 |
+
names: ['Bottle,100PLUS ACTIVE 1.5L','Bottle,100PLUS ACTIVE 500ML','Bottle,100PLUS LEMON LIME 1.5L',
|
10 |
+
'Bottle,100PLUS ORANGE 500ML', 'Bottle,100PLUS ORIGINAL 1.5L',
|
11 |
+
'Bottle,100PLUS TANGY ORANGE 1.5L','Bottle,100PLUS ZERO 1.5L', 'Bottle,100PLUS ZERO 500ML','Packet,F:M MAGNOLIA CHOC 1L',
|
12 |
+
'Bottle,F&N GINGER ADE 1.5L','Bottle,F&N GRAPE 1.5L','Bottle,F&N ICE CREAM SODA 1.5L','Bottle,F&N LYCHEE PEAR 1.5L','Bottle,F&N ORANGE 1.5L',
|
13 |
+
'Bottle,F&N PINEAPPLE PET 1.5L','Bottle,F&N SARSI 1.5L','Bottle,F&N SS ICE LEM TEA RS 500ML','Bottle,F&N SS ICE LEMON TEA RS 1.5L','Bottle,F&N SS ICE LEMON TEA 1.5L','Bottle,F&N SS ICE LEMON TEA 500ML',
|
14 |
+
'Bottle,F&N SS ICE PEACH TEA 1.5L','Bottle,SS ICE LEMON GT 1.48L','Bottle,SS WHITE CHRYS TEA 1.48L','Packet,FARMHOUSE FRESH MILK 1L FNDM','Packet,FARMHOUSE PLAIN LF 1L',
|
15 |
+
'Packet,PURA FRESH MILK 1L FS','Packet,NUTRISOY REG NO SUGAR ADDED 1L','Packet,NUTRISOY PLAIN 475ML','Packet,NUTRISOY PLAIN 1L','Packet,NUTRISOY OMEGA RD SUGAR 1L','Packet,NUTRISOY OMEGA NSA 1L',
|
16 |
+
'Packet,NUTRISOY ALMOND 1L','Packet,MAGNOLIA FRESH MILK 1L FNDM','Packet,FM MAG FC PLAIN 200ML', 'Packet,MAG OMEGA PLUS PLAIN 200ML','Packet,MAG KURMA MILK 500ML','Packet,MAG KURMA MILK 1L',
|
17 |
+
'Packet,MAG CHOCOLATE FC 500ML','Packet,MAG BROWN SUGAR SS MILK 1L','Packet,FM MAG LFHC PLN 500ML','Packet,FM MAG LFHC OAT 500ML','Packet,FM MAG LFHC OAT 1L','Packet,FM MAG FC PLAIN 500ML',
|
18 |
+
'Void,PARTIAL VOID', 'Void,FULL VOID','Bottle,F&N SS ICE LEM TEA 500ML'] # class names
|
requirements.txt
CHANGED
@@ -38,55 +38,3 @@ pandas>=1.1.4
|
|
38 |
# pycocotools>=2.0 # COCO mAP
|
39 |
# roboflow
|
40 |
#thop # FLOPs computation
|
41 |
-
|
42 |
-
|
43 |
-
# YOLOv5 requirements
|
44 |
-
# Usage: pip install -r requirements.txt
|
45 |
-
|
46 |
-
# Base ------------------------------------------------------------------------
|
47 |
-
gitpython>=3.1.30
|
48 |
-
matplotlib>=3.3
|
49 |
-
numpy>=1.23.5
|
50 |
-
opencv-python>=4.1.1
|
51 |
-
pillow>=10.3.0
|
52 |
-
psutil # system resources
|
53 |
-
PyYAML>=5.3.1
|
54 |
-
requests>=2.23.0
|
55 |
-
scipy>=1.4.1
|
56 |
-
thop>=0.1.1 # FLOPs computation
|
57 |
-
torch>=1.8.0 # see https://pytorch.org/get-started/locally (recommended)
|
58 |
-
torchvision>=0.9.0
|
59 |
-
tqdm>=4.64.0
|
60 |
-
ultralytics>=8.0.232
|
61 |
-
# protobuf<=3.20.1 # https://github.com/ultralytics/yolov5/issues/8012
|
62 |
-
|
63 |
-
# Logging ---------------------------------------------------------------------
|
64 |
-
# tensorboard>=2.4.1
|
65 |
-
# clearml>=1.2.0
|
66 |
-
# comet
|
67 |
-
|
68 |
-
# Plotting --------------------------------------------------------------------
|
69 |
-
pandas>=1.1.4
|
70 |
-
seaborn>=0.11.0
|
71 |
-
|
72 |
-
# Export ----------------------------------------------------------------------
|
73 |
-
# coremltools>=6.0 # CoreML export
|
74 |
-
# onnx>=1.10.0 # ONNX export
|
75 |
-
# onnx-simplifier>=0.4.1 # ONNX simplifier
|
76 |
-
# nvidia-pyindex # TensorRT export
|
77 |
-
# nvidia-tensorrt # TensorRT export
|
78 |
-
# scikit-learn<=1.1.2 # CoreML quantization
|
79 |
-
# tensorflow>=2.4.0,<=2.13.1 # TF exports (-cpu, -aarch64, -macos)
|
80 |
-
# tensorflowjs>=3.9.0 # TF.js export
|
81 |
-
# openvino-dev>=2023.0 # OpenVINO export
|
82 |
-
|
83 |
-
# Deploy ----------------------------------------------------------------------
|
84 |
-
setuptools>=65.5.1 # Snyk vulnerability fix
|
85 |
-
# tritonclient[all]~=2.24.0
|
86 |
-
|
87 |
-
# Extras ----------------------------------------------------------------------
|
88 |
-
# ipython # interactive notebook
|
89 |
-
# mss # screenshots
|
90 |
-
# albumentations>=1.0.3
|
91 |
-
# pycocotools>=2.0.6 # COCO mAP
|
92 |
-
wheel>=0.38.0 # not directly required, pinned by Snyk to avoid a vulnerability
|
|
|
38 |
# pycocotools>=2.0 # COCO mAP
|
39 |
# roboflow
|
40 |
#thop # FLOPs computation
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
runtime.txt
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
python-3.9.0
|
sample_master_planogram.jpeg
ADDED
Git LFS Details
|
sample_planogram.jpg
ADDED
setup.sh
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
mkdir -p ~/.streamlit/
|
2 |
+
echo "\
|
3 |
+
[server]\n\
|
4 |
+
headless = true\n\
|
5 |
+
port = $PORT\n\
|
6 |
+
enableCORS = false\n\
|
7 |
+
\n\
|
8 |
+
" > ~/.streamlit/config.toml
|
test_local_infernce.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
tmp.png
ADDED
Git LFS Details
|
tmp_xml_annotation.xml
ADDED
File without changes
|
train.py
ADDED
@@ -0,0 +1,1046 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
2 |
+
"""
|
3 |
+
Train a YOLOv5 model on a custom dataset
|
4 |
+
|
5 |
+
Usage:
|
6 |
+
$ python path/to/train.py --data coco128.yaml --weights yolov5s.pt --img 640
|
7 |
+
"""
|
8 |
+
|
9 |
+
import argparse
|
10 |
+
import logging
|
11 |
+
import math
|
12 |
+
import os
|
13 |
+
import random
|
14 |
+
import sys
|
15 |
+
import time
|
16 |
+
from copy import deepcopy
|
17 |
+
from pathlib import Path
|
18 |
+
|
19 |
+
import numpy as np
|
20 |
+
import torch
|
21 |
+
import torch.distributed as dist
|
22 |
+
import torch.nn as nn
|
23 |
+
import yaml
|
24 |
+
from torch.cuda import amp
|
25 |
+
from torch.nn.parallel import DistributedDataParallel as DDP
|
26 |
+
from torch.optim import SGD, Adam, lr_scheduler
|
27 |
+
from tqdm import tqdm
|
28 |
+
|
29 |
+
FILE = Path(__file__).absolute()
|
30 |
+
sys.path.append(FILE.parents[0].as_posix()) # add yolov5/ to path
|
31 |
+
|
32 |
+
import val # for end-of-epoch mAP
|
33 |
+
from models.experimental import attempt_load
|
34 |
+
from models.yolo import Model
|
35 |
+
from utils.autoanchor import check_anchors
|
36 |
+
from utils.callbacks import Callbacks
|
37 |
+
from utils.datasets import create_dataloader
|
38 |
+
from utils.downloads import attempt_download
|
39 |
+
from utils.general import (
|
40 |
+
check_dataset,
|
41 |
+
check_file,
|
42 |
+
check_git_status,
|
43 |
+
check_img_size,
|
44 |
+
check_requirements,
|
45 |
+
check_suffix,
|
46 |
+
check_yaml,
|
47 |
+
colorstr,
|
48 |
+
get_latest_run,
|
49 |
+
increment_path,
|
50 |
+
init_seeds,
|
51 |
+
labels_to_class_weights,
|
52 |
+
labels_to_image_weights,
|
53 |
+
methods,
|
54 |
+
one_cycle,
|
55 |
+
print_mutation,
|
56 |
+
set_logging,
|
57 |
+
strip_optimizer,
|
58 |
+
)
|
59 |
+
from utils.loggers import Loggers
|
60 |
+
from utils.loggers.wandb.wandb_utils import check_wandb_resume
|
61 |
+
from utils.loss import ComputeLoss
|
62 |
+
from utils.metrics import fitness
|
63 |
+
from utils.plots import plot_evolve, plot_labels
|
64 |
+
from utils.torch_utils import (
|
65 |
+
EarlyStopping,
|
66 |
+
ModelEMA,
|
67 |
+
de_parallel,
|
68 |
+
intersect_dicts,
|
69 |
+
select_device,
|
70 |
+
torch_distributed_zero_first,
|
71 |
+
)
|
72 |
+
|
73 |
+
LOGGER = logging.getLogger(__name__)
|
74 |
+
LOCAL_RANK = int(
|
75 |
+
os.getenv("LOCAL_RANK", -1)
|
76 |
+
) # https://pytorch.org/docs/stable/elastic/run.html
|
77 |
+
RANK = int(os.getenv("RANK", -1))
|
78 |
+
WORLD_SIZE = int(os.getenv("WORLD_SIZE", 1))
|
79 |
+
|
80 |
+
|
81 |
+
def train(hyp, opt, device, callbacks): # path/to/hyp.yaml or hyp dictionary
|
82 |
+
(
|
83 |
+
save_dir,
|
84 |
+
epochs,
|
85 |
+
batch_size,
|
86 |
+
weights,
|
87 |
+
single_cls,
|
88 |
+
evolve,
|
89 |
+
data,
|
90 |
+
cfg,
|
91 |
+
resume,
|
92 |
+
noval,
|
93 |
+
nosave,
|
94 |
+
workers,
|
95 |
+
freeze,
|
96 |
+
) = (
|
97 |
+
Path(opt.save_dir),
|
98 |
+
opt.epochs,
|
99 |
+
opt.batch_size,
|
100 |
+
opt.weights,
|
101 |
+
opt.single_cls,
|
102 |
+
opt.evolve,
|
103 |
+
opt.data,
|
104 |
+
opt.cfg,
|
105 |
+
opt.resume,
|
106 |
+
opt.noval,
|
107 |
+
opt.nosave,
|
108 |
+
opt.workers,
|
109 |
+
opt.freeze,
|
110 |
+
)
|
111 |
+
|
112 |
+
# Directories
|
113 |
+
w = save_dir / "weights" # weights dir
|
114 |
+
w.mkdir(parents=True, exist_ok=True) # make dir
|
115 |
+
last, best = w / "last.pt", w / "best.pt"
|
116 |
+
|
117 |
+
# Hyperparameters
|
118 |
+
if isinstance(hyp, str):
|
119 |
+
with open(hyp) as f:
|
120 |
+
hyp = yaml.safe_load(f) # load hyps dict
|
121 |
+
LOGGER.info(
|
122 |
+
colorstr("hyperparameters: ")
|
123 |
+
+ ", ".join(f"{k}={v}" for k, v in hyp.items())
|
124 |
+
)
|
125 |
+
|
126 |
+
# Save run settings
|
127 |
+
with open(save_dir / "hyp.yaml", "w") as f:
|
128 |
+
yaml.safe_dump(hyp, f, sort_keys=False)
|
129 |
+
with open(save_dir / "opt.yaml", "w") as f:
|
130 |
+
yaml.safe_dump(vars(opt), f, sort_keys=False)
|
131 |
+
data_dict = None
|
132 |
+
|
133 |
+
# Loggers
|
134 |
+
if RANK in [-1, 0]:
|
135 |
+
loggers = Loggers(
|
136 |
+
save_dir, weights, opt, hyp, LOGGER
|
137 |
+
) # loggers instance
|
138 |
+
if loggers.wandb:
|
139 |
+
data_dict = loggers.wandb.data_dict
|
140 |
+
if resume:
|
141 |
+
weights, epochs, hyp = opt.weights, opt.epochs, opt.hyp
|
142 |
+
|
143 |
+
# Register actions
|
144 |
+
for k in methods(loggers):
|
145 |
+
callbacks.register_action(k, callback=getattr(loggers, k))
|
146 |
+
|
147 |
+
# Config
|
148 |
+
plots = not evolve # create plots
|
149 |
+
cuda = device.type != "cpu"
|
150 |
+
init_seeds(1 + RANK)
|
151 |
+
with torch_distributed_zero_first(RANK):
|
152 |
+
data_dict = data_dict or check_dataset(data) # check if None
|
153 |
+
train_path, val_path = data_dict["train"], data_dict["val"]
|
154 |
+
nc = 1 if single_cls else int(data_dict["nc"]) # number of classes
|
155 |
+
names = (
|
156 |
+
["item"]
|
157 |
+
if single_cls and len(data_dict["names"]) != 1
|
158 |
+
else data_dict["names"]
|
159 |
+
) # class names
|
160 |
+
assert (
|
161 |
+
len(names) == nc
|
162 |
+
), f"{len(names)} names found for nc={nc} dataset in {data}" # check
|
163 |
+
is_coco = data.endswith("coco.yaml") and nc == 80 # COCO dataset
|
164 |
+
|
165 |
+
# Model
|
166 |
+
check_suffix(weights, ".pt") # check weights
|
167 |
+
pretrained = weights.endswith(".pt")
|
168 |
+
if pretrained:
|
169 |
+
with torch_distributed_zero_first(RANK):
|
170 |
+
weights = attempt_download(
|
171 |
+
weights
|
172 |
+
) # download if not found locally
|
173 |
+
ckpt = torch.load(weights, map_location=device) # load checkpoint
|
174 |
+
model = Model(
|
175 |
+
cfg or ckpt["model"].yaml, ch=3, nc=nc, anchors=hyp.get("anchors")
|
176 |
+
).to(
|
177 |
+
device
|
178 |
+
) # create
|
179 |
+
exclude = (
|
180 |
+
["anchor"] if (cfg or hyp.get("anchors")) and not resume else []
|
181 |
+
) # exclude keys
|
182 |
+
csd = (
|
183 |
+
ckpt["model"].float().state_dict()
|
184 |
+
) # checkpoint state_dict as FP32
|
185 |
+
csd = intersect_dicts(
|
186 |
+
csd, model.state_dict(), exclude=exclude
|
187 |
+
) # intersect
|
188 |
+
model.load_state_dict(csd, strict=False) # load
|
189 |
+
LOGGER.info(
|
190 |
+
f"Transferred {len(csd)}/{len(model.state_dict())} items from {weights}"
|
191 |
+
) # report
|
192 |
+
else:
|
193 |
+
model = Model(cfg, ch=3, nc=nc, anchors=hyp.get("anchors")).to(
|
194 |
+
device
|
195 |
+
) # create
|
196 |
+
|
197 |
+
# Freeze
|
198 |
+
freeze = [f"model.{x}." for x in range(freeze)] # layers to freeze
|
199 |
+
for k, v in model.named_parameters():
|
200 |
+
v.requires_grad = True # train all layers
|
201 |
+
if any(x in k for x in freeze):
|
202 |
+
print(f"freezing {k}")
|
203 |
+
v.requires_grad = False
|
204 |
+
|
205 |
+
# Optimizer
|
206 |
+
nbs = 64 # nominal batch size
|
207 |
+
accumulate = max(
|
208 |
+
round(nbs / batch_size), 1
|
209 |
+
) # accumulate loss before optimizing
|
210 |
+
hyp["weight_decay"] *= batch_size * accumulate / nbs # scale weight_decay
|
211 |
+
LOGGER.info(f"Scaled weight_decay = {hyp['weight_decay']}")
|
212 |
+
|
213 |
+
g0, g1, g2 = [], [], [] # optimizer parameter groups
|
214 |
+
for v in model.modules():
|
215 |
+
if hasattr(v, "bias") and isinstance(v.bias, nn.Parameter): # bias
|
216 |
+
g2.append(v.bias)
|
217 |
+
if isinstance(v, nn.BatchNorm2d): # weight (no decay)
|
218 |
+
g0.append(v.weight)
|
219 |
+
elif hasattr(v, "weight") and isinstance(
|
220 |
+
v.weight, nn.Parameter
|
221 |
+
): # weight (with decay)
|
222 |
+
g1.append(v.weight)
|
223 |
+
|
224 |
+
if opt.adam:
|
225 |
+
optimizer = Adam(
|
226 |
+
g0, lr=hyp["lr0"], betas=(hyp["momentum"], 0.999)
|
227 |
+
) # adjust beta1 to momentum
|
228 |
+
else:
|
229 |
+
optimizer = SGD(
|
230 |
+
g0, lr=hyp["lr0"], momentum=hyp["momentum"], nesterov=True
|
231 |
+
)
|
232 |
+
|
233 |
+
optimizer.add_param_group(
|
234 |
+
{"params": g1, "weight_decay": hyp["weight_decay"]}
|
235 |
+
) # add g1 with weight_decay
|
236 |
+
optimizer.add_param_group({"params": g2}) # add g2 (biases)
|
237 |
+
LOGGER.info(
|
238 |
+
f"{colorstr('optimizer:')} {type(optimizer).__name__} with parameter groups "
|
239 |
+
f"{len(g0)} weight, {len(g1)} weight (no decay), {len(g2)} bias"
|
240 |
+
)
|
241 |
+
del g0, g1, g2
|
242 |
+
|
243 |
+
# Scheduler
|
244 |
+
if opt.linear_lr:
|
245 |
+
lf = (
|
246 |
+
lambda x: (1 - x / (epochs - 1)) * (1.0 - hyp["lrf"]) + hyp["lrf"]
|
247 |
+
) # linear
|
248 |
+
else:
|
249 |
+
lf = one_cycle(1, hyp["lrf"], epochs) # cosine 1->hyp['lrf']
|
250 |
+
scheduler = lr_scheduler.LambdaLR(
|
251 |
+
optimizer, lr_lambda=lf
|
252 |
+
) # plot_lr_scheduler(optimizer, scheduler, epochs)
|
253 |
+
|
254 |
+
# EMA
|
255 |
+
ema = ModelEMA(model) if RANK in [-1, 0] else None
|
256 |
+
|
257 |
+
# Resume
|
258 |
+
start_epoch, best_fitness = 0, 0.0
|
259 |
+
if pretrained:
|
260 |
+
# Optimizer
|
261 |
+
if ckpt["optimizer"] is not None:
|
262 |
+
optimizer.load_state_dict(ckpt["optimizer"])
|
263 |
+
best_fitness = ckpt["best_fitness"]
|
264 |
+
|
265 |
+
# EMA
|
266 |
+
if ema and ckpt.get("ema"):
|
267 |
+
ema.ema.load_state_dict(ckpt["ema"].float().state_dict())
|
268 |
+
ema.updates = ckpt["updates"]
|
269 |
+
|
270 |
+
# Epochs
|
271 |
+
start_epoch = ckpt["epoch"] + 1
|
272 |
+
if resume:
|
273 |
+
assert (
|
274 |
+
start_epoch > 0
|
275 |
+
), f"{weights} training to {epochs} epochs is finished, nothing to resume."
|
276 |
+
if epochs < start_epoch:
|
277 |
+
LOGGER.info(
|
278 |
+
f"{weights} has been trained for {ckpt['epoch']} epochs. Fine-tuning for {epochs} more epochs."
|
279 |
+
)
|
280 |
+
epochs += ckpt["epoch"] # finetune additional epochs
|
281 |
+
|
282 |
+
del ckpt, csd
|
283 |
+
|
284 |
+
# Image sizes
|
285 |
+
gs = max(int(model.stride.max()), 32) # grid size (max stride)
|
286 |
+
nl = model.model[
|
287 |
+
-1
|
288 |
+
].nl # number of detection layers (used for scaling hyp['obj'])
|
289 |
+
imgsz = check_img_size(
|
290 |
+
opt.imgsz, gs, floor=gs * 2
|
291 |
+
) # verify imgsz is gs-multiple
|
292 |
+
|
293 |
+
# DP mode
|
294 |
+
if cuda and RANK == -1 and torch.cuda.device_count() > 1:
|
295 |
+
logging.warning(
|
296 |
+
"DP not recommended, instead use torch.distributed.run for best DDP Multi-GPU results.\n"
|
297 |
+
"See Multi-GPU Tutorial at https://github.com/ultralytics/yolov5/issues/475 to get started."
|
298 |
+
)
|
299 |
+
model = torch.nn.DataParallel(model)
|
300 |
+
|
301 |
+
# SyncBatchNorm
|
302 |
+
if opt.sync_bn and cuda and RANK != -1:
|
303 |
+
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model).to(device)
|
304 |
+
LOGGER.info("Using SyncBatchNorm()")
|
305 |
+
|
306 |
+
# Trainloader
|
307 |
+
train_loader, dataset = create_dataloader(
|
308 |
+
train_path,
|
309 |
+
imgsz,
|
310 |
+
batch_size // WORLD_SIZE,
|
311 |
+
gs,
|
312 |
+
single_cls,
|
313 |
+
hyp=hyp,
|
314 |
+
augment=True,
|
315 |
+
cache=opt.cache,
|
316 |
+
rect=opt.rect,
|
317 |
+
rank=RANK,
|
318 |
+
workers=workers,
|
319 |
+
image_weights=opt.image_weights,
|
320 |
+
quad=opt.quad,
|
321 |
+
prefix=colorstr("train: "),
|
322 |
+
)
|
323 |
+
mlc = int(np.concatenate(dataset.labels, 0)[:, 0].max()) # max label class
|
324 |
+
nb = len(train_loader) # number of batches
|
325 |
+
assert (
|
326 |
+
mlc < nc
|
327 |
+
), f"Label class {mlc} exceeds nc={nc} in {data}. Possible class labels are 0-{nc - 1}"
|
328 |
+
|
329 |
+
# Process 0
|
330 |
+
if RANK in [-1, 0]:
|
331 |
+
val_loader = create_dataloader(
|
332 |
+
val_path,
|
333 |
+
imgsz,
|
334 |
+
batch_size // WORLD_SIZE * 2,
|
335 |
+
gs,
|
336 |
+
single_cls,
|
337 |
+
hyp=hyp,
|
338 |
+
cache=None if noval else opt.cache,
|
339 |
+
rect=True,
|
340 |
+
rank=-1,
|
341 |
+
workers=workers,
|
342 |
+
pad=0.5,
|
343 |
+
prefix=colorstr("val: "),
|
344 |
+
)[0]
|
345 |
+
|
346 |
+
if not resume:
|
347 |
+
labels = np.concatenate(dataset.labels, 0)
|
348 |
+
# c = torch.tensor(labels[:, 0]) # classes
|
349 |
+
# cf = torch.bincount(c.long(), minlength=nc) + 1. # frequency
|
350 |
+
# model._initialize_biases(cf.to(device))
|
351 |
+
if plots:
|
352 |
+
plot_labels(labels, names, save_dir)
|
353 |
+
|
354 |
+
# Anchors
|
355 |
+
if not opt.noautoanchor:
|
356 |
+
check_anchors(
|
357 |
+
dataset, model=model, thr=hyp["anchor_t"], imgsz=imgsz
|
358 |
+
)
|
359 |
+
model.half().float() # pre-reduce anchor precision
|
360 |
+
|
361 |
+
callbacks.run("on_pretrain_routine_end")
|
362 |
+
|
363 |
+
# DDP mode
|
364 |
+
if cuda and RANK != -1:
|
365 |
+
model = DDP(model, device_ids=[LOCAL_RANK], output_device=LOCAL_RANK)
|
366 |
+
|
367 |
+
# Model parameters
|
368 |
+
hyp["box"] *= 3.0 / nl # scale to layers
|
369 |
+
hyp["cls"] *= nc / 80.0 * 3.0 / nl # scale to classes and layers
|
370 |
+
hyp["obj"] *= (
|
371 |
+
(imgsz / 640) ** 2 * 3.0 / nl
|
372 |
+
) # scale to image size and layers
|
373 |
+
hyp["label_smoothing"] = opt.label_smoothing
|
374 |
+
model.nc = nc # attach number of classes to model
|
375 |
+
model.hyp = hyp # attach hyperparameters to model
|
376 |
+
model.class_weights = (
|
377 |
+
labels_to_class_weights(dataset.labels, nc).to(device) * nc
|
378 |
+
) # attach class weights
|
379 |
+
model.names = names
|
380 |
+
|
381 |
+
# Start training
|
382 |
+
t0 = time.time()
|
383 |
+
nw = max(
|
384 |
+
round(hyp["warmup_epochs"] * nb), 1000
|
385 |
+
) # number of warmup iterations, max(3 epochs, 1k iterations)
|
386 |
+
# nw = min(nw, (epochs - start_epoch) / 2 * nb) # limit warmup to < 1/2 of training
|
387 |
+
last_opt_step = -1
|
388 |
+
maps = np.zeros(nc) # mAP per class
|
389 |
+
results = (
|
390 |
+
0,
|
391 |
+
0,
|
392 |
+
0,
|
393 |
+
0,
|
394 |
+
0,
|
395 |
+
0,
|
396 |
+
0,
|
397 |
+
) # P, R, mAP@.5, mAP@.5-.95, val_loss(box, obj, cls)
|
398 |
+
scheduler.last_epoch = start_epoch - 1 # do not move
|
399 |
+
scaler = amp.GradScaler(enabled=cuda)
|
400 |
+
stopper = EarlyStopping(patience=opt.patience)
|
401 |
+
compute_loss = ComputeLoss(model) # init loss class
|
402 |
+
LOGGER.info(
|
403 |
+
f"Image sizes {imgsz} train, {imgsz} val\n"
|
404 |
+
f"Using {train_loader.num_workers} dataloader workers\n"
|
405 |
+
f"Logging results to {colorstr('bold', save_dir)}\n"
|
406 |
+
f"Starting training for {epochs} epochs..."
|
407 |
+
)
|
408 |
+
for epoch in range(
|
409 |
+
start_epoch, epochs
|
410 |
+
): # epoch ------------------------------------------------------------------
|
411 |
+
model.train()
|
412 |
+
|
413 |
+
# Update image weights (optional, single-GPU only)
|
414 |
+
if opt.image_weights:
|
415 |
+
cw = (
|
416 |
+
model.class_weights.cpu().numpy() * (1 - maps) ** 2 / nc
|
417 |
+
) # class weights
|
418 |
+
iw = labels_to_image_weights(
|
419 |
+
dataset.labels, nc=nc, class_weights=cw
|
420 |
+
) # image weights
|
421 |
+
dataset.indices = random.choices(
|
422 |
+
range(dataset.n), weights=iw, k=dataset.n
|
423 |
+
) # rand weighted idx
|
424 |
+
|
425 |
+
# Update mosaic border (optional)
|
426 |
+
# b = int(random.uniform(0.25 * imgsz, 0.75 * imgsz + gs) // gs * gs)
|
427 |
+
# dataset.mosaic_border = [b - imgsz, -b] # height, width borders
|
428 |
+
|
429 |
+
mloss = torch.zeros(3, device=device) # mean losses
|
430 |
+
if RANK != -1:
|
431 |
+
train_loader.sampler.set_epoch(epoch)
|
432 |
+
pbar = enumerate(train_loader)
|
433 |
+
LOGGER.info(
|
434 |
+
("\n" + "%10s" * 7)
|
435 |
+
% ("Epoch", "gpu_mem", "box", "obj", "cls", "labels", "img_size")
|
436 |
+
)
|
437 |
+
if RANK in [-1, 0]:
|
438 |
+
pbar = tqdm(pbar, total=nb) # progress bar
|
439 |
+
optimizer.zero_grad()
|
440 |
+
for i, (
|
441 |
+
imgs,
|
442 |
+
targets,
|
443 |
+
paths,
|
444 |
+
_,
|
445 |
+
) in (
|
446 |
+
pbar
|
447 |
+
): # batch -------------------------------------------------------------
|
448 |
+
ni = (
|
449 |
+
i + nb * epoch
|
450 |
+
) # number integrated batches (since train start)
|
451 |
+
imgs = (
|
452 |
+
imgs.to(device, non_blocking=True).float() / 255.0
|
453 |
+
) # uint8 to float32, 0-255 to 0.0-1.0
|
454 |
+
|
455 |
+
# Warmup
|
456 |
+
if ni <= nw:
|
457 |
+
xi = [0, nw] # x interp
|
458 |
+
# compute_loss.gr = np.interp(ni, xi, [0.0, 1.0]) # iou loss ratio (obj_loss = 1.0 or iou)
|
459 |
+
accumulate = max(
|
460 |
+
1, np.interp(ni, xi, [1, nbs / batch_size]).round()
|
461 |
+
)
|
462 |
+
for j, x in enumerate(optimizer.param_groups):
|
463 |
+
# bias lr falls from 0.1 to lr0, all other lrs rise from 0.0 to lr0
|
464 |
+
x["lr"] = np.interp(
|
465 |
+
ni,
|
466 |
+
xi,
|
467 |
+
[
|
468 |
+
hyp["warmup_bias_lr"] if j == 2 else 0.0,
|
469 |
+
x["initial_lr"] * lf(epoch),
|
470 |
+
],
|
471 |
+
)
|
472 |
+
if "momentum" in x:
|
473 |
+
x["momentum"] = np.interp(
|
474 |
+
ni, xi, [hyp["warmup_momentum"], hyp["momentum"]]
|
475 |
+
)
|
476 |
+
|
477 |
+
# Multi-scale
|
478 |
+
if opt.multi_scale:
|
479 |
+
sz = (
|
480 |
+
random.randrange(imgsz * 0.5, imgsz * 1.5 + gs) // gs * gs
|
481 |
+
) # size
|
482 |
+
sf = sz / max(imgs.shape[2:]) # scale factor
|
483 |
+
if sf != 1:
|
484 |
+
ns = [
|
485 |
+
math.ceil(x * sf / gs) * gs for x in imgs.shape[2:]
|
486 |
+
] # new shape (stretched to gs-multiple)
|
487 |
+
imgs = nn.functional.interpolate(
|
488 |
+
imgs, size=ns, mode="bilinear", align_corners=False
|
489 |
+
)
|
490 |
+
|
491 |
+
# Forward
|
492 |
+
with amp.autocast(enabled=cuda):
|
493 |
+
pred = model(imgs) # forward
|
494 |
+
loss, loss_items = compute_loss(
|
495 |
+
pred, targets.to(device)
|
496 |
+
) # loss scaled by batch_size
|
497 |
+
if RANK != -1:
|
498 |
+
loss *= WORLD_SIZE # gradient averaged between devices in DDP mode
|
499 |
+
if opt.quad:
|
500 |
+
loss *= 4.0
|
501 |
+
|
502 |
+
# Backward
|
503 |
+
scaler.scale(loss).backward()
|
504 |
+
|
505 |
+
# Optimize
|
506 |
+
if ni - last_opt_step >= accumulate:
|
507 |
+
scaler.step(optimizer) # optimizer.step
|
508 |
+
scaler.update()
|
509 |
+
optimizer.zero_grad()
|
510 |
+
if ema:
|
511 |
+
ema.update(model)
|
512 |
+
last_opt_step = ni
|
513 |
+
|
514 |
+
# Log
|
515 |
+
if RANK in [-1, 0]:
|
516 |
+
mloss = (mloss * i + loss_items) / (
|
517 |
+
i + 1
|
518 |
+
) # update mean losses
|
519 |
+
mem = f"{torch.cuda.memory_reserved() / 1E9 if torch.cuda.is_available() else 0:.3g}G" # (GB)
|
520 |
+
pbar.set_description(
|
521 |
+
("%10s" * 2 + "%10.4g" * 5)
|
522 |
+
% (
|
523 |
+
f"{epoch}/{epochs - 1}",
|
524 |
+
mem,
|
525 |
+
*mloss,
|
526 |
+
targets.shape[0],
|
527 |
+
imgs.shape[-1],
|
528 |
+
)
|
529 |
+
)
|
530 |
+
callbacks.run(
|
531 |
+
"on_train_batch_end",
|
532 |
+
ni,
|
533 |
+
model,
|
534 |
+
imgs,
|
535 |
+
targets,
|
536 |
+
paths,
|
537 |
+
plots,
|
538 |
+
opt.sync_bn,
|
539 |
+
)
|
540 |
+
# end batch ------------------------------------------------------------------------------------------------
|
541 |
+
|
542 |
+
# Scheduler
|
543 |
+
lr = [x["lr"] for x in optimizer.param_groups] # for loggers
|
544 |
+
scheduler.step()
|
545 |
+
|
546 |
+
if RANK in [-1, 0]:
|
547 |
+
# mAP
|
548 |
+
callbacks.run("on_train_epoch_end", epoch=epoch)
|
549 |
+
ema.update_attr(
|
550 |
+
model,
|
551 |
+
include=[
|
552 |
+
"yaml",
|
553 |
+
"nc",
|
554 |
+
"hyp",
|
555 |
+
"names",
|
556 |
+
"stride",
|
557 |
+
"class_weights",
|
558 |
+
],
|
559 |
+
)
|
560 |
+
final_epoch = (epoch + 1 == epochs) or stopper.possible_stop
|
561 |
+
if not noval or final_epoch: # Calculate mAP
|
562 |
+
results, maps, _ = val.run(
|
563 |
+
data_dict,
|
564 |
+
batch_size=batch_size // WORLD_SIZE * 2,
|
565 |
+
imgsz=imgsz,
|
566 |
+
model=ema.ema,
|
567 |
+
single_cls=single_cls,
|
568 |
+
dataloader=val_loader,
|
569 |
+
save_dir=save_dir,
|
570 |
+
save_json=is_coco and final_epoch,
|
571 |
+
verbose=nc < 50 and final_epoch,
|
572 |
+
plots=plots and final_epoch,
|
573 |
+
callbacks=callbacks,
|
574 |
+
compute_loss=compute_loss,
|
575 |
+
)
|
576 |
+
|
577 |
+
# Update best mAP
|
578 |
+
fi = fitness(
|
579 |
+
np.array(results).reshape(1, -1)
|
580 |
+
) # weighted combination of [P, R, mAP@.5, mAP@.5-.95]
|
581 |
+
if fi > best_fitness:
|
582 |
+
best_fitness = fi
|
583 |
+
log_vals = list(mloss) + list(results) + lr
|
584 |
+
callbacks.run(
|
585 |
+
"on_fit_epoch_end", log_vals, epoch, best_fitness, fi
|
586 |
+
)
|
587 |
+
|
588 |
+
# Save model
|
589 |
+
if (not nosave) or (final_epoch and not evolve): # if save
|
590 |
+
ckpt = {
|
591 |
+
"epoch": epoch,
|
592 |
+
"best_fitness": best_fitness,
|
593 |
+
"model": deepcopy(de_parallel(model)).half(),
|
594 |
+
"ema": deepcopy(ema.ema).half(),
|
595 |
+
"updates": ema.updates,
|
596 |
+
"optimizer": optimizer.state_dict(),
|
597 |
+
"wandb_id": loggers.wandb.wandb_run.id
|
598 |
+
if loggers.wandb
|
599 |
+
else None,
|
600 |
+
}
|
601 |
+
|
602 |
+
# Save last, best and delete
|
603 |
+
torch.save(ckpt, last)
|
604 |
+
if best_fitness == fi:
|
605 |
+
torch.save(ckpt, best)
|
606 |
+
del ckpt
|
607 |
+
callbacks.run(
|
608 |
+
"on_model_save", last, epoch, final_epoch, best_fitness, fi
|
609 |
+
)
|
610 |
+
|
611 |
+
# Stop Single-GPU
|
612 |
+
if RANK == -1 and stopper(epoch=epoch, fitness=fi):
|
613 |
+
break
|
614 |
+
|
615 |
+
# Stop DDP TODO: known issues shttps://github.com/ultralytics/yolov5/pull/4576
|
616 |
+
# stop = stopper(epoch=epoch, fitness=fi)
|
617 |
+
# if RANK == 0:
|
618 |
+
# dist.broadcast_object_list([stop], 0) # broadcast 'stop' to all ranks
|
619 |
+
|
620 |
+
# Stop DPP
|
621 |
+
# with torch_distributed_zero_first(RANK):
|
622 |
+
# if stop:
|
623 |
+
# break # must break all DDP ranks
|
624 |
+
|
625 |
+
# end epoch ----------------------------------------------------------------------------------------------------
|
626 |
+
# end training -----------------------------------------------------------------------------------------------------
|
627 |
+
if RANK in [-1, 0]:
|
628 |
+
LOGGER.info(
|
629 |
+
f"\n{epoch - start_epoch + 1} epochs completed in {(time.time() - t0) / 3600:.3f} hours."
|
630 |
+
)
|
631 |
+
if not evolve:
|
632 |
+
if is_coco: # COCO dataset
|
633 |
+
for m in (
|
634 |
+
[last, best] if best.exists() else [last]
|
635 |
+
): # speed, mAP tests
|
636 |
+
results, _, _ = val.run(
|
637 |
+
data_dict,
|
638 |
+
batch_size=batch_size // WORLD_SIZE * 2,
|
639 |
+
imgsz=imgsz,
|
640 |
+
model=attempt_load(m, device).half(),
|
641 |
+
iou_thres=0.7, # NMS IoU threshold for best pycocotools results
|
642 |
+
single_cls=single_cls,
|
643 |
+
dataloader=val_loader,
|
644 |
+
save_dir=save_dir,
|
645 |
+
save_json=True,
|
646 |
+
plots=False,
|
647 |
+
)
|
648 |
+
# Strip optimizers
|
649 |
+
for f in last, best:
|
650 |
+
if f.exists():
|
651 |
+
strip_optimizer(f) # strip optimizers
|
652 |
+
callbacks.run("on_train_end", last, best, plots, epoch)
|
653 |
+
LOGGER.info(f"Results saved to {colorstr('bold', save_dir)}")
|
654 |
+
|
655 |
+
torch.cuda.empty_cache()
|
656 |
+
return results
|
657 |
+
|
658 |
+
|
659 |
+
def parse_opt(known=False):
|
660 |
+
parser = argparse.ArgumentParser()
|
661 |
+
parser.add_argument(
|
662 |
+
"--weights",
|
663 |
+
type=str,
|
664 |
+
default="yolov5s.pt",
|
665 |
+
help="initial weights path",
|
666 |
+
)
|
667 |
+
parser.add_argument("--cfg", type=str, default="", help="model.yaml path")
|
668 |
+
parser.add_argument(
|
669 |
+
"--data",
|
670 |
+
type=str,
|
671 |
+
default="data/coco128.yaml",
|
672 |
+
help="dataset.yaml path",
|
673 |
+
)
|
674 |
+
parser.add_argument(
|
675 |
+
"--hyp",
|
676 |
+
type=str,
|
677 |
+
default="data/hyps/hyp.scratch.yaml",
|
678 |
+
help="hyperparameters path",
|
679 |
+
)
|
680 |
+
parser.add_argument("--epochs", type=int, default=300)
|
681 |
+
parser.add_argument(
|
682 |
+
"--batch-size",
|
683 |
+
type=int,
|
684 |
+
default=16,
|
685 |
+
help="total batch size for all GPUs",
|
686 |
+
)
|
687 |
+
parser.add_argument(
|
688 |
+
"--imgsz",
|
689 |
+
"--img",
|
690 |
+
"--img-size",
|
691 |
+
type=int,
|
692 |
+
default=640,
|
693 |
+
help="train, val image size (pixels)",
|
694 |
+
)
|
695 |
+
parser.add_argument(
|
696 |
+
"--rect", action="store_true", help="rectangular training"
|
697 |
+
)
|
698 |
+
parser.add_argument(
|
699 |
+
"--resume",
|
700 |
+
nargs="?",
|
701 |
+
const=True,
|
702 |
+
default=False,
|
703 |
+
help="resume most recent training",
|
704 |
+
)
|
705 |
+
parser.add_argument(
|
706 |
+
"--nosave", action="store_true", help="only save final checkpoint"
|
707 |
+
)
|
708 |
+
parser.add_argument(
|
709 |
+
"--noval", action="store_true", help="only validate final epoch"
|
710 |
+
)
|
711 |
+
parser.add_argument(
|
712 |
+
"--noautoanchor", action="store_true", help="disable autoanchor check"
|
713 |
+
)
|
714 |
+
parser.add_argument(
|
715 |
+
"--evolve",
|
716 |
+
type=int,
|
717 |
+
nargs="?",
|
718 |
+
const=300,
|
719 |
+
help="evolve hyperparameters for x generations",
|
720 |
+
)
|
721 |
+
parser.add_argument("--bucket", type=str, default="", help="gsutil bucket")
|
722 |
+
parser.add_argument(
|
723 |
+
"--cache",
|
724 |
+
type=str,
|
725 |
+
nargs="?",
|
726 |
+
const="ram",
|
727 |
+
help='--cache images in "ram" (default) or "disk"',
|
728 |
+
)
|
729 |
+
parser.add_argument(
|
730 |
+
"--image-weights",
|
731 |
+
action="store_true",
|
732 |
+
help="use weighted image selection for training",
|
733 |
+
)
|
734 |
+
parser.add_argument(
|
735 |
+
"--device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu"
|
736 |
+
)
|
737 |
+
parser.add_argument(
|
738 |
+
"--multi-scale", action="store_true", help="vary img-size +/- 50%%"
|
739 |
+
)
|
740 |
+
parser.add_argument(
|
741 |
+
"--single-cls",
|
742 |
+
action="store_true",
|
743 |
+
help="train multi-class data as single-class",
|
744 |
+
)
|
745 |
+
parser.add_argument(
|
746 |
+
"--adam", action="store_true", help="use torch.optim.Adam() optimizer"
|
747 |
+
)
|
748 |
+
parser.add_argument(
|
749 |
+
"--sync-bn",
|
750 |
+
action="store_true",
|
751 |
+
help="use SyncBatchNorm, only available in DDP mode",
|
752 |
+
)
|
753 |
+
parser.add_argument(
|
754 |
+
"--workers",
|
755 |
+
type=int,
|
756 |
+
default=8,
|
757 |
+
help="maximum number of dataloader workers",
|
758 |
+
)
|
759 |
+
parser.add_argument(
|
760 |
+
"--project", default="runs/train", help="save to project/name"
|
761 |
+
)
|
762 |
+
parser.add_argument("--entity", default=None, help="W&B entity")
|
763 |
+
parser.add_argument("--name", default="exp", help="save to project/name")
|
764 |
+
parser.add_argument(
|
765 |
+
"--exist-ok",
|
766 |
+
action="store_true",
|
767 |
+
help="existing project/name ok, do not increment",
|
768 |
+
)
|
769 |
+
parser.add_argument("--quad", action="store_true", help="quad dataloader")
|
770 |
+
parser.add_argument("--linear-lr", action="store_true", help="linear LR")
|
771 |
+
parser.add_argument(
|
772 |
+
"--label-smoothing",
|
773 |
+
type=float,
|
774 |
+
default=0.0,
|
775 |
+
help="Label smoothing epsilon",
|
776 |
+
)
|
777 |
+
parser.add_argument(
|
778 |
+
"--upload_dataset",
|
779 |
+
action="store_true",
|
780 |
+
help="Upload dataset as W&B artifact table",
|
781 |
+
)
|
782 |
+
parser.add_argument(
|
783 |
+
"--bbox_interval",
|
784 |
+
type=int,
|
785 |
+
default=-1,
|
786 |
+
help="Set bounding-box image logging interval for W&B",
|
787 |
+
)
|
788 |
+
parser.add_argument(
|
789 |
+
"--save_period",
|
790 |
+
type=int,
|
791 |
+
default=-1,
|
792 |
+
help='Log model after every "save_period" epoch',
|
793 |
+
)
|
794 |
+
parser.add_argument(
|
795 |
+
"--artifact_alias",
|
796 |
+
type=str,
|
797 |
+
default="latest",
|
798 |
+
help="version of dataset artifact to be used",
|
799 |
+
)
|
800 |
+
parser.add_argument(
|
801 |
+
"--local_rank",
|
802 |
+
type=int,
|
803 |
+
default=-1,
|
804 |
+
help="DDP parameter, do not modify",
|
805 |
+
)
|
806 |
+
parser.add_argument(
|
807 |
+
"--freeze",
|
808 |
+
type=int,
|
809 |
+
default=0,
|
810 |
+
help="Number of layers to freeze. backbone=10, all=24",
|
811 |
+
)
|
812 |
+
parser.add_argument(
|
813 |
+
"--patience",
|
814 |
+
type=int,
|
815 |
+
default=100,
|
816 |
+
help="EarlyStopping patience (epochs without improvement)",
|
817 |
+
)
|
818 |
+
opt = parser.parse_known_args()[0] if known else parser.parse_args()
|
819 |
+
return opt
|
820 |
+
|
821 |
+
|
822 |
+
def main(opt, callbacks=Callbacks()):
|
823 |
+
# Checks
|
824 |
+
set_logging(RANK)
|
825 |
+
if RANK in [-1, 0]:
|
826 |
+
print(
|
827 |
+
colorstr("train: ")
|
828 |
+
+ ", ".join(f"{k}={v}" for k, v in vars(opt).items())
|
829 |
+
)
|
830 |
+
check_git_status()
|
831 |
+
check_requirements(
|
832 |
+
requirements=FILE.parent / "requirements.txt", exclude=["thop"]
|
833 |
+
)
|
834 |
+
|
835 |
+
# Resume
|
836 |
+
if (
|
837 |
+
opt.resume and not check_wandb_resume(opt) and not opt.evolve
|
838 |
+
): # resume an interrupted run
|
839 |
+
ckpt = (
|
840 |
+
opt.resume if isinstance(opt.resume, str) else get_latest_run()
|
841 |
+
) # specified or most recent path
|
842 |
+
assert os.path.isfile(
|
843 |
+
ckpt
|
844 |
+
), "ERROR: --resume checkpoint does not exist"
|
845 |
+
with open(Path(ckpt).parent.parent / "opt.yaml") as f:
|
846 |
+
opt = argparse.Namespace(**yaml.safe_load(f)) # replace
|
847 |
+
opt.cfg, opt.weights, opt.resume = "", ckpt, True # reinstate
|
848 |
+
LOGGER.info(f"Resuming training from {ckpt}")
|
849 |
+
else:
|
850 |
+
opt.data, opt.cfg, opt.hyp = (
|
851 |
+
check_file(opt.data),
|
852 |
+
check_yaml(opt.cfg),
|
853 |
+
check_yaml(opt.hyp),
|
854 |
+
) # check YAMLs
|
855 |
+
assert len(opt.cfg) or len(
|
856 |
+
opt.weights
|
857 |
+
), "either --cfg or --weights must be specified"
|
858 |
+
if opt.evolve:
|
859 |
+
opt.project = "runs/evolve"
|
860 |
+
opt.exist_ok = opt.resume
|
861 |
+
opt.save_dir = str(
|
862 |
+
increment_path(Path(opt.project) / opt.name, exist_ok=opt.exist_ok)
|
863 |
+
)
|
864 |
+
|
865 |
+
# DDP mode
|
866 |
+
device = select_device(opt.device, batch_size=opt.batch_size)
|
867 |
+
if LOCAL_RANK != -1:
|
868 |
+
from datetime import timedelta
|
869 |
+
|
870 |
+
assert (
|
871 |
+
torch.cuda.device_count() > LOCAL_RANK
|
872 |
+
), "insufficient CUDA devices for DDP command"
|
873 |
+
assert (
|
874 |
+
opt.batch_size % WORLD_SIZE == 0
|
875 |
+
), "--batch-size must be multiple of CUDA device count"
|
876 |
+
assert (
|
877 |
+
not opt.image_weights
|
878 |
+
), "--image-weights argument is not compatible with DDP training"
|
879 |
+
assert (
|
880 |
+
not opt.evolve
|
881 |
+
), "--evolve argument is not compatible with DDP training"
|
882 |
+
torch.cuda.set_device(LOCAL_RANK)
|
883 |
+
device = torch.device("cuda", LOCAL_RANK)
|
884 |
+
dist.init_process_group(
|
885 |
+
backend="nccl" if dist.is_nccl_available() else "gloo"
|
886 |
+
)
|
887 |
+
|
888 |
+
# Train
|
889 |
+
if not opt.evolve:
|
890 |
+
train(opt.hyp, opt, device, callbacks)
|
891 |
+
if WORLD_SIZE > 1 and RANK == 0:
|
892 |
+
_ = [
|
893 |
+
print("Destroying process group... ", end=""),
|
894 |
+
dist.destroy_process_group(),
|
895 |
+
print("Done."),
|
896 |
+
]
|
897 |
+
|
898 |
+
# Evolve hyperparameters (optional)
|
899 |
+
else:
|
900 |
+
# Hyperparameter evolution metadata (mutation scale 0-1, lower_limit, upper_limit)
|
901 |
+
meta = {
|
902 |
+
"lr0": (
|
903 |
+
1,
|
904 |
+
1e-5,
|
905 |
+
1e-1,
|
906 |
+
), # initial learning rate (SGD=1E-2, Adam=1E-3)
|
907 |
+
"lrf": (
|
908 |
+
1,
|
909 |
+
0.01,
|
910 |
+
1.0,
|
911 |
+
), # final OneCycleLR learning rate (lr0 * lrf)
|
912 |
+
"momentum": (0.3, 0.6, 0.98), # SGD momentum/Adam beta1
|
913 |
+
"weight_decay": (1, 0.0, 0.001), # optimizer weight decay
|
914 |
+
"warmup_epochs": (1, 0.0, 5.0), # warmup epochs (fractions ok)
|
915 |
+
"warmup_momentum": (1, 0.0, 0.95), # warmup initial momentum
|
916 |
+
"warmup_bias_lr": (1, 0.0, 0.2), # warmup initial bias lr
|
917 |
+
"box": (1, 0.02, 0.2), # box loss gain
|
918 |
+
"cls": (1, 0.2, 4.0), # cls loss gain
|
919 |
+
"cls_pw": (1, 0.5, 2.0), # cls BCELoss positive_weight
|
920 |
+
"obj": (1, 0.2, 4.0), # obj loss gain (scale with pixels)
|
921 |
+
"obj_pw": (1, 0.5, 2.0), # obj BCELoss positive_weight
|
922 |
+
"iou_t": (0, 0.1, 0.7), # IoU training threshold
|
923 |
+
"anchor_t": (1, 2.0, 8.0), # anchor-multiple threshold
|
924 |
+
"anchors": (2, 2.0, 10.0), # anchors per output grid (0 to ignore)
|
925 |
+
"fl_gamma": (
|
926 |
+
0,
|
927 |
+
0.0,
|
928 |
+
2.0,
|
929 |
+
), # focal loss gamma (efficientDet default gamma=1.5)
|
930 |
+
"hsv_h": (1, 0.0, 0.1), # image HSV-Hue augmentation (fraction)
|
931 |
+
"hsv_s": (
|
932 |
+
1,
|
933 |
+
0.0,
|
934 |
+
0.9,
|
935 |
+
), # image HSV-Saturation augmentation (fraction)
|
936 |
+
"hsv_v": (1, 0.0, 0.9), # image HSV-Value augmentation (fraction)
|
937 |
+
"degrees": (1, 0.0, 45.0), # image rotation (+/- deg)
|
938 |
+
"translate": (1, 0.0, 0.9), # image translation (+/- fraction)
|
939 |
+
"scale": (1, 0.0, 0.9), # image scale (+/- gain)
|
940 |
+
"shear": (1, 0.0, 10.0), # image shear (+/- deg)
|
941 |
+
"perspective": (
|
942 |
+
0,
|
943 |
+
0.0,
|
944 |
+
0.001,
|
945 |
+
), # image perspective (+/- fraction), range 0-0.001
|
946 |
+
"flipud": (1, 0.0, 1.0), # image flip up-down (probability)
|
947 |
+
"fliplr": (0, 0.0, 1.0), # image flip left-right (probability)
|
948 |
+
"mosaic": (1, 0.0, 1.0), # image mixup (probability)
|
949 |
+
"mixup": (1, 0.0, 1.0), # image mixup (probability)
|
950 |
+
"copy_paste": (1, 0.0, 1.0),
|
951 |
+
} # segment copy-paste (probability)
|
952 |
+
|
953 |
+
with open(opt.hyp) as f:
|
954 |
+
hyp = yaml.safe_load(f) # load hyps dict
|
955 |
+
if "anchors" not in hyp: # anchors commented in hyp.yaml
|
956 |
+
hyp["anchors"] = 3
|
957 |
+
opt.noval, opt.nosave, save_dir = (
|
958 |
+
True,
|
959 |
+
True,
|
960 |
+
Path(opt.save_dir),
|
961 |
+
) # only val/save final epoch
|
962 |
+
# ei = [isinstance(x, (int, float)) for x in hyp.values()] # evolvable indices
|
963 |
+
evolve_yaml, evolve_csv = (
|
964 |
+
save_dir / "hyp_evolve.yaml",
|
965 |
+
save_dir / "evolve.csv",
|
966 |
+
)
|
967 |
+
if opt.bucket:
|
968 |
+
os.system(
|
969 |
+
f"gsutil cp gs://{opt.bucket}/evolve.csv {save_dir}"
|
970 |
+
) # download evolve.csv if exists
|
971 |
+
|
972 |
+
for _ in range(opt.evolve): # generations to evolve
|
973 |
+
if (
|
974 |
+
evolve_csv.exists()
|
975 |
+
): # if evolve.csv exists: select best hyps and mutate
|
976 |
+
# Select parent(s)
|
977 |
+
parent = (
|
978 |
+
"single" # parent selection method: 'single' or 'weighted'
|
979 |
+
)
|
980 |
+
x = np.loadtxt(evolve_csv, ndmin=2, delimiter=",", skiprows=1)
|
981 |
+
n = min(5, len(x)) # number of previous results to consider
|
982 |
+
x = x[np.argsort(-fitness(x))][:n] # top n mutations
|
983 |
+
w = fitness(x) - fitness(x).min() + 1e-6 # weights (sum > 0)
|
984 |
+
if parent == "single" or len(x) == 1:
|
985 |
+
# x = x[random.randint(0, n - 1)] # random selection
|
986 |
+
x = x[
|
987 |
+
random.choices(range(n), weights=w)[0]
|
988 |
+
] # weighted selection
|
989 |
+
elif parent == "weighted":
|
990 |
+
x = (x * w.reshape(n, 1)).sum(
|
991 |
+
0
|
992 |
+
) / w.sum() # weighted combination
|
993 |
+
|
994 |
+
# Mutate
|
995 |
+
mp, s = 0.8, 0.2 # mutation probability, sigma
|
996 |
+
npr = np.random
|
997 |
+
npr.seed(int(time.time()))
|
998 |
+
g = np.array([meta[k][0] for k in hyp.keys()]) # gains 0-1
|
999 |
+
ng = len(meta)
|
1000 |
+
v = np.ones(ng)
|
1001 |
+
while all(
|
1002 |
+
v == 1
|
1003 |
+
): # mutate until a change occurs (prevent duplicates)
|
1004 |
+
v = (
|
1005 |
+
g
|
1006 |
+
* (npr.random(ng) < mp)
|
1007 |
+
* npr.randn(ng)
|
1008 |
+
* npr.random()
|
1009 |
+
* s
|
1010 |
+
+ 1
|
1011 |
+
).clip(0.3, 3.0)
|
1012 |
+
for i, k in enumerate(hyp.keys()): # plt.hist(v.ravel(), 300)
|
1013 |
+
hyp[k] = float(x[i + 7] * v[i]) # mutate
|
1014 |
+
|
1015 |
+
# Constrain to limits
|
1016 |
+
for k, v in meta.items():
|
1017 |
+
hyp[k] = max(hyp[k], v[1]) # lower limit
|
1018 |
+
hyp[k] = min(hyp[k], v[2]) # upper limit
|
1019 |
+
hyp[k] = round(hyp[k], 5) # significant digits
|
1020 |
+
|
1021 |
+
# Train mutation
|
1022 |
+
results = train(hyp.copy(), opt, device, callbacks)
|
1023 |
+
|
1024 |
+
# Write mutation results
|
1025 |
+
print_mutation(results, hyp.copy(), save_dir, opt.bucket)
|
1026 |
+
|
1027 |
+
# Plot results
|
1028 |
+
plot_evolve(evolve_csv)
|
1029 |
+
print(
|
1030 |
+
f"Hyperparameter evolution finished\n"
|
1031 |
+
f"Results saved to {colorstr('bold', save_dir)}\n"
|
1032 |
+
f"Use best hyperparameters example: $ python train.py --hyp {evolve_yaml}"
|
1033 |
+
)
|
1034 |
+
|
1035 |
+
|
1036 |
+
def run(**kwargs):
|
1037 |
+
# Usage: import train; train.run(data='coco128.yaml', imgsz=320, weights='yolov5m.pt')
|
1038 |
+
opt = parse_opt(True)
|
1039 |
+
for k, v in kwargs.items():
|
1040 |
+
setattr(opt, k, v)
|
1041 |
+
main(opt)
|
1042 |
+
|
1043 |
+
|
1044 |
+
if __name__ == "__main__":
|
1045 |
+
opt = parse_opt()
|
1046 |
+
main(opt)
|
tutorial.ipynb
ADDED
@@ -0,0 +1,1022 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
{
|
2 |
+
"nbformat": 4,
|
3 |
+
"nbformat_minor": 0,
|
4 |
+
"metadata": {
|
5 |
+
"colab": {
|
6 |
+
"name": "YOLOv5 Tutorial",
|
7 |
+
"provenance": [],
|
8 |
+
"collapsed_sections": [],
|
9 |
+
"include_colab_link": true
|
10 |
+
},
|
11 |
+
"kernelspec": {
|
12 |
+
"name": "python3",
|
13 |
+
"display_name": "Python 3"
|
14 |
+
},
|
15 |
+
"accelerator": "GPU",
|
16 |
+
"widgets": {
|
17 |
+
"application/vnd.jupyter.widget-state+json": {
|
18 |
+
"484511f272e64eab8b42e68dac5f7a66": {
|
19 |
+
"model_module": "@jupyter-widgets/controls",
|
20 |
+
"model_name": "HBoxModel",
|
21 |
+
"model_module_version": "1.5.0",
|
22 |
+
"state": {
|
23 |
+
"_view_name": "HBoxView",
|
24 |
+
"_dom_classes": [],
|
25 |
+
"_model_name": "HBoxModel",
|
26 |
+
"_view_module": "@jupyter-widgets/controls",
|
27 |
+
"_model_module_version": "1.5.0",
|
28 |
+
"_view_count": null,
|
29 |
+
"_view_module_version": "1.5.0",
|
30 |
+
"box_style": "",
|
31 |
+
"layout": "IPY_MODEL_78cceec059784f2bb36988d3336e4d56",
|
32 |
+
"_model_module": "@jupyter-widgets/controls",
|
33 |
+
"children": [
|
34 |
+
"IPY_MODEL_ab93d8b65c134605934ff9ec5efb1bb6",
|
35 |
+
"IPY_MODEL_30df865ded4c434191bce772c9a82f3a",
|
36 |
+
"IPY_MODEL_20cdc61eb3404f42a12b37901b0d85fb"
|
37 |
+
]
|
38 |
+
}
|
39 |
+
},
|
40 |
+
"78cceec059784f2bb36988d3336e4d56": {
|
41 |
+
"model_module": "@jupyter-widgets/base",
|
42 |
+
"model_name": "LayoutModel",
|
43 |
+
"model_module_version": "1.2.0",
|
44 |
+
"state": {
|
45 |
+
"_view_name": "LayoutView",
|
46 |
+
"grid_template_rows": null,
|
47 |
+
"right": null,
|
48 |
+
"justify_content": null,
|
49 |
+
"_view_module": "@jupyter-widgets/base",
|
50 |
+
"overflow": null,
|
51 |
+
"_model_module_version": "1.2.0",
|
52 |
+
"_view_count": null,
|
53 |
+
"flex_flow": null,
|
54 |
+
"width": null,
|
55 |
+
"min_width": null,
|
56 |
+
"border": null,
|
57 |
+
"align_items": null,
|
58 |
+
"bottom": null,
|
59 |
+
"_model_module": "@jupyter-widgets/base",
|
60 |
+
"top": null,
|
61 |
+
"grid_column": null,
|
62 |
+
"overflow_y": null,
|
63 |
+
"overflow_x": null,
|
64 |
+
"grid_auto_flow": null,
|
65 |
+
"grid_area": null,
|
66 |
+
"grid_template_columns": null,
|
67 |
+
"flex": null,
|
68 |
+
"_model_name": "LayoutModel",
|
69 |
+
"justify_items": null,
|
70 |
+
"grid_row": null,
|
71 |
+
"max_height": null,
|
72 |
+
"align_content": null,
|
73 |
+
"visibility": null,
|
74 |
+
"align_self": null,
|
75 |
+
"height": null,
|
76 |
+
"min_height": null,
|
77 |
+
"padding": null,
|
78 |
+
"grid_auto_rows": null,
|
79 |
+
"grid_gap": null,
|
80 |
+
"max_width": null,
|
81 |
+
"order": null,
|
82 |
+
"_view_module_version": "1.2.0",
|
83 |
+
"grid_template_areas": null,
|
84 |
+
"object_position": null,
|
85 |
+
"object_fit": null,
|
86 |
+
"grid_auto_columns": null,
|
87 |
+
"margin": null,
|
88 |
+
"display": null,
|
89 |
+
"left": null
|
90 |
+
}
|
91 |
+
},
|
92 |
+
"ab93d8b65c134605934ff9ec5efb1bb6": {
|
93 |
+
"model_module": "@jupyter-widgets/controls",
|
94 |
+
"model_name": "HTMLModel",
|
95 |
+
"model_module_version": "1.5.0",
|
96 |
+
"state": {
|
97 |
+
"_view_name": "HTMLView",
|
98 |
+
"style": "IPY_MODEL_2d7239993a9645b09b221405ac682743",
|
99 |
+
"_dom_classes": [],
|
100 |
+
"description": "",
|
101 |
+
"_model_name": "HTMLModel",
|
102 |
+
"placeholder": "",
|
103 |
+
"_view_module": "@jupyter-widgets/controls",
|
104 |
+
"_model_module_version": "1.5.0",
|
105 |
+
"value": "100%",
|
106 |
+
"_view_count": null,
|
107 |
+
"_view_module_version": "1.5.0",
|
108 |
+
"description_tooltip": null,
|
109 |
+
"_model_module": "@jupyter-widgets/controls",
|
110 |
+
"layout": "IPY_MODEL_17b5a87f92104ec7ab96bf507637d0d2"
|
111 |
+
}
|
112 |
+
},
|
113 |
+
"30df865ded4c434191bce772c9a82f3a": {
|
114 |
+
"model_module": "@jupyter-widgets/controls",
|
115 |
+
"model_name": "FloatProgressModel",
|
116 |
+
"model_module_version": "1.5.0",
|
117 |
+
"state": {
|
118 |
+
"_view_name": "ProgressView",
|
119 |
+
"style": "IPY_MODEL_2358bfb2270247359e94b066b3cc3d1f",
|
120 |
+
"_dom_classes": [],
|
121 |
+
"description": "",
|
122 |
+
"_model_name": "FloatProgressModel",
|
123 |
+
"bar_style": "success",
|
124 |
+
"max": 818322941,
|
125 |
+
"_view_module": "@jupyter-widgets/controls",
|
126 |
+
"_model_module_version": "1.5.0",
|
127 |
+
"value": 818322941,
|
128 |
+
"_view_count": null,
|
129 |
+
"_view_module_version": "1.5.0",
|
130 |
+
"orientation": "horizontal",
|
131 |
+
"min": 0,
|
132 |
+
"description_tooltip": null,
|
133 |
+
"_model_module": "@jupyter-widgets/controls",
|
134 |
+
"layout": "IPY_MODEL_3e984405db654b0b83b88b2db08baffd"
|
135 |
+
}
|
136 |
+
},
|
137 |
+
"20cdc61eb3404f42a12b37901b0d85fb": {
|
138 |
+
"model_module": "@jupyter-widgets/controls",
|
139 |
+
"model_name": "HTMLModel",
|
140 |
+
"model_module_version": "1.5.0",
|
141 |
+
"state": {
|
142 |
+
"_view_name": "HTMLView",
|
143 |
+
"style": "IPY_MODEL_654d8a19b9f949c6bbdaf8b0875c931e",
|
144 |
+
"_dom_classes": [],
|
145 |
+
"description": "",
|
146 |
+
"_model_name": "HTMLModel",
|
147 |
+
"placeholder": "",
|
148 |
+
"_view_module": "@jupyter-widgets/controls",
|
149 |
+
"_model_module_version": "1.5.0",
|
150 |
+
"value": " 780M/780M [00:33<00:00, 24.4MB/s]",
|
151 |
+
"_view_count": null,
|
152 |
+
"_view_module_version": "1.5.0",
|
153 |
+
"description_tooltip": null,
|
154 |
+
"_model_module": "@jupyter-widgets/controls",
|
155 |
+
"layout": "IPY_MODEL_896030c5d13b415aaa05032818d81a6e"
|
156 |
+
}
|
157 |
+
},
|
158 |
+
"2d7239993a9645b09b221405ac682743": {
|
159 |
+
"model_module": "@jupyter-widgets/controls",
|
160 |
+
"model_name": "DescriptionStyleModel",
|
161 |
+
"model_module_version": "1.5.0",
|
162 |
+
"state": {
|
163 |
+
"_view_name": "StyleView",
|
164 |
+
"_model_name": "DescriptionStyleModel",
|
165 |
+
"description_width": "",
|
166 |
+
"_view_module": "@jupyter-widgets/base",
|
167 |
+
"_model_module_version": "1.5.0",
|
168 |
+
"_view_count": null,
|
169 |
+
"_view_module_version": "1.2.0",
|
170 |
+
"_model_module": "@jupyter-widgets/controls"
|
171 |
+
}
|
172 |
+
},
|
173 |
+
"17b5a87f92104ec7ab96bf507637d0d2": {
|
174 |
+
"model_module": "@jupyter-widgets/base",
|
175 |
+
"model_name": "LayoutModel",
|
176 |
+
"model_module_version": "1.2.0",
|
177 |
+
"state": {
|
178 |
+
"_view_name": "LayoutView",
|
179 |
+
"grid_template_rows": null,
|
180 |
+
"right": null,
|
181 |
+
"justify_content": null,
|
182 |
+
"_view_module": "@jupyter-widgets/base",
|
183 |
+
"overflow": null,
|
184 |
+
"_model_module_version": "1.2.0",
|
185 |
+
"_view_count": null,
|
186 |
+
"flex_flow": null,
|
187 |
+
"width": null,
|
188 |
+
"min_width": null,
|
189 |
+
"border": null,
|
190 |
+
"align_items": null,
|
191 |
+
"bottom": null,
|
192 |
+
"_model_module": "@jupyter-widgets/base",
|
193 |
+
"top": null,
|
194 |
+
"grid_column": null,
|
195 |
+
"overflow_y": null,
|
196 |
+
"overflow_x": null,
|
197 |
+
"grid_auto_flow": null,
|
198 |
+
"grid_area": null,
|
199 |
+
"grid_template_columns": null,
|
200 |
+
"flex": null,
|
201 |
+
"_model_name": "LayoutModel",
|
202 |
+
"justify_items": null,
|
203 |
+
"grid_row": null,
|
204 |
+
"max_height": null,
|
205 |
+
"align_content": null,
|
206 |
+
"visibility": null,
|
207 |
+
"align_self": null,
|
208 |
+
"height": null,
|
209 |
+
"min_height": null,
|
210 |
+
"padding": null,
|
211 |
+
"grid_auto_rows": null,
|
212 |
+
"grid_gap": null,
|
213 |
+
"max_width": null,
|
214 |
+
"order": null,
|
215 |
+
"_view_module_version": "1.2.0",
|
216 |
+
"grid_template_areas": null,
|
217 |
+
"object_position": null,
|
218 |
+
"object_fit": null,
|
219 |
+
"grid_auto_columns": null,
|
220 |
+
"margin": null,
|
221 |
+
"display": null,
|
222 |
+
"left": null
|
223 |
+
}
|
224 |
+
},
|
225 |
+
"2358bfb2270247359e94b066b3cc3d1f": {
|
226 |
+
"model_module": "@jupyter-widgets/controls",
|
227 |
+
"model_name": "ProgressStyleModel",
|
228 |
+
"model_module_version": "1.5.0",
|
229 |
+
"state": {
|
230 |
+
"_view_name": "StyleView",
|
231 |
+
"_model_name": "ProgressStyleModel",
|
232 |
+
"description_width": "",
|
233 |
+
"_view_module": "@jupyter-widgets/base",
|
234 |
+
"_model_module_version": "1.5.0",
|
235 |
+
"_view_count": null,
|
236 |
+
"_view_module_version": "1.2.0",
|
237 |
+
"bar_color": null,
|
238 |
+
"_model_module": "@jupyter-widgets/controls"
|
239 |
+
}
|
240 |
+
},
|
241 |
+
"3e984405db654b0b83b88b2db08baffd": {
|
242 |
+
"model_module": "@jupyter-widgets/base",
|
243 |
+
"model_name": "LayoutModel",
|
244 |
+
"model_module_version": "1.2.0",
|
245 |
+
"state": {
|
246 |
+
"_view_name": "LayoutView",
|
247 |
+
"grid_template_rows": null,
|
248 |
+
"right": null,
|
249 |
+
"justify_content": null,
|
250 |
+
"_view_module": "@jupyter-widgets/base",
|
251 |
+
"overflow": null,
|
252 |
+
"_model_module_version": "1.2.0",
|
253 |
+
"_view_count": null,
|
254 |
+
"flex_flow": null,
|
255 |
+
"width": null,
|
256 |
+
"min_width": null,
|
257 |
+
"border": null,
|
258 |
+
"align_items": null,
|
259 |
+
"bottom": null,
|
260 |
+
"_model_module": "@jupyter-widgets/base",
|
261 |
+
"top": null,
|
262 |
+
"grid_column": null,
|
263 |
+
"overflow_y": null,
|
264 |
+
"overflow_x": null,
|
265 |
+
"grid_auto_flow": null,
|
266 |
+
"grid_area": null,
|
267 |
+
"grid_template_columns": null,
|
268 |
+
"flex": null,
|
269 |
+
"_model_name": "LayoutModel",
|
270 |
+
"justify_items": null,
|
271 |
+
"grid_row": null,
|
272 |
+
"max_height": null,
|
273 |
+
"align_content": null,
|
274 |
+
"visibility": null,
|
275 |
+
"align_self": null,
|
276 |
+
"height": null,
|
277 |
+
"min_height": null,
|
278 |
+
"padding": null,
|
279 |
+
"grid_auto_rows": null,
|
280 |
+
"grid_gap": null,
|
281 |
+
"max_width": null,
|
282 |
+
"order": null,
|
283 |
+
"_view_module_version": "1.2.0",
|
284 |
+
"grid_template_areas": null,
|
285 |
+
"object_position": null,
|
286 |
+
"object_fit": null,
|
287 |
+
"grid_auto_columns": null,
|
288 |
+
"margin": null,
|
289 |
+
"display": null,
|
290 |
+
"left": null
|
291 |
+
}
|
292 |
+
},
|
293 |
+
"654d8a19b9f949c6bbdaf8b0875c931e": {
|
294 |
+
"model_module": "@jupyter-widgets/controls",
|
295 |
+
"model_name": "DescriptionStyleModel",
|
296 |
+
"model_module_version": "1.5.0",
|
297 |
+
"state": {
|
298 |
+
"_view_name": "StyleView",
|
299 |
+
"_model_name": "DescriptionStyleModel",
|
300 |
+
"description_width": "",
|
301 |
+
"_view_module": "@jupyter-widgets/base",
|
302 |
+
"_model_module_version": "1.5.0",
|
303 |
+
"_view_count": null,
|
304 |
+
"_view_module_version": "1.2.0",
|
305 |
+
"_model_module": "@jupyter-widgets/controls"
|
306 |
+
}
|
307 |
+
},
|
308 |
+
"896030c5d13b415aaa05032818d81a6e": {
|
309 |
+
"model_module": "@jupyter-widgets/base",
|
310 |
+
"model_name": "LayoutModel",
|
311 |
+
"model_module_version": "1.2.0",
|
312 |
+
"state": {
|
313 |
+
"_view_name": "LayoutView",
|
314 |
+
"grid_template_rows": null,
|
315 |
+
"right": null,
|
316 |
+
"justify_content": null,
|
317 |
+
"_view_module": "@jupyter-widgets/base",
|
318 |
+
"overflow": null,
|
319 |
+
"_model_module_version": "1.2.0",
|
320 |
+
"_view_count": null,
|
321 |
+
"flex_flow": null,
|
322 |
+
"width": null,
|
323 |
+
"min_width": null,
|
324 |
+
"border": null,
|
325 |
+
"align_items": null,
|
326 |
+
"bottom": null,
|
327 |
+
"_model_module": "@jupyter-widgets/base",
|
328 |
+
"top": null,
|
329 |
+
"grid_column": null,
|
330 |
+
"overflow_y": null,
|
331 |
+
"overflow_x": null,
|
332 |
+
"grid_auto_flow": null,
|
333 |
+
"grid_area": null,
|
334 |
+
"grid_template_columns": null,
|
335 |
+
"flex": null,
|
336 |
+
"_model_name": "LayoutModel",
|
337 |
+
"justify_items": null,
|
338 |
+
"grid_row": null,
|
339 |
+
"max_height": null,
|
340 |
+
"align_content": null,
|
341 |
+
"visibility": null,
|
342 |
+
"align_self": null,
|
343 |
+
"height": null,
|
344 |
+
"min_height": null,
|
345 |
+
"padding": null,
|
346 |
+
"grid_auto_rows": null,
|
347 |
+
"grid_gap": null,
|
348 |
+
"max_width": null,
|
349 |
+
"order": null,
|
350 |
+
"_view_module_version": "1.2.0",
|
351 |
+
"grid_template_areas": null,
|
352 |
+
"object_position": null,
|
353 |
+
"object_fit": null,
|
354 |
+
"grid_auto_columns": null,
|
355 |
+
"margin": null,
|
356 |
+
"display": null,
|
357 |
+
"left": null
|
358 |
+
}
|
359 |
+
}
|
360 |
+
}
|
361 |
+
}
|
362 |
+
},
|
363 |
+
"cells": [
|
364 |
+
{
|
365 |
+
"cell_type": "markdown",
|
366 |
+
"metadata": {
|
367 |
+
"id": "view-in-github",
|
368 |
+
"colab_type": "text"
|
369 |
+
},
|
370 |
+
"source": [
|
371 |
+
"<a href=\"https://colab.research.google.com/github/ultralytics/yolov5/blob/master/tutorial.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
|
372 |
+
]
|
373 |
+
},
|
374 |
+
{
|
375 |
+
"cell_type": "markdown",
|
376 |
+
"metadata": {
|
377 |
+
"id": "t6MPjfT5NrKQ"
|
378 |
+
},
|
379 |
+
"source": [
|
380 |
+
"<a align=\"left\" href=\"https://ultralytics.com/yolov5\" target=\"_blank\">\n",
|
381 |
+
"<img src=\"https://user-images.githubusercontent.com/26833433/125273437-35b3fc00-e30d-11eb-9079-46f313325424.png\"></a>\n",
|
382 |
+
"\n",
|
383 |
+
"This is the **official YOLOv5 🚀 notebook** by **Ultralytics**, and is freely available for redistribution under the [GPL-3.0 license](https://choosealicense.com/licenses/gpl-3.0/). \n",
|
384 |
+
"For more information please visit https://github.com/ultralytics/yolov5 and https://ultralytics.com. Thank you!"
|
385 |
+
]
|
386 |
+
},
|
387 |
+
{
|
388 |
+
"cell_type": "markdown",
|
389 |
+
"metadata": {
|
390 |
+
"id": "7mGmQbAO5pQb"
|
391 |
+
},
|
392 |
+
"source": [
|
393 |
+
"# Setup\n",
|
394 |
+
"\n",
|
395 |
+
"Clone repo, install dependencies and check PyTorch and GPU."
|
396 |
+
]
|
397 |
+
},
|
398 |
+
{
|
399 |
+
"cell_type": "code",
|
400 |
+
"metadata": {
|
401 |
+
"id": "wbvMlHd_QwMG",
|
402 |
+
"colab": {
|
403 |
+
"base_uri": "https://localhost:8080/"
|
404 |
+
},
|
405 |
+
"outputId": "4d67116a-43e9-4d84-d19e-1edd83f23a04"
|
406 |
+
},
|
407 |
+
"source": [
|
408 |
+
"!git clone https://github.com/ultralytics/yolov5 # clone repo\n",
|
409 |
+
"%cd yolov5\n",
|
410 |
+
"%pip install -qr requirements.txt # install dependencies\n",
|
411 |
+
"\n",
|
412 |
+
"import torch\n",
|
413 |
+
"from IPython.display import Image, clear_output # to display images\n",
|
414 |
+
"\n",
|
415 |
+
"clear_output()\n",
|
416 |
+
"print(f\"Setup complete. Using torch {torch.__version__} ({torch.cuda.get_device_properties(0).name if torch.cuda.is_available() else 'CPU'})\")"
|
417 |
+
],
|
418 |
+
"execution_count": null,
|
419 |
+
"outputs": [
|
420 |
+
{
|
421 |
+
"output_type": "stream",
|
422 |
+
"text": [
|
423 |
+
"Setup complete. Using torch 1.9.0+cu102 (Tesla V100-SXM2-16GB)\n"
|
424 |
+
],
|
425 |
+
"name": "stdout"
|
426 |
+
}
|
427 |
+
]
|
428 |
+
},
|
429 |
+
{
|
430 |
+
"cell_type": "markdown",
|
431 |
+
"metadata": {
|
432 |
+
"id": "4JnkELT0cIJg"
|
433 |
+
},
|
434 |
+
"source": [
|
435 |
+
"# 1. Inference\n",
|
436 |
+
"\n",
|
437 |
+
"`detect.py` runs YOLOv5 inference on a variety of sources, downloading models automatically from the [latest YOLOv5 release](https://github.com/ultralytics/yolov5/releases), and saving results to `runs/detect`. Example inference sources are:\n",
|
438 |
+
"\n",
|
439 |
+
"```shell\n",
|
440 |
+
"python detect.py --source 0 # webcam\n",
|
441 |
+
" file.jpg # image \n",
|
442 |
+
" file.mp4 # video\n",
|
443 |
+
" path/ # directory\n",
|
444 |
+
" path/*.jpg # glob\n",
|
445 |
+
" 'https://youtu.be/NUsoVlDFqZg' # YouTube\n",
|
446 |
+
" 'rtsp://example.com/media.mp4' # RTSP, RTMP, HTTP stream\n",
|
447 |
+
"```"
|
448 |
+
]
|
449 |
+
},
|
450 |
+
{
|
451 |
+
"cell_type": "code",
|
452 |
+
"metadata": {
|
453 |
+
"id": "zR9ZbuQCH7FX",
|
454 |
+
"colab": {
|
455 |
+
"base_uri": "https://localhost:8080/"
|
456 |
+
},
|
457 |
+
"outputId": "8b728908-81ab-4861-edb0-4d0c46c439fb"
|
458 |
+
},
|
459 |
+
"source": [
|
460 |
+
"!python detect.py --weights yolov5s.pt --img 640 --conf 0.25 --source data/images/\n",
|
461 |
+
"Image(filename='runs/detect/exp/zidane.jpg', width=600)"
|
462 |
+
],
|
463 |
+
"execution_count": null,
|
464 |
+
"outputs": [
|
465 |
+
{
|
466 |
+
"output_type": "stream",
|
467 |
+
"text": [
|
468 |
+
"\u001b[34m\u001b[1mdetect: \u001b[0mweights=['yolov5s.pt'], source=data/images/, imgsz=640, conf_thres=0.25, iou_thres=0.45, max_det=1000, device=, view_img=False, save_txt=False, save_conf=False, save_crop=False, nosave=False, classes=None, agnostic_nms=False, augment=False, visualize=False, update=False, project=runs/detect, name=exp, exist_ok=False, line_thickness=3, hide_labels=False, hide_conf=False, half=False\n",
|
469 |
+
"YOLOv5 🚀 v5.0-367-g01cdb76 torch 1.9.0+cu102 CUDA:0 (Tesla V100-SXM2-16GB, 16160.5MB)\n",
|
470 |
+
"\n",
|
471 |
+
"Fusing layers... \n",
|
472 |
+
"Model Summary: 224 layers, 7266973 parameters, 0 gradients\n",
|
473 |
+
"image 1/2 /content/yolov5/data/images/bus.jpg: 640x480 4 persons, 1 bus, 1 fire hydrant, Done. (0.007s)\n",
|
474 |
+
"image 2/2 /content/yolov5/data/images/zidane.jpg: 384x640 2 persons, 2 ties, Done. (0.007s)\n",
|
475 |
+
"Results saved to \u001b[1mruns/detect/exp\u001b[0m\n",
|
476 |
+
"Done. (0.091s)\n"
|
477 |
+
],
|
478 |
+
"name": "stdout"
|
479 |
+
}
|
480 |
+
]
|
481 |
+
},
|
482 |
+
{
|
483 |
+
"cell_type": "markdown",
|
484 |
+
"metadata": {
|
485 |
+
"id": "hkAzDWJ7cWTr"
|
486 |
+
},
|
487 |
+
"source": [
|
488 |
+
" \n",
|
489 |
+
"<img align=\"left\" src=\"https://user-images.githubusercontent.com/26833433/127574988-6a558aa1-d268-44b9-bf6b-62d4c605cc72.jpg\" width=\"600\">"
|
490 |
+
]
|
491 |
+
},
|
492 |
+
{
|
493 |
+
"cell_type": "markdown",
|
494 |
+
"metadata": {
|
495 |
+
"id": "0eq1SMWl6Sfn"
|
496 |
+
},
|
497 |
+
"source": [
|
498 |
+
"# 2. Validate\n",
|
499 |
+
"Validate a model's accuracy on [COCO](https://cocodataset.org/#home) val or test-dev datasets. Models are downloaded automatically from the [latest YOLOv5 release](https://github.com/ultralytics/yolov5/releases). To show results by class use the `--verbose` flag. Note that `pycocotools` metrics may be ~1% better than the equivalent repo metrics, as is visible below, due to slight differences in mAP computation."
|
500 |
+
]
|
501 |
+
},
|
502 |
+
{
|
503 |
+
"cell_type": "markdown",
|
504 |
+
"metadata": {
|
505 |
+
"id": "eyTZYGgRjnMc"
|
506 |
+
},
|
507 |
+
"source": [
|
508 |
+
"## COCO val2017\n",
|
509 |
+
"Download [COCO val 2017](https://github.com/ultralytics/yolov5/blob/74b34872fdf41941cddcf243951cdb090fbac17b/data/coco.yaml#L14) dataset (1GB - 5000 images), and test model accuracy."
|
510 |
+
]
|
511 |
+
},
|
512 |
+
{
|
513 |
+
"cell_type": "code",
|
514 |
+
"metadata": {
|
515 |
+
"id": "WQPtK1QYVaD_",
|
516 |
+
"colab": {
|
517 |
+
"base_uri": "https://localhost:8080/",
|
518 |
+
"height": 48,
|
519 |
+
"referenced_widgets": [
|
520 |
+
"484511f272e64eab8b42e68dac5f7a66",
|
521 |
+
"78cceec059784f2bb36988d3336e4d56",
|
522 |
+
"ab93d8b65c134605934ff9ec5efb1bb6",
|
523 |
+
"30df865ded4c434191bce772c9a82f3a",
|
524 |
+
"20cdc61eb3404f42a12b37901b0d85fb",
|
525 |
+
"2d7239993a9645b09b221405ac682743",
|
526 |
+
"17b5a87f92104ec7ab96bf507637d0d2",
|
527 |
+
"2358bfb2270247359e94b066b3cc3d1f",
|
528 |
+
"3e984405db654b0b83b88b2db08baffd",
|
529 |
+
"654d8a19b9f949c6bbdaf8b0875c931e",
|
530 |
+
"896030c5d13b415aaa05032818d81a6e"
|
531 |
+
]
|
532 |
+
},
|
533 |
+
"outputId": "7e6f5c96-c819-43e1-cd03-d3b9878cf8de"
|
534 |
+
},
|
535 |
+
"source": [
|
536 |
+
"# Download COCO val2017\n",
|
537 |
+
"torch.hub.download_url_to_file('https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017val.zip', 'tmp.zip')\n",
|
538 |
+
"!unzip -q tmp.zip -d ../datasets && rm tmp.zip"
|
539 |
+
],
|
540 |
+
"execution_count": null,
|
541 |
+
"outputs": [
|
542 |
+
{
|
543 |
+
"output_type": "display_data",
|
544 |
+
"data": {
|
545 |
+
"application/vnd.jupyter.widget-view+json": {
|
546 |
+
"model_id": "484511f272e64eab8b42e68dac5f7a66",
|
547 |
+
"version_minor": 0,
|
548 |
+
"version_major": 2
|
549 |
+
},
|
550 |
+
"text/plain": [
|
551 |
+
" 0%| | 0.00/780M [00:00<?, ?B/s]"
|
552 |
+
]
|
553 |
+
},
|
554 |
+
"metadata": {
|
555 |
+
"tags": []
|
556 |
+
}
|
557 |
+
}
|
558 |
+
]
|
559 |
+
},
|
560 |
+
{
|
561 |
+
"cell_type": "code",
|
562 |
+
"metadata": {
|
563 |
+
"id": "X58w8JLpMnjH",
|
564 |
+
"colab": {
|
565 |
+
"base_uri": "https://localhost:8080/"
|
566 |
+
},
|
567 |
+
"outputId": "3dd0e2fc-aecf-4108-91b1-6392da1863cb"
|
568 |
+
},
|
569 |
+
"source": [
|
570 |
+
"# Run YOLOv5x on COCO val2017\n",
|
571 |
+
"!python val.py --weights yolov5x.pt --data coco.yaml --img 640 --iou 0.65 --half"
|
572 |
+
],
|
573 |
+
"execution_count": null,
|
574 |
+
"outputs": [
|
575 |
+
{
|
576 |
+
"output_type": "stream",
|
577 |
+
"text": [
|
578 |
+
"\u001b[34m\u001b[1mval: \u001b[0mdata=./data/coco.yaml, weights=['yolov5x.pt'], batch_size=32, imgsz=640, conf_thres=0.001, iou_thres=0.65, task=val, device=, single_cls=False, augment=False, verbose=False, save_txt=False, save_hybrid=False, save_conf=False, save_json=True, project=runs/val, name=exp, exist_ok=False, half=True\n",
|
579 |
+
"YOLOv5 🚀 v5.0-367-g01cdb76 torch 1.9.0+cu102 CUDA:0 (Tesla V100-SXM2-16GB, 16160.5MB)\n",
|
580 |
+
"\n",
|
581 |
+
"Downloading https://github.com/ultralytics/yolov5/releases/download/v5.0/yolov5x.pt to yolov5x.pt...\n",
|
582 |
+
"100% 168M/168M [00:08<00:00, 20.6MB/s]\n",
|
583 |
+
"\n",
|
584 |
+
"Fusing layers... \n",
|
585 |
+
"Model Summary: 476 layers, 87730285 parameters, 0 gradients\n",
|
586 |
+
"\u001b[34m\u001b[1mval: \u001b[0mScanning '../datasets/coco/val2017' images and labels...4952 found, 48 missing, 0 empty, 0 corrupted: 100% 5000/5000 [00:01<00:00, 2749.96it/s]\n",
|
587 |
+
"\u001b[34m\u001b[1mval: \u001b[0mNew cache created: ../datasets/coco/val2017.cache\n",
|
588 |
+
" Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 157/157 [01:08<00:00, 2.28it/s]\n",
|
589 |
+
" all 5000 36335 0.746 0.626 0.68 0.49\n",
|
590 |
+
"Speed: 0.1ms pre-process, 5.1ms inference, 1.6ms NMS per image at shape (32, 3, 640, 640)\n",
|
591 |
+
"\n",
|
592 |
+
"Evaluating pycocotools mAP... saving runs/val/exp/yolov5x_predictions.json...\n",
|
593 |
+
"loading annotations into memory...\n",
|
594 |
+
"Done (t=0.46s)\n",
|
595 |
+
"creating index...\n",
|
596 |
+
"index created!\n",
|
597 |
+
"Loading and preparing results...\n",
|
598 |
+
"DONE (t=4.94s)\n",
|
599 |
+
"creating index...\n",
|
600 |
+
"index created!\n",
|
601 |
+
"Running per image evaluation...\n",
|
602 |
+
"Evaluate annotation type *bbox*\n",
|
603 |
+
"DONE (t=83.60s).\n",
|
604 |
+
"Accumulating evaluation results...\n",
|
605 |
+
"DONE (t=13.22s).\n",
|
606 |
+
" Average Precision (AP) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.504\n",
|
607 |
+
" Average Precision (AP) @[ IoU=0.50 | area= all | maxDets=100 ] = 0.688\n",
|
608 |
+
" Average Precision (AP) @[ IoU=0.75 | area= all | maxDets=100 ] = 0.546\n",
|
609 |
+
" Average Precision (AP) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.351\n",
|
610 |
+
" Average Precision (AP) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.551\n",
|
611 |
+
" Average Precision (AP) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.644\n",
|
612 |
+
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 1 ] = 0.382\n",
|
613 |
+
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets= 10 ] = 0.629\n",
|
614 |
+
" Average Recall (AR) @[ IoU=0.50:0.95 | area= all | maxDets=100 ] = 0.681\n",
|
615 |
+
" Average Recall (AR) @[ IoU=0.50:0.95 | area= small | maxDets=100 ] = 0.524\n",
|
616 |
+
" Average Recall (AR) @[ IoU=0.50:0.95 | area=medium | maxDets=100 ] = 0.735\n",
|
617 |
+
" Average Recall (AR) @[ IoU=0.50:0.95 | area= large | maxDets=100 ] = 0.827\n",
|
618 |
+
"Results saved to \u001b[1mruns/val/exp\u001b[0m\n"
|
619 |
+
],
|
620 |
+
"name": "stdout"
|
621 |
+
}
|
622 |
+
]
|
623 |
+
},
|
624 |
+
{
|
625 |
+
"cell_type": "markdown",
|
626 |
+
"metadata": {
|
627 |
+
"id": "rc_KbFk0juX2"
|
628 |
+
},
|
629 |
+
"source": [
|
630 |
+
"## COCO test-dev2017\n",
|
631 |
+
"Download [COCO test2017](https://github.com/ultralytics/yolov5/blob/74b34872fdf41941cddcf243951cdb090fbac17b/data/coco.yaml#L15) dataset (7GB - 40,000 images), to test model accuracy on test-dev set (**20,000 images, no labels**). Results are saved to a `*.json` file which should be **zipped** and submitted to the evaluation server at https://competitions.codalab.org/competitions/20794."
|
632 |
+
]
|
633 |
+
},
|
634 |
+
{
|
635 |
+
"cell_type": "code",
|
636 |
+
"metadata": {
|
637 |
+
"id": "V0AJnSeCIHyJ"
|
638 |
+
},
|
639 |
+
"source": [
|
640 |
+
"# Download COCO test-dev2017\n",
|
641 |
+
"torch.hub.download_url_to_file('https://github.com/ultralytics/yolov5/releases/download/v1.0/coco2017labels.zip', 'tmp.zip')\n",
|
642 |
+
"!unzip -q tmp.zip -d ../ && rm tmp.zip # unzip labels\n",
|
643 |
+
"!f=\"test2017.zip\" && curl http://images.cocodataset.org/zips/$f -o $f && unzip -q $f && rm $f # 7GB, 41k images\n",
|
644 |
+
"%mv ./test2017 ../coco/images # move to /coco"
|
645 |
+
],
|
646 |
+
"execution_count": null,
|
647 |
+
"outputs": []
|
648 |
+
},
|
649 |
+
{
|
650 |
+
"cell_type": "code",
|
651 |
+
"metadata": {
|
652 |
+
"id": "29GJXAP_lPrt"
|
653 |
+
},
|
654 |
+
"source": [
|
655 |
+
"# Run YOLOv5s on COCO test-dev2017 using --task test\n",
|
656 |
+
"!python val.py --weights yolov5s.pt --data coco.yaml --task test"
|
657 |
+
],
|
658 |
+
"execution_count": null,
|
659 |
+
"outputs": []
|
660 |
+
},
|
661 |
+
{
|
662 |
+
"cell_type": "markdown",
|
663 |
+
"metadata": {
|
664 |
+
"id": "VUOiNLtMP5aG"
|
665 |
+
},
|
666 |
+
"source": [
|
667 |
+
"# 3. Train\n",
|
668 |
+
"\n",
|
669 |
+
"Download [COCO128](https://www.kaggle.com/ultralytics/coco128), a small 128-image tutorial dataset, start tensorboard and train YOLOv5s from a pretrained checkpoint for 3 epochs (note actual training is typically much longer, around **300-1000 epochs**, depending on your dataset)."
|
670 |
+
]
|
671 |
+
},
|
672 |
+
{
|
673 |
+
"cell_type": "code",
|
674 |
+
"metadata": {
|
675 |
+
"id": "Knxi2ncxWffW"
|
676 |
+
},
|
677 |
+
"source": [
|
678 |
+
"# Download COCO128\n",
|
679 |
+
"torch.hub.download_url_to_file('https://github.com/ultralytics/yolov5/releases/download/v1.0/coco128.zip', 'tmp.zip')\n",
|
680 |
+
"!unzip -q tmp.zip -d ../datasets && rm tmp.zip"
|
681 |
+
],
|
682 |
+
"execution_count": null,
|
683 |
+
"outputs": []
|
684 |
+
},
|
685 |
+
{
|
686 |
+
"cell_type": "markdown",
|
687 |
+
"metadata": {
|
688 |
+
"id": "_pOkGLv1dMqh"
|
689 |
+
},
|
690 |
+
"source": [
|
691 |
+
"Train a YOLOv5s model on [COCO128](https://www.kaggle.com/ultralytics/coco128) with `--data coco128.yaml`, starting from pretrained `--weights yolov5s.pt`, or from randomly initialized `--weights '' --cfg yolov5s.yaml`. Models are downloaded automatically from the [latest YOLOv5 release](https://github.com/ultralytics/yolov5/releases), and **COCO, COCO128, and VOC datasets are downloaded automatically** on first use.\n",
|
692 |
+
"\n",
|
693 |
+
"All training results are saved to `runs/train/` with incrementing run directories, i.e. `runs/train/exp2`, `runs/train/exp3` etc.\n"
|
694 |
+
]
|
695 |
+
},
|
696 |
+
{
|
697 |
+
"cell_type": "code",
|
698 |
+
"metadata": {
|
699 |
+
"id": "bOy5KI2ncnWd"
|
700 |
+
},
|
701 |
+
"source": [
|
702 |
+
"# Tensorboard (optional)\n",
|
703 |
+
"%load_ext tensorboard\n",
|
704 |
+
"%tensorboard --logdir runs/train"
|
705 |
+
],
|
706 |
+
"execution_count": null,
|
707 |
+
"outputs": []
|
708 |
+
},
|
709 |
+
{
|
710 |
+
"cell_type": "code",
|
711 |
+
"metadata": {
|
712 |
+
"id": "2fLAV42oNb7M"
|
713 |
+
},
|
714 |
+
"source": [
|
715 |
+
"# Weights & Biases (optional)\n",
|
716 |
+
"%pip install -q wandb\n",
|
717 |
+
"import wandb\n",
|
718 |
+
"wandb.login()"
|
719 |
+
],
|
720 |
+
"execution_count": null,
|
721 |
+
"outputs": []
|
722 |
+
},
|
723 |
+
{
|
724 |
+
"cell_type": "code",
|
725 |
+
"metadata": {
|
726 |
+
"id": "1NcFxRcFdJ_O",
|
727 |
+
"colab": {
|
728 |
+
"base_uri": "https://localhost:8080/"
|
729 |
+
},
|
730 |
+
"outputId": "00ea4b14-a75c-44a2-a913-03b431b69de5"
|
731 |
+
},
|
732 |
+
"source": [
|
733 |
+
"# Train YOLOv5s on COCO128 for 3 epochs\n",
|
734 |
+
"!python train.py --img 640 --batch 16 --epochs 3 --data coco128.yaml --weights yolov5s.pt --cache"
|
735 |
+
],
|
736 |
+
"execution_count": null,
|
737 |
+
"outputs": [
|
738 |
+
{
|
739 |
+
"output_type": "stream",
|
740 |
+
"text": [
|
741 |
+
"\u001b[34m\u001b[1mtrain: \u001b[0mweights=yolov5s.pt, cfg=, data=coco128.yaml, hyp=data/hyps/hyp.scratch.yaml, epochs=3, batch_size=16, imgsz=640, rect=False, resume=False, nosave=False, noval=False, noautoanchor=False, evolve=None, bucket=, cache=ram, image_weights=False, device=, multi_scale=False, single_cls=False, adam=False, sync_bn=False, workers=8, project=runs/train, entity=None, name=exp, exist_ok=False, quad=False, linear_lr=False, label_smoothing=0.0, upload_dataset=False, bbox_interval=-1, save_period=-1, artifact_alias=latest, local_rank=-1, freeze=0\n",
|
742 |
+
"\u001b[34m\u001b[1mgithub: \u001b[0mup to date with https://github.com/ultralytics/yolov5 ✅\n",
|
743 |
+
"YOLOv5 🚀 v5.0-367-g01cdb76 torch 1.9.0+cu102 CUDA:0 (Tesla V100-SXM2-16GB, 16160.5MB)\n",
|
744 |
+
"\n",
|
745 |
+
"\u001b[34m\u001b[1mhyperparameters: \u001b[0mlr0=0.01, lrf=0.2, momentum=0.937, weight_decay=0.0005, warmup_epochs=3.0, warmup_momentum=0.8, warmup_bias_lr=0.1, box=0.05, cls=0.5, cls_pw=1.0, obj=1.0, obj_pw=1.0, iou_t=0.2, anchor_t=4.0, fl_gamma=0.0, hsv_h=0.015, hsv_s=0.7, hsv_v=0.4, degrees=0.0, translate=0.1, scale=0.5, shear=0.0, perspective=0.0, flipud=0.0, fliplr=0.5, mosaic=1.0, mixup=0.0, copy_paste=0.0\n",
|
746 |
+
"\u001b[34m\u001b[1mWeights & Biases: \u001b[0mrun 'pip install wandb' to automatically track and visualize YOLOv5 🚀 runs (RECOMMENDED)\n",
|
747 |
+
"\u001b[34m\u001b[1mTensorBoard: \u001b[0mStart with 'tensorboard --logdir runs/train', view at http://localhost:6006/\n",
|
748 |
+
"2021-08-15 14:40:43.449642: I tensorflow/stream_executor/platform/default/dso_loader.cc:53] Successfully opened dynamic library libcudart.so.11.0\n",
|
749 |
+
"\n",
|
750 |
+
" from n params module arguments \n",
|
751 |
+
" 0 -1 1 3520 models.common.Focus [3, 32, 3] \n",
|
752 |
+
" 1 -1 1 18560 models.common.Conv [32, 64, 3, 2] \n",
|
753 |
+
" 2 -1 1 18816 models.common.C3 [64, 64, 1] \n",
|
754 |
+
" 3 -1 1 73984 models.common.Conv [64, 128, 3, 2] \n",
|
755 |
+
" 4 -1 3 156928 models.common.C3 [128, 128, 3] \n",
|
756 |
+
" 5 -1 1 295424 models.common.Conv [128, 256, 3, 2] \n",
|
757 |
+
" 6 -1 3 625152 models.common.C3 [256, 256, 3] \n",
|
758 |
+
" 7 -1 1 1180672 models.common.Conv [256, 512, 3, 2] \n",
|
759 |
+
" 8 -1 1 656896 models.common.SPP [512, 512, [5, 9, 13]] \n",
|
760 |
+
" 9 -1 1 1182720 models.common.C3 [512, 512, 1, False] \n",
|
761 |
+
" 10 -1 1 131584 models.common.Conv [512, 256, 1, 1] \n",
|
762 |
+
" 11 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n",
|
763 |
+
" 12 [-1, 6] 1 0 models.common.Concat [1] \n",
|
764 |
+
" 13 -1 1 361984 models.common.C3 [512, 256, 1, False] \n",
|
765 |
+
" 14 -1 1 33024 models.common.Conv [256, 128, 1, 1] \n",
|
766 |
+
" 15 -1 1 0 torch.nn.modules.upsampling.Upsample [None, 2, 'nearest'] \n",
|
767 |
+
" 16 [-1, 4] 1 0 models.common.Concat [1] \n",
|
768 |
+
" 17 -1 1 90880 models.common.C3 [256, 128, 1, False] \n",
|
769 |
+
" 18 -1 1 147712 models.common.Conv [128, 128, 3, 2] \n",
|
770 |
+
" 19 [-1, 14] 1 0 models.common.Concat [1] \n",
|
771 |
+
" 20 -1 1 296448 models.common.C3 [256, 256, 1, False] \n",
|
772 |
+
" 21 -1 1 590336 models.common.Conv [256, 256, 3, 2] \n",
|
773 |
+
" 22 [-1, 10] 1 0 models.common.Concat [1] \n",
|
774 |
+
" 23 -1 1 1182720 models.common.C3 [512, 512, 1, False] \n",
|
775 |
+
" 24 [17, 20, 23] 1 229245 models.yolo.Detect [80, [[10, 13, 16, 30, 33, 23], [30, 61, 62, 45, 59, 119], [116, 90, 156, 198, 373, 326]], [128, 256, 512]]\n",
|
776 |
+
"Model Summary: 283 layers, 7276605 parameters, 7276605 gradients, 17.1 GFLOPs\n",
|
777 |
+
"\n",
|
778 |
+
"Transferred 362/362 items from yolov5s.pt\n",
|
779 |
+
"Scaled weight_decay = 0.0005\n",
|
780 |
+
"\u001b[34m\u001b[1moptimizer:\u001b[0m SGD with parameter groups 59 weight, 62 weight (no decay), 62 bias\n",
|
781 |
+
"\u001b[34m\u001b[1malbumentations: \u001b[0mversion 1.0.3 required by YOLOv5, but version 0.1.12 is currently installed\n",
|
782 |
+
"\u001b[34m\u001b[1mtrain: \u001b[0mScanning '../datasets/coco128/labels/train2017' images and labels...128 found, 0 missing, 2 empty, 0 corrupted: 100% 128/128 [00:00<00:00, 2440.28it/s]\n",
|
783 |
+
"\u001b[34m\u001b[1mtrain: \u001b[0mNew cache created: ../datasets/coco128/labels/train2017.cache\n",
|
784 |
+
"\u001b[34m\u001b[1mtrain: \u001b[0mCaching images (0.1GB ram): 100% 128/128 [00:00<00:00, 302.61it/s]\n",
|
785 |
+
"\u001b[34m\u001b[1mval: \u001b[0mScanning '../datasets/coco128/labels/train2017.cache' images and labels... 128 found, 0 missing, 2 empty, 0 corrupted: 100% 128/128 [00:00<?, ?it/s]\n",
|
786 |
+
"\u001b[34m\u001b[1mval: \u001b[0mCaching images (0.1GB ram): 100% 128/128 [00:00<00:00, 142.55it/s]\n",
|
787 |
+
"[W pthreadpool-cpp.cc:90] Warning: Leaking Caffe2 thread-pool after fork. (function pthreadpool)\n",
|
788 |
+
"[W pthreadpool-cpp.cc:90] Warning: Leaking Caffe2 thread-pool after fork. (function pthreadpool)\n",
|
789 |
+
"Plotting labels... \n",
|
790 |
+
"\n",
|
791 |
+
"\u001b[34m\u001b[1mautoanchor: \u001b[0mAnalyzing anchors... anchors/target = 4.27, Best Possible Recall (BPR) = 0.9935\n",
|
792 |
+
"Image sizes 640 train, 640 val\n",
|
793 |
+
"Using 2 dataloader workers\n",
|
794 |
+
"Logging results to runs/train/exp\n",
|
795 |
+
"Starting training for 3 epochs...\n",
|
796 |
+
"\n",
|
797 |
+
" Epoch gpu_mem box obj cls labels img_size\n",
|
798 |
+
" 0/2 3.64G 0.04492 0.0674 0.02213 298 640: 100% 8/8 [00:03<00:00, 2.05it/s]\n",
|
799 |
+
" Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:00<00:00, 4.70it/s]\n",
|
800 |
+
" all 128 929 0.686 0.565 0.642 0.421\n",
|
801 |
+
"\n",
|
802 |
+
" Epoch gpu_mem box obj cls labels img_size\n",
|
803 |
+
" 1/2 5.04G 0.04403 0.0611 0.01986 232 640: 100% 8/8 [00:01<00:00, 5.59it/s]\n",
|
804 |
+
" Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:00<00:00, 4.46it/s]\n",
|
805 |
+
" all 128 929 0.694 0.563 0.654 0.425\n",
|
806 |
+
"\n",
|
807 |
+
" Epoch gpu_mem box obj cls labels img_size\n",
|
808 |
+
" 2/2 5.04G 0.04616 0.07056 0.02071 214 640: 100% 8/8 [00:01<00:00, 5.94it/s]\n",
|
809 |
+
" Class Images Labels P R mAP@.5 mAP@.5:.95: 100% 4/4 [00:02<00:00, 1.52it/s]\n",
|
810 |
+
" all 128 929 0.711 0.562 0.66 0.431\n",
|
811 |
+
"\n",
|
812 |
+
"3 epochs completed in 0.005 hours.\n",
|
813 |
+
"Optimizer stripped from runs/train/exp/weights/last.pt, 14.8MB\n",
|
814 |
+
"Optimizer stripped from runs/train/exp/weights/best.pt, 14.8MB\n",
|
815 |
+
"Results saved to \u001b[1mruns/train/exp\u001b[0m\n"
|
816 |
+
],
|
817 |
+
"name": "stdout"
|
818 |
+
}
|
819 |
+
]
|
820 |
+
},
|
821 |
+
{
|
822 |
+
"cell_type": "markdown",
|
823 |
+
"metadata": {
|
824 |
+
"id": "15glLzbQx5u0"
|
825 |
+
},
|
826 |
+
"source": [
|
827 |
+
"# 4. Visualize"
|
828 |
+
]
|
829 |
+
},
|
830 |
+
{
|
831 |
+
"cell_type": "markdown",
|
832 |
+
"metadata": {
|
833 |
+
"id": "DLI1JmHU7B0l"
|
834 |
+
},
|
835 |
+
"source": [
|
836 |
+
"## Weights & Biases Logging 🌟 NEW\n",
|
837 |
+
"\n",
|
838 |
+
"[Weights & Biases](https://wandb.ai/site?utm_campaign=repo_yolo_notebook) (W&B) is now integrated with YOLOv5 for real-time visualization and cloud logging of training runs. This allows for better run comparison and introspection, as well improved visibility and collaboration for teams. To enable W&B `pip install wandb`, and then train normally (you will be guided through setup on first use). \n",
|
839 |
+
"\n",
|
840 |
+
"During training you will see live updates at [https://wandb.ai/home](https://wandb.ai/home?utm_campaign=repo_yolo_notebook), and you can create and share detailed [Reports](https://wandb.ai/glenn-jocher/yolov5_tutorial/reports/YOLOv5-COCO128-Tutorial-Results--VmlldzozMDI5OTY) of your results. For more information see the [YOLOv5 Weights & Biases Tutorial](https://github.com/ultralytics/yolov5/issues/1289). \n",
|
841 |
+
"\n",
|
842 |
+
"<img align=\"left\" src=\"https://user-images.githubusercontent.com/26833433/125274843-a27bc600-e30e-11eb-9a44-62af0b7a50a2.png\" width=\"800\">"
|
843 |
+
]
|
844 |
+
},
|
845 |
+
{
|
846 |
+
"cell_type": "markdown",
|
847 |
+
"metadata": {
|
848 |
+
"id": "-WPvRbS5Swl6"
|
849 |
+
},
|
850 |
+
"source": [
|
851 |
+
"## Local Logging\n",
|
852 |
+
"\n",
|
853 |
+
"All results are logged by default to `runs/train`, with a new experiment directory created for each new training as `runs/train/exp2`, `runs/train/exp3`, etc. View train and val jpgs to see mosaics, labels, predictions and augmentation effects. Note an Ultralytics **Mosaic Dataloader** is used for training (shown below), which combines 4 images into 1 mosaic during training.\n",
|
854 |
+
"\n",
|
855 |
+
"> <img src=\"https://user-images.githubusercontent.com/26833433/131255960-b536647f-7c61-4f60-bbc5-cb2544d71b2a.jpg\" width=\"700\"> \n",
|
856 |
+
"`train_batch0.jpg` shows train batch 0 mosaics and labels\n",
|
857 |
+
"\n",
|
858 |
+
"> <img src=\"https://user-images.githubusercontent.com/26833433/131256748-603cafc7-55d1-4e58-ab26-83657761aed9.jpg\" width=\"700\"> \n",
|
859 |
+
"`test_batch0_labels.jpg` shows val batch 0 labels\n",
|
860 |
+
"\n",
|
861 |
+
"> <img src=\"https://user-images.githubusercontent.com/26833433/131256752-3f25d7a5-7b0f-4bb3-ab78-46343c3800fe.jpg\" width=\"700\"> \n",
|
862 |
+
"`test_batch0_pred.jpg` shows val batch 0 _predictions_\n",
|
863 |
+
"\n",
|
864 |
+
"Training results are automatically logged to [Tensorboard](https://www.tensorflow.org/tensorboard) and [CSV](https://github.com/ultralytics/yolov5/pull/4148) as `results.csv`, which is plotted as `results.png` (below) after training completes. You can also plot any `results.csv` file manually:\n",
|
865 |
+
"\n",
|
866 |
+
"```python\n",
|
867 |
+
"from utils.plots import plot_results \n",
|
868 |
+
"plot_results('path/to/results.csv') # plot 'results.csv' as 'results.png'\n",
|
869 |
+
"```\n",
|
870 |
+
"\n",
|
871 |
+
"<img align=\"left\" width=\"800\" alt=\"COCO128 Training Results\" src=\"https://user-images.githubusercontent.com/26833433/126906780-8c5e2990-6116-4de6-b78a-367244a33ccf.png\">"
|
872 |
+
]
|
873 |
+
},
|
874 |
+
{
|
875 |
+
"cell_type": "markdown",
|
876 |
+
"metadata": {
|
877 |
+
"id": "Zelyeqbyt3GD"
|
878 |
+
},
|
879 |
+
"source": [
|
880 |
+
"# Environments\n",
|
881 |
+
"\n",
|
882 |
+
"YOLOv5 may be run in any of the following up-to-date verified environments (with all dependencies including [CUDA](https://developer.nvidia.com/cuda)/[CUDNN](https://developer.nvidia.com/cudnn), [Python](https://www.python.org/) and [PyTorch](https://pytorch.org/) preinstalled):\n",
|
883 |
+
"\n",
|
884 |
+
"- **Google Colab and Kaggle** notebooks with free GPU: <a href=\"https://colab.research.google.com/github/ultralytics/yolov5/blob/master/tutorial.ipynb\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"></a> <a href=\"https://www.kaggle.com/ultralytics/yolov5\"><img src=\"https://kaggle.com/static/images/open-in-kaggle.svg\" alt=\"Open In Kaggle\"></a>\n",
|
885 |
+
"- **Google Cloud** Deep Learning VM. See [GCP Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/GCP-Quickstart)\n",
|
886 |
+
"- **Amazon** Deep Learning AMI. See [AWS Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/AWS-Quickstart)\n",
|
887 |
+
"- **Docker Image**. See [Docker Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/Docker-Quickstart) <a href=\"https://hub.docker.com/r/ultralytics/yolov5\"><img src=\"https://img.shields.io/docker/pulls/ultralytics/yolov5?logo=docker\" alt=\"Docker Pulls\"></a>\n"
|
888 |
+
]
|
889 |
+
},
|
890 |
+
{
|
891 |
+
"cell_type": "markdown",
|
892 |
+
"metadata": {
|
893 |
+
"id": "6Qu7Iesl0p54"
|
894 |
+
},
|
895 |
+
"source": [
|
896 |
+
"# Status\n",
|
897 |
+
"\n",
|
898 |
+
"![CI CPU testing](https://github.com/ultralytics/yolov5/workflows/CI%20CPU%20testing/badge.svg)\n",
|
899 |
+
"\n",
|
900 |
+
"If this badge is green, all [YOLOv5 GitHub Actions](https://github.com/ultralytics/yolov5/actions) Continuous Integration (CI) tests are currently passing. CI tests verify correct operation of YOLOv5 training ([train.py](https://github.com/ultralytics/yolov5/blob/master/train.py)), testing ([val.py](https://github.com/ultralytics/yolov5/blob/master/val.py)), inference ([detect.py](https://github.com/ultralytics/yolov5/blob/master/detect.py)) and export ([export.py](https://github.com/ultralytics/yolov5/blob/master/export.py)) on MacOS, Windows, and Ubuntu every 24 hours and on every commit.\n"
|
901 |
+
]
|
902 |
+
},
|
903 |
+
{
|
904 |
+
"cell_type": "markdown",
|
905 |
+
"metadata": {
|
906 |
+
"id": "IEijrePND_2I"
|
907 |
+
},
|
908 |
+
"source": [
|
909 |
+
"# Appendix\n",
|
910 |
+
"\n",
|
911 |
+
"Optional extras below. Unit tests validate repo functionality and should be run on any PRs submitted.\n"
|
912 |
+
]
|
913 |
+
},
|
914 |
+
{
|
915 |
+
"cell_type": "code",
|
916 |
+
"metadata": {
|
917 |
+
"id": "mcKoSIK2WSzj"
|
918 |
+
},
|
919 |
+
"source": [
|
920 |
+
"# Reproduce\n",
|
921 |
+
"for x in 'yolov5s', 'yolov5m', 'yolov5l', 'yolov5x':\n",
|
922 |
+
" !python val.py --weights {x}.pt --data coco.yaml --img 640 --conf 0.25 --iou 0.45 # speed\n",
|
923 |
+
" !python val.py --weights {x}.pt --data coco.yaml --img 640 --conf 0.001 --iou 0.65 # mAP"
|
924 |
+
],
|
925 |
+
"execution_count": null,
|
926 |
+
"outputs": []
|
927 |
+
},
|
928 |
+
{
|
929 |
+
"cell_type": "code",
|
930 |
+
"metadata": {
|
931 |
+
"id": "GMusP4OAxFu6"
|
932 |
+
},
|
933 |
+
"source": [
|
934 |
+
"# PyTorch Hub\n",
|
935 |
+
"import torch\n",
|
936 |
+
"\n",
|
937 |
+
"# Model\n",
|
938 |
+
"model = torch.hub.load('ultralytics/yolov5', 'yolov5s')\n",
|
939 |
+
"\n",
|
940 |
+
"# Images\n",
|
941 |
+
"dir = 'https://ultralytics.com/images/'\n",
|
942 |
+
"imgs = [dir + f for f in ('zidane.jpg', 'bus.jpg')] # batch of images\n",
|
943 |
+
"\n",
|
944 |
+
"# Inference\n",
|
945 |
+
"results = model(imgs)\n",
|
946 |
+
"results.print() # or .show(), .save()"
|
947 |
+
],
|
948 |
+
"execution_count": null,
|
949 |
+
"outputs": []
|
950 |
+
},
|
951 |
+
{
|
952 |
+
"cell_type": "code",
|
953 |
+
"metadata": {
|
954 |
+
"id": "FGH0ZjkGjejy"
|
955 |
+
},
|
956 |
+
"source": [
|
957 |
+
"# Unit tests\n",
|
958 |
+
"%%shell\n",
|
959 |
+
"export PYTHONPATH=\"$PWD\" # to run *.py. files in subdirectories\n",
|
960 |
+
"\n",
|
961 |
+
"rm -rf runs # remove runs/\n",
|
962 |
+
"for m in yolov5s; do # models\n",
|
963 |
+
" python train.py --weights $m.pt --epochs 3 --img 320 --device 0 # train pretrained\n",
|
964 |
+
" python train.py --weights '' --cfg $m.yaml --epochs 3 --img 320 --device 0 # train scratch\n",
|
965 |
+
" for d in 0 cpu; do # devices\n",
|
966 |
+
" python detect.py --weights $m.pt --device $d # detect official\n",
|
967 |
+
" python detect.py --weights runs/train/exp/weights/best.pt --device $d # detect custom\n",
|
968 |
+
" python val.py --weights $m.pt --device $d # val official\n",
|
969 |
+
" python val.py --weights runs/train/exp/weights/best.pt --device $d # val custom\n",
|
970 |
+
" done\n",
|
971 |
+
" python hubconf.py # hub\n",
|
972 |
+
" python models/yolo.py --cfg $m.yaml # inspect\n",
|
973 |
+
" python export.py --weights $m.pt --img 640 --batch 1 # export\n",
|
974 |
+
"done"
|
975 |
+
],
|
976 |
+
"execution_count": null,
|
977 |
+
"outputs": []
|
978 |
+
},
|
979 |
+
{
|
980 |
+
"cell_type": "code",
|
981 |
+
"metadata": {
|
982 |
+
"id": "gogI-kwi3Tye"
|
983 |
+
},
|
984 |
+
"source": [
|
985 |
+
"# Profile\n",
|
986 |
+
"from utils.torch_utils import profile\n",
|
987 |
+
"\n",
|
988 |
+
"m1 = lambda x: x * torch.sigmoid(x)\n",
|
989 |
+
"m2 = torch.nn.SiLU()\n",
|
990 |
+
"results = profile(input=torch.randn(16, 3, 640, 640), ops=[m1, m2], n=100)"
|
991 |
+
],
|
992 |
+
"execution_count": null,
|
993 |
+
"outputs": []
|
994 |
+
},
|
995 |
+
{
|
996 |
+
"cell_type": "code",
|
997 |
+
"metadata": {
|
998 |
+
"id": "RVRSOhEvUdb5"
|
999 |
+
},
|
1000 |
+
"source": [
|
1001 |
+
"# Evolve\n",
|
1002 |
+
"!python train.py --img 640 --batch 64 --epochs 100 --data coco128.yaml --weights yolov5s.pt --cache --noautoanchor --evolve\n",
|
1003 |
+
"!d=runs/train/evolve && cp evolve.* $d && zip -r evolve.zip $d && gsutil mv evolve.zip gs://bucket # upload results (optional)"
|
1004 |
+
],
|
1005 |
+
"execution_count": null,
|
1006 |
+
"outputs": []
|
1007 |
+
},
|
1008 |
+
{
|
1009 |
+
"cell_type": "code",
|
1010 |
+
"metadata": {
|
1011 |
+
"id": "BSgFCAcMbk1R"
|
1012 |
+
},
|
1013 |
+
"source": [
|
1014 |
+
"# VOC\n",
|
1015 |
+
"for b, m in zip([64, 48, 32, 16], ['yolov5s', 'yolov5m', 'yolov5l', 'yolov5x']): # zip(batch_size, model)\n",
|
1016 |
+
" !python train.py --batch {b} --weights {m}.pt --data VOC.yaml --epochs 50 --cache --img 512 --nosave --hyp hyp.finetune.yaml --project VOC --name {m}"
|
1017 |
+
],
|
1018 |
+
"execution_count": null,
|
1019 |
+
"outputs": []
|
1020 |
+
}
|
1021 |
+
]
|
1022 |
+
}
|
utils.py
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import json
|
2 |
+
import os
|
3 |
+
|
4 |
+
# from sklearn.externals import joblib
|
5 |
+
import joblib
|
6 |
+
import numpy as np
|
7 |
+
import pandas as pd
|
8 |
+
|
9 |
+
# from .variables import old_ocr_req_cols
|
10 |
+
# from .skew_correction import PageSkewWraper
|
11 |
+
|
12 |
+
const_HW = 1.294117647
|
13 |
+
const_W = 600
|
14 |
+
|
15 |
+
|
16 |
+
def bucket_sort(df, colmn, ymax_col="ymax", ymin_col="ymin"):
|
17 |
+
df["line_number"] = 0
|
18 |
+
colmn.append("line_number")
|
19 |
+
array_value = df[colmn].values
|
20 |
+
start_index = Line_counter = counter = 0
|
21 |
+
ymax, ymin, line_no = (
|
22 |
+
colmn.index(ymax_col),
|
23 |
+
colmn.index(ymin_col),
|
24 |
+
colmn.index("line_number"),
|
25 |
+
)
|
26 |
+
while counter < len(array_value):
|
27 |
+
current_ymax = array_value[start_index][ymax]
|
28 |
+
for next_index in range(start_index, len(array_value)):
|
29 |
+
counter += 1
|
30 |
+
|
31 |
+
next_ymin = array_value[next_index][ymin]
|
32 |
+
next_ymax = array_value[next_index][ymax]
|
33 |
+
if current_ymax > next_ymin:
|
34 |
+
|
35 |
+
array_value[next_index][line_no] = Line_counter + 1
|
36 |
+
# if current_ymax < next_ymax:
|
37 |
+
|
38 |
+
# current_ymax = next_ymax
|
39 |
+
else:
|
40 |
+
counter -= 1
|
41 |
+
break
|
42 |
+
# print(counter, len(array_value), start_index)
|
43 |
+
start_index = counter
|
44 |
+
Line_counter += 1
|
45 |
+
return pd.DataFrame(array_value, columns=colmn)
|
46 |
+
|
47 |
+
|
48 |
+
def do_sorting(df):
|
49 |
+
df.sort_values(["ymin", "xmin"], ascending=True, inplace=True)
|
50 |
+
df["idx"] = df.index
|
51 |
+
if "line_number" in df.columns:
|
52 |
+
print("line number removed")
|
53 |
+
df.drop("line_number", axis=1, inplace=True)
|
54 |
+
req_colns = ["xmin", "ymin", "xmax", "ymax", "idx"]
|
55 |
+
temp_df = df.copy()
|
56 |
+
temp = bucket_sort(temp_df.copy(), req_colns)
|
57 |
+
df = df.merge(temp[["idx", "line_number"]], on="idx")
|
58 |
+
df.sort_values(["line_number", "xmin"], ascending=True, inplace=True)
|
59 |
+
df = df.reset_index(drop=True)
|
60 |
+
df = df.reset_index(drop=True)
|
61 |
+
return df
|
val.py
ADDED
@@ -0,0 +1,593 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# YOLOv5 🚀 by Ultralytics, GPL-3.0 license
|
2 |
+
"""
|
3 |
+
Validate a trained YOLOv5 model accuracy on a custom dataset
|
4 |
+
|
5 |
+
Usage:
|
6 |
+
$ python path/to/val.py --data coco128.yaml --weights yolov5s.pt --img 640
|
7 |
+
"""
|
8 |
+
|
9 |
+
import argparse
|
10 |
+
import json
|
11 |
+
import os
|
12 |
+
import sys
|
13 |
+
from pathlib import Path
|
14 |
+
from threading import Thread
|
15 |
+
|
16 |
+
import numpy as np
|
17 |
+
import torch
|
18 |
+
from tqdm import tqdm
|
19 |
+
|
20 |
+
FILE = Path(__file__).absolute()
|
21 |
+
sys.path.append(FILE.parents[0].as_posix()) # add yolov5/ to path
|
22 |
+
|
23 |
+
from models.experimental import attempt_load
|
24 |
+
from utils.callbacks import Callbacks
|
25 |
+
from utils.datasets import create_dataloader
|
26 |
+
from utils.general import (
|
27 |
+
box_iou,
|
28 |
+
check_dataset,
|
29 |
+
check_img_size,
|
30 |
+
check_requirements,
|
31 |
+
check_suffix,
|
32 |
+
check_yaml,
|
33 |
+
coco80_to_coco91_class,
|
34 |
+
colorstr,
|
35 |
+
increment_path,
|
36 |
+
non_max_suppression,
|
37 |
+
scale_coords,
|
38 |
+
set_logging,
|
39 |
+
xywh2xyxy,
|
40 |
+
xyxy2xywh,
|
41 |
+
)
|
42 |
+
from utils.metrics import ConfusionMatrix, ap_per_class
|
43 |
+
from utils.plots import output_to_target, plot_images, plot_study_txt
|
44 |
+
from utils.torch_utils import select_device, time_sync
|
45 |
+
|
46 |
+
|
47 |
+
def save_one_txt(predn, save_conf, shape, file):
|
48 |
+
# Save one txt result
|
49 |
+
gn = torch.tensor(shape)[[1, 0, 1, 0]] # normalization gain whwh
|
50 |
+
for *xyxy, conf, cls in predn.tolist():
|
51 |
+
xywh = (
|
52 |
+
(xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()
|
53 |
+
) # normalized xywh
|
54 |
+
line = (
|
55 |
+
(cls, *xywh, conf) if save_conf else (cls, *xywh)
|
56 |
+
) # label format
|
57 |
+
with open(file, "a") as f:
|
58 |
+
f.write(("%g " * len(line)).rstrip() % line + "\n")
|
59 |
+
|
60 |
+
|
61 |
+
def save_one_json(predn, jdict, path, class_map):
|
62 |
+
# Save one JSON result {"image_id": 42, "category_id": 18, "bbox": [258.15, 41.29, 348.26, 243.78], "score": 0.236}
|
63 |
+
image_id = int(path.stem) if path.stem.isnumeric() else path.stem
|
64 |
+
box = xyxy2xywh(predn[:, :4]) # xywh
|
65 |
+
box[:, :2] -= box[:, 2:] / 2 # xy center to top-left corner
|
66 |
+
for p, b in zip(predn.tolist(), box.tolist()):
|
67 |
+
jdict.append(
|
68 |
+
{
|
69 |
+
"image_id": image_id,
|
70 |
+
"category_id": class_map[int(p[5])],
|
71 |
+
"bbox": [round(x, 3) for x in b],
|
72 |
+
"score": round(p[4], 5),
|
73 |
+
}
|
74 |
+
)
|
75 |
+
|
76 |
+
|
77 |
+
def process_batch(detections, labels, iouv):
|
78 |
+
"""
|
79 |
+
Return correct predictions matrix. Both sets of boxes are in (x1, y1, x2, y2) format.
|
80 |
+
Arguments:
|
81 |
+
detections (Array[N, 6]), x1, y1, x2, y2, conf, class
|
82 |
+
labels (Array[M, 5]), class, x1, y1, x2, y2
|
83 |
+
Returns:
|
84 |
+
correct (Array[N, 10]), for 10 IoU levels
|
85 |
+
"""
|
86 |
+
correct = torch.zeros(
|
87 |
+
detections.shape[0],
|
88 |
+
iouv.shape[0],
|
89 |
+
dtype=torch.bool,
|
90 |
+
device=iouv.device,
|
91 |
+
)
|
92 |
+
iou = box_iou(labels[:, 1:], detections[:, :4])
|
93 |
+
x = torch.where(
|
94 |
+
(iou >= iouv[0]) & (labels[:, 0:1] == detections[:, 5])
|
95 |
+
) # IoU above threshold and classes match
|
96 |
+
if x[0].shape[0]:
|
97 |
+
matches = (
|
98 |
+
torch.cat((torch.stack(x, 1), iou[x[0], x[1]][:, None]), 1)
|
99 |
+
.cpu()
|
100 |
+
.numpy()
|
101 |
+
) # [label, detection, iou]
|
102 |
+
if x[0].shape[0] > 1:
|
103 |
+
matches = matches[matches[:, 2].argsort()[::-1]]
|
104 |
+
matches = matches[np.unique(matches[:, 1], return_index=True)[1]]
|
105 |
+
# matches = matches[matches[:, 2].argsort()[::-1]]
|
106 |
+
matches = matches[np.unique(matches[:, 0], return_index=True)[1]]
|
107 |
+
matches = torch.Tensor(matches).to(iouv.device)
|
108 |
+
correct[matches[:, 1].long()] = matches[:, 2:3] >= iouv
|
109 |
+
return correct
|
110 |
+
|
111 |
+
|
112 |
+
@torch.no_grad()
|
113 |
+
def run(
|
114 |
+
data,
|
115 |
+
weights=None, # model.pt path(s)
|
116 |
+
batch_size=32, # batch size
|
117 |
+
imgsz=640, # inference size (pixels)
|
118 |
+
conf_thres=0.001, # confidence threshold
|
119 |
+
iou_thres=0.6, # NMS IoU threshold
|
120 |
+
task="val", # train, val, test, speed or study
|
121 |
+
device="", # cuda device, i.e. 0 or 0,1,2,3 or cpu
|
122 |
+
single_cls=False, # treat as single-class dataset
|
123 |
+
augment=False, # augmented inference
|
124 |
+
verbose=False, # verbose output
|
125 |
+
save_txt=False, # save results to *.txt
|
126 |
+
save_hybrid=False, # save label+prediction hybrid results to *.txt
|
127 |
+
save_conf=False, # save confidences in --save-txt labels
|
128 |
+
save_json=False, # save a COCO-JSON results file
|
129 |
+
project="runs/val", # save to project/name
|
130 |
+
name="exp", # save to project/name
|
131 |
+
exist_ok=False, # existing project/name ok, do not increment
|
132 |
+
half=True, # use FP16 half-precision inference
|
133 |
+
model=None,
|
134 |
+
dataloader=None,
|
135 |
+
save_dir=Path(""),
|
136 |
+
plots=True,
|
137 |
+
callbacks=Callbacks(),
|
138 |
+
compute_loss=None,
|
139 |
+
):
|
140 |
+
# Initialize/load model and set device
|
141 |
+
training = model is not None
|
142 |
+
if training: # called by train.py
|
143 |
+
device = next(model.parameters()).device # get model device
|
144 |
+
|
145 |
+
else: # called directly
|
146 |
+
device = select_device(device, batch_size=batch_size)
|
147 |
+
|
148 |
+
# Directories
|
149 |
+
save_dir = increment_path(
|
150 |
+
Path(project) / name, exist_ok=exist_ok
|
151 |
+
) # increment run
|
152 |
+
(save_dir / "labels" if save_txt else save_dir).mkdir(
|
153 |
+
parents=True, exist_ok=True
|
154 |
+
) # make dir
|
155 |
+
|
156 |
+
# Load model
|
157 |
+
check_suffix(weights, ".pt")
|
158 |
+
model = attempt_load(weights, map_location=device) # load FP32 model
|
159 |
+
gs = max(int(model.stride.max()), 32) # grid size (max stride)
|
160 |
+
imgsz = check_img_size(imgsz, s=gs) # check image size
|
161 |
+
|
162 |
+
# Multi-GPU disabled, incompatible with .half() https://github.com/ultralytics/yolov5/issues/99
|
163 |
+
# if device.type != 'cpu' and torch.cuda.device_count() > 1:
|
164 |
+
# model = nn.DataParallel(model)
|
165 |
+
|
166 |
+
# Data
|
167 |
+
data = check_dataset(data) # check
|
168 |
+
|
169 |
+
# Half
|
170 |
+
half &= device.type != "cpu" # half precision only supported on CUDA
|
171 |
+
if half:
|
172 |
+
model.half()
|
173 |
+
|
174 |
+
# Configure
|
175 |
+
model.eval()
|
176 |
+
is_coco = isinstance(data.get("val"), str) and data["val"].endswith(
|
177 |
+
"coco/val2017.txt"
|
178 |
+
) # COCO dataset
|
179 |
+
nc = 1 if single_cls else int(data["nc"]) # number of classes
|
180 |
+
iouv = torch.linspace(0.5, 0.95, 10).to(
|
181 |
+
device
|
182 |
+
) # iou vector for mAP@0.5:0.95
|
183 |
+
niou = iouv.numel()
|
184 |
+
|
185 |
+
# Dataloader
|
186 |
+
if not training:
|
187 |
+
if device.type != "cpu":
|
188 |
+
model(
|
189 |
+
torch.zeros(1, 3, imgsz, imgsz)
|
190 |
+
.to(device)
|
191 |
+
.type_as(next(model.parameters()))
|
192 |
+
) # run once
|
193 |
+
task = (
|
194 |
+
task if task in ("train", "val", "test") else "val"
|
195 |
+
) # path to train/val/test images
|
196 |
+
dataloader = create_dataloader(
|
197 |
+
data[task],
|
198 |
+
imgsz,
|
199 |
+
batch_size,
|
200 |
+
gs,
|
201 |
+
single_cls,
|
202 |
+
pad=0.5,
|
203 |
+
rect=True,
|
204 |
+
prefix=colorstr(f"{task}: "),
|
205 |
+
)[0]
|
206 |
+
|
207 |
+
seen = 0
|
208 |
+
confusion_matrix = ConfusionMatrix(nc=nc)
|
209 |
+
names = {
|
210 |
+
k: v
|
211 |
+
for k, v in enumerate(
|
212 |
+
model.names if hasattr(model, "names") else model.module.names
|
213 |
+
)
|
214 |
+
}
|
215 |
+
class_map = coco80_to_coco91_class() if is_coco else list(range(1000))
|
216 |
+
s = ("%20s" + "%11s" * 6) % (
|
217 |
+
"Class",
|
218 |
+
"Images",
|
219 |
+
"Labels",
|
220 |
+
"P",
|
221 |
+
"R",
|
222 |
+
"mAP@.5",
|
223 |
+
"mAP@.5:.95",
|
224 |
+
)
|
225 |
+
dt, p, r, f1, mp, mr, map50, map = (
|
226 |
+
[0.0, 0.0, 0.0],
|
227 |
+
0.0,
|
228 |
+
0.0,
|
229 |
+
0.0,
|
230 |
+
0.0,
|
231 |
+
0.0,
|
232 |
+
0.0,
|
233 |
+
0.0,
|
234 |
+
)
|
235 |
+
loss = torch.zeros(3, device=device)
|
236 |
+
jdict, stats, ap, ap_class = [], [], [], []
|
237 |
+
for batch_i, (img, targets, paths, shapes) in enumerate(
|
238 |
+
tqdm(dataloader, desc=s)
|
239 |
+
):
|
240 |
+
t1 = time_sync()
|
241 |
+
img = img.to(device, non_blocking=True)
|
242 |
+
img = img.half() if half else img.float() # uint8 to fp16/32
|
243 |
+
img /= 255.0 # 0 - 255 to 0.0 - 1.0
|
244 |
+
targets = targets.to(device)
|
245 |
+
nb, _, height, width = img.shape # batch size, channels, height, width
|
246 |
+
t2 = time_sync()
|
247 |
+
dt[0] += t2 - t1
|
248 |
+
|
249 |
+
# Run model
|
250 |
+
out, train_out = model(
|
251 |
+
img, augment=augment
|
252 |
+
) # inference and training outputs
|
253 |
+
dt[1] += time_sync() - t2
|
254 |
+
|
255 |
+
# Compute loss
|
256 |
+
if compute_loss:
|
257 |
+
loss += compute_loss([x.float() for x in train_out], targets)[
|
258 |
+
1
|
259 |
+
] # box, obj, cls
|
260 |
+
|
261 |
+
# Run NMS
|
262 |
+
targets[:, 2:] *= torch.Tensor([width, height, width, height]).to(
|
263 |
+
device
|
264 |
+
) # to pixels
|
265 |
+
lb = (
|
266 |
+
[targets[targets[:, 0] == i, 1:] for i in range(nb)]
|
267 |
+
if save_hybrid
|
268 |
+
else []
|
269 |
+
) # for autolabelling
|
270 |
+
t3 = time_sync()
|
271 |
+
out = non_max_suppression(
|
272 |
+
out,
|
273 |
+
conf_thres,
|
274 |
+
iou_thres,
|
275 |
+
labels=lb,
|
276 |
+
multi_label=True,
|
277 |
+
agnostic=single_cls,
|
278 |
+
)
|
279 |
+
dt[2] += time_sync() - t3
|
280 |
+
|
281 |
+
# Statistics per image
|
282 |
+
for si, pred in enumerate(out):
|
283 |
+
labels = targets[targets[:, 0] == si, 1:]
|
284 |
+
nl = len(labels)
|
285 |
+
tcls = labels[:, 0].tolist() if nl else [] # target class
|
286 |
+
path, shape = Path(paths[si]), shapes[si][0]
|
287 |
+
seen += 1
|
288 |
+
|
289 |
+
if len(pred) == 0:
|
290 |
+
if nl:
|
291 |
+
stats.append(
|
292 |
+
(
|
293 |
+
torch.zeros(0, niou, dtype=torch.bool),
|
294 |
+
torch.Tensor(),
|
295 |
+
torch.Tensor(),
|
296 |
+
tcls,
|
297 |
+
)
|
298 |
+
)
|
299 |
+
continue
|
300 |
+
|
301 |
+
# Predictions
|
302 |
+
if single_cls:
|
303 |
+
pred[:, 5] = 0
|
304 |
+
predn = pred.clone()
|
305 |
+
scale_coords(
|
306 |
+
img[si].shape[1:], predn[:, :4], shape, shapes[si][1]
|
307 |
+
) # native-space pred
|
308 |
+
|
309 |
+
# Evaluate
|
310 |
+
if nl:
|
311 |
+
tbox = xywh2xyxy(labels[:, 1:5]) # target boxes
|
312 |
+
scale_coords(
|
313 |
+
img[si].shape[1:], tbox, shape, shapes[si][1]
|
314 |
+
) # native-space labels
|
315 |
+
labelsn = torch.cat(
|
316 |
+
(labels[:, 0:1], tbox), 1
|
317 |
+
) # native-space labels
|
318 |
+
correct = process_batch(predn, labelsn, iouv)
|
319 |
+
if plots:
|
320 |
+
confusion_matrix.process_batch(predn, labelsn)
|
321 |
+
else:
|
322 |
+
correct = torch.zeros(pred.shape[0], niou, dtype=torch.bool)
|
323 |
+
stats.append(
|
324 |
+
(correct.cpu(), pred[:, 4].cpu(), pred[:, 5].cpu(), tcls)
|
325 |
+
) # (correct, conf, pcls, tcls)
|
326 |
+
|
327 |
+
# Save/log
|
328 |
+
if save_txt:
|
329 |
+
save_one_txt(
|
330 |
+
predn,
|
331 |
+
save_conf,
|
332 |
+
shape,
|
333 |
+
file=save_dir / "labels" / (path.stem + ".txt"),
|
334 |
+
)
|
335 |
+
if save_json:
|
336 |
+
save_one_json(
|
337 |
+
predn, jdict, path, class_map
|
338 |
+
) # append to COCO-JSON dictionary
|
339 |
+
callbacks.run(
|
340 |
+
"on_val_image_end", pred, predn, path, names, img[si]
|
341 |
+
)
|
342 |
+
|
343 |
+
# Plot images
|
344 |
+
if plots and batch_i < 3:
|
345 |
+
f = save_dir / f"val_batch{batch_i}_labels.jpg" # labels
|
346 |
+
Thread(
|
347 |
+
target=plot_images,
|
348 |
+
args=(img, targets, paths, f, names),
|
349 |
+
daemon=True,
|
350 |
+
).start()
|
351 |
+
f = save_dir / f"val_batch{batch_i}_pred.jpg" # predictions
|
352 |
+
Thread(
|
353 |
+
target=plot_images,
|
354 |
+
args=(img, output_to_target(out), paths, f, names),
|
355 |
+
daemon=True,
|
356 |
+
).start()
|
357 |
+
|
358 |
+
# Compute statistics
|
359 |
+
stats = [np.concatenate(x, 0) for x in zip(*stats)] # to numpy
|
360 |
+
if len(stats) and stats[0].any():
|
361 |
+
p, r, ap, f1, ap_class = ap_per_class(
|
362 |
+
*stats, plot=plots, save_dir=save_dir, names=names
|
363 |
+
)
|
364 |
+
ap50, ap = ap[:, 0], ap.mean(1) # AP@0.5, AP@0.5:0.95
|
365 |
+
mp, mr, map50, map = p.mean(), r.mean(), ap50.mean(), ap.mean()
|
366 |
+
nt = np.bincount(
|
367 |
+
stats[3].astype(np.int64), minlength=nc
|
368 |
+
) # number of targets per class
|
369 |
+
else:
|
370 |
+
nt = torch.zeros(1)
|
371 |
+
|
372 |
+
# Print results
|
373 |
+
pf = "%20s" + "%11i" * 2 + "%11.3g" * 4 # print format
|
374 |
+
print(pf % ("all", seen, nt.sum(), mp, mr, map50, map))
|
375 |
+
|
376 |
+
# Print results per class
|
377 |
+
if (verbose or (nc < 50 and not training)) and nc > 1 and len(stats):
|
378 |
+
for i, c in enumerate(ap_class):
|
379 |
+
print(pf % (names[c], seen, nt[c], p[i], r[i], ap50[i], ap[i]))
|
380 |
+
|
381 |
+
# Print speeds
|
382 |
+
t = tuple(x / seen * 1e3 for x in dt) # speeds per image
|
383 |
+
if not training:
|
384 |
+
shape = (batch_size, 3, imgsz, imgsz)
|
385 |
+
print(
|
386 |
+
f"Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {shape}"
|
387 |
+
% t
|
388 |
+
)
|
389 |
+
|
390 |
+
# Plots
|
391 |
+
if plots:
|
392 |
+
confusion_matrix.plot(save_dir=save_dir, names=list(names.values()))
|
393 |
+
callbacks.run("on_val_end")
|
394 |
+
|
395 |
+
# Save JSON
|
396 |
+
if save_json and len(jdict):
|
397 |
+
w = (
|
398 |
+
Path(weights[0] if isinstance(weights, list) else weights).stem
|
399 |
+
if weights is not None
|
400 |
+
else ""
|
401 |
+
) # weights
|
402 |
+
anno_json = str(
|
403 |
+
Path(data.get("path", "../coco"))
|
404 |
+
/ "annotations/instances_val2017.json"
|
405 |
+
) # annotations json
|
406 |
+
pred_json = str(save_dir / f"{w}_predictions.json") # predictions json
|
407 |
+
print(f"\nEvaluating pycocotools mAP... saving {pred_json}...")
|
408 |
+
with open(pred_json, "w") as f:
|
409 |
+
json.dump(jdict, f)
|
410 |
+
|
411 |
+
try: # https://github.com/cocodataset/cocoapi/blob/master/PythonAPI/pycocoEvalDemo.ipynb
|
412 |
+
check_requirements(["pycocotools"])
|
413 |
+
from pycocotools.coco import COCO
|
414 |
+
from pycocotools.cocoeval import COCOeval
|
415 |
+
|
416 |
+
anno = COCO(anno_json) # init annotations api
|
417 |
+
pred = anno.loadRes(pred_json) # init predictions api
|
418 |
+
eval = COCOeval(anno, pred, "bbox")
|
419 |
+
if is_coco:
|
420 |
+
eval.params.imgIds = [
|
421 |
+
int(Path(x).stem) for x in dataloader.dataset.img_files
|
422 |
+
] # image IDs to evaluate
|
423 |
+
eval.evaluate()
|
424 |
+
eval.accumulate()
|
425 |
+
eval.summarize()
|
426 |
+
map, map50 = eval.stats[
|
427 |
+
:2
|
428 |
+
] # update results (mAP@0.5:0.95, mAP@0.5)
|
429 |
+
except Exception as e:
|
430 |
+
print(f"pycocotools unable to run: {e}")
|
431 |
+
|
432 |
+
# Return results
|
433 |
+
model.float() # for training
|
434 |
+
if not training:
|
435 |
+
s = (
|
436 |
+
f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}"
|
437 |
+
if save_txt
|
438 |
+
else ""
|
439 |
+
)
|
440 |
+
print(f"Results saved to {colorstr('bold', save_dir)}{s}")
|
441 |
+
maps = np.zeros(nc) + map
|
442 |
+
for i, c in enumerate(ap_class):
|
443 |
+
maps[c] = ap[i]
|
444 |
+
return (
|
445 |
+
(mp, mr, map50, map, *(loss.cpu() / len(dataloader)).tolist()),
|
446 |
+
maps,
|
447 |
+
t,
|
448 |
+
)
|
449 |
+
|
450 |
+
|
451 |
+
def parse_opt():
|
452 |
+
parser = argparse.ArgumentParser(prog="val.py")
|
453 |
+
parser.add_argument(
|
454 |
+
"--data",
|
455 |
+
type=str,
|
456 |
+
default="data/coco128.yaml",
|
457 |
+
help="dataset.yaml path",
|
458 |
+
)
|
459 |
+
parser.add_argument(
|
460 |
+
"--weights",
|
461 |
+
nargs="+",
|
462 |
+
type=str,
|
463 |
+
default="yolov5s.pt",
|
464 |
+
help="model.pt path(s)",
|
465 |
+
)
|
466 |
+
parser.add_argument(
|
467 |
+
"--batch-size", type=int, default=32, help="batch size"
|
468 |
+
)
|
469 |
+
parser.add_argument(
|
470 |
+
"--imgsz",
|
471 |
+
"--img",
|
472 |
+
"--img-size",
|
473 |
+
type=int,
|
474 |
+
default=640,
|
475 |
+
help="inference size (pixels)",
|
476 |
+
)
|
477 |
+
parser.add_argument(
|
478 |
+
"--conf-thres", type=float, default=0.001, help="confidence threshold"
|
479 |
+
)
|
480 |
+
parser.add_argument(
|
481 |
+
"--iou-thres", type=float, default=0.6, help="NMS IoU threshold"
|
482 |
+
)
|
483 |
+
parser.add_argument(
|
484 |
+
"--task", default="val", help="train, val, test, speed or study"
|
485 |
+
)
|
486 |
+
parser.add_argument(
|
487 |
+
"--device", default="", help="cuda device, i.e. 0 or 0,1,2,3 or cpu"
|
488 |
+
)
|
489 |
+
parser.add_argument(
|
490 |
+
"--single-cls",
|
491 |
+
action="store_true",
|
492 |
+
help="treat as single-class dataset",
|
493 |
+
)
|
494 |
+
parser.add_argument(
|
495 |
+
"--augment", action="store_true", help="augmented inference"
|
496 |
+
)
|
497 |
+
parser.add_argument(
|
498 |
+
"--verbose", action="store_true", help="report mAP by class"
|
499 |
+
)
|
500 |
+
parser.add_argument(
|
501 |
+
"--save-txt", action="store_true", help="save results to *.txt"
|
502 |
+
)
|
503 |
+
parser.add_argument(
|
504 |
+
"--save-hybrid",
|
505 |
+
action="store_true",
|
506 |
+
help="save label+prediction hybrid results to *.txt",
|
507 |
+
)
|
508 |
+
parser.add_argument(
|
509 |
+
"--save-conf",
|
510 |
+
action="store_true",
|
511 |
+
help="save confidences in --save-txt labels",
|
512 |
+
)
|
513 |
+
parser.add_argument(
|
514 |
+
"--save-json",
|
515 |
+
action="store_true",
|
516 |
+
help="save a COCO-JSON results file",
|
517 |
+
)
|
518 |
+
parser.add_argument(
|
519 |
+
"--project", default="runs/val", help="save to project/name"
|
520 |
+
)
|
521 |
+
parser.add_argument("--name", default="exp", help="save to project/name")
|
522 |
+
parser.add_argument(
|
523 |
+
"--exist-ok",
|
524 |
+
action="store_true",
|
525 |
+
help="existing project/name ok, do not increment",
|
526 |
+
)
|
527 |
+
parser.add_argument(
|
528 |
+
"--half", action="store_true", help="use FP16 half-precision inference"
|
529 |
+
)
|
530 |
+
opt = parser.parse_args()
|
531 |
+
opt.save_json |= opt.data.endswith("coco.yaml")
|
532 |
+
opt.save_txt |= opt.save_hybrid
|
533 |
+
opt.data = check_yaml(opt.data) # check YAML
|
534 |
+
return opt
|
535 |
+
|
536 |
+
|
537 |
+
def main(opt):
|
538 |
+
set_logging()
|
539 |
+
print(
|
540 |
+
colorstr("val: ") + ", ".join(f"{k}={v}" for k, v in vars(opt).items())
|
541 |
+
)
|
542 |
+
check_requirements(
|
543 |
+
requirements=FILE.parent / "requirements.txt",
|
544 |
+
exclude=("tensorboard", "thop"),
|
545 |
+
)
|
546 |
+
|
547 |
+
if opt.task in ("train", "val", "test"): # run normally
|
548 |
+
run(**vars(opt))
|
549 |
+
|
550 |
+
elif opt.task == "speed": # speed benchmarks
|
551 |
+
for w in (
|
552 |
+
opt.weights if isinstance(opt.weights, list) else [opt.weights]
|
553 |
+
):
|
554 |
+
run(
|
555 |
+
opt.data,
|
556 |
+
weights=w,
|
557 |
+
batch_size=opt.batch_size,
|
558 |
+
imgsz=opt.imgsz,
|
559 |
+
conf_thres=0.25,
|
560 |
+
iou_thres=0.45,
|
561 |
+
save_json=False,
|
562 |
+
plots=False,
|
563 |
+
)
|
564 |
+
|
565 |
+
elif opt.task == "study": # run over a range of settings and save/plot
|
566 |
+
# python val.py --task study --data coco.yaml --iou 0.7 --weights yolov5s.pt yolov5m.pt yolov5l.pt yolov5x.pt
|
567 |
+
x = list(range(256, 1536 + 128, 128)) # x axis (image sizes)
|
568 |
+
for w in (
|
569 |
+
opt.weights if isinstance(opt.weights, list) else [opt.weights]
|
570 |
+
):
|
571 |
+
f = f"study_{Path(opt.data).stem}_{Path(w).stem}.txt" # filename to save to
|
572 |
+
y = [] # y axis
|
573 |
+
for i in x: # img-size
|
574 |
+
print(f"\nRunning {f} point {i}...")
|
575 |
+
r, _, t = run(
|
576 |
+
opt.data,
|
577 |
+
weights=w,
|
578 |
+
batch_size=opt.batch_size,
|
579 |
+
imgsz=i,
|
580 |
+
conf_thres=opt.conf_thres,
|
581 |
+
iou_thres=opt.iou_thres,
|
582 |
+
save_json=opt.save_json,
|
583 |
+
plots=False,
|
584 |
+
)
|
585 |
+
y.append(r + t) # results and times
|
586 |
+
np.savetxt(f, y, fmt="%10.4g") # save
|
587 |
+
os.system("zip -r study.zip study_*.txt")
|
588 |
+
plot_study_txt(x=x) # plot
|
589 |
+
|
590 |
+
|
591 |
+
if __name__ == "__main__":
|
592 |
+
opt = parse_opt()
|
593 |
+
main(opt)
|
yolo_inference_util.py
ADDED
@@ -0,0 +1,369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import argparse
|
2 |
+
import sys
|
3 |
+
from pathlib import Path
|
4 |
+
|
5 |
+
import cv2
|
6 |
+
import numpy as np
|
7 |
+
import torch
|
8 |
+
import torch.backends.cudnn as cudnn
|
9 |
+
|
10 |
+
from models.experimental import attempt_load
|
11 |
+
from utils.datasets import LoadImages, LoadStreams
|
12 |
+
from utils.general import (
|
13 |
+
apply_classifier,
|
14 |
+
check_img_size,
|
15 |
+
check_imshow,
|
16 |
+
check_requirements,
|
17 |
+
check_suffix,
|
18 |
+
colorstr,
|
19 |
+
increment_path,
|
20 |
+
is_ascii,
|
21 |
+
non_max_suppression,
|
22 |
+
save_one_box,
|
23 |
+
scale_coords,
|
24 |
+
set_logging,
|
25 |
+
strip_optimizer,
|
26 |
+
xyxy2xywh,
|
27 |
+
)
|
28 |
+
from utils.plots import Annotator, colors
|
29 |
+
from utils.torch_utils import load_classifier, select_device, time_sync
|
30 |
+
|
31 |
+
# FILE = Path(__file__).resolve()
|
32 |
+
# ROOT = FILE.parents[0] # YOLOv5 root directory
|
33 |
+
# if str(ROOT) not in sys.path:
|
34 |
+
# sys.path.append(str(ROOT)) # add ROOT to PATH
|
35 |
+
|
36 |
+
|
37 |
+
|
38 |
+
@torch.no_grad()
|
39 |
+
def run_yolo_v5(
|
40 |
+
weights="yolov5s.pt", # model.pt path(s)
|
41 |
+
source="data/images", # file/dir/URL/glob, 0 for webcam
|
42 |
+
imgsz=640, # inference size (pixels)
|
43 |
+
conf_thres=0.25, # confidence threshold
|
44 |
+
iou_thres=0.45, # NMS IOU threshold
|
45 |
+
max_det=1000, # maximum detections per image
|
46 |
+
device="", # cuda device, i.e. 0 or 0,1,2,3 or cpu
|
47 |
+
view_img=False, # show results
|
48 |
+
save_txt=False, # save results to *.txt
|
49 |
+
save_conf=False, # save confidences in --save-txt labels
|
50 |
+
save_crop=False, # save cropped prediction boxes
|
51 |
+
nosave=False, # do not save images/videos
|
52 |
+
classes=None, # filter by class: --class 0, or --class 0 2 3
|
53 |
+
agnostic_nms=False, # class-agnostic NMS
|
54 |
+
augment=False, # augmented inference
|
55 |
+
visualize=False, # visualize features
|
56 |
+
update=False, # update all models
|
57 |
+
project="runs/detect", # save results to project/name
|
58 |
+
name="exp", # save results to project/name
|
59 |
+
exist_ok=False, # existing project/name ok, do not increment
|
60 |
+
line_thickness=3, # bounding box thickness (pixels)
|
61 |
+
hide_labels=False, # hide labels
|
62 |
+
hide_conf=False, # hide confidences
|
63 |
+
half=False, # use FP16 half-precision inference
|
64 |
+
):
|
65 |
+
save_img = not nosave and not source.endswith(
|
66 |
+
".txt"
|
67 |
+
) # save inference images
|
68 |
+
webcam = (
|
69 |
+
source.isnumeric()
|
70 |
+
or source.endswith(".txt")
|
71 |
+
or source.lower().startswith(
|
72 |
+
("rtsp://", "rtmp://", "http://", "https://")
|
73 |
+
)
|
74 |
+
)
|
75 |
+
|
76 |
+
# Directories
|
77 |
+
save_dir = increment_path(
|
78 |
+
Path(project) / name, exist_ok=exist_ok
|
79 |
+
) # increment run
|
80 |
+
(save_dir / "labels" if save_txt else save_dir).mkdir(
|
81 |
+
parents=True, exist_ok=True
|
82 |
+
) # make dir
|
83 |
+
|
84 |
+
# Initialize
|
85 |
+
set_logging()
|
86 |
+
device = select_device(device)
|
87 |
+
half &= device.type != "cpu" # half precision only supported on CUDA
|
88 |
+
|
89 |
+
# Load model
|
90 |
+
w = weights[0] if isinstance(weights, list) else weights
|
91 |
+
classify, suffix, suffixes = (
|
92 |
+
False,
|
93 |
+
Path(w).suffix.lower(),
|
94 |
+
[".pt", ".onnx", ".tflite", ".pb", ""],
|
95 |
+
)
|
96 |
+
check_suffix(w, suffixes) # check weights have acceptable suffix
|
97 |
+
pt, onnx, tflite, pb, saved_model = (
|
98 |
+
suffix == x for x in suffixes
|
99 |
+
) # backend booleans
|
100 |
+
stride, names = 64, [f"class{i}" for i in range(1000)] # assign defaults
|
101 |
+
if pt:
|
102 |
+
model = attempt_load(weights, map_location=device) # load FP32 model
|
103 |
+
stride = int(model.stride.max()) # model stride
|
104 |
+
names = (
|
105 |
+
model.module.names if hasattr(model, "module") else model.names
|
106 |
+
) # get class names
|
107 |
+
if half:
|
108 |
+
model.half() # to FP16
|
109 |
+
if classify: # second-stage classifier
|
110 |
+
modelc = load_classifier(name="resnet50", n=2) # initialize
|
111 |
+
modelc.load_state_dict(
|
112 |
+
torch.load("resnet50.pt", map_location=device)["model"]
|
113 |
+
).to(device).eval()
|
114 |
+
elif onnx:
|
115 |
+
check_requirements(("onnx", "onnxruntime"))
|
116 |
+
import onnxruntime
|
117 |
+
|
118 |
+
session = onnxruntime.InferenceSession(w, None)
|
119 |
+
else: # TensorFlow models
|
120 |
+
check_requirements(("tensorflow>=2.4.1",))
|
121 |
+
import tensorflow as tf
|
122 |
+
|
123 |
+
if (
|
124 |
+
pb
|
125 |
+
): # https://www.tensorflow.org/guide/migrate#a_graphpb_or_graphpbtxt
|
126 |
+
|
127 |
+
def wrap_frozen_graph(gd, inputs, outputs):
|
128 |
+
x = tf.compat.v1.wrap_function(
|
129 |
+
lambda: tf.compat.v1.import_graph_def(gd, name=""), []
|
130 |
+
) # wrapped import
|
131 |
+
return x.prune(
|
132 |
+
tf.nest.map_structure(x.graph.as_graph_element, inputs),
|
133 |
+
tf.nest.map_structure(x.graph.as_graph_element, outputs),
|
134 |
+
)
|
135 |
+
|
136 |
+
graph_def = tf.Graph().as_graph_def()
|
137 |
+
graph_def.ParseFromString(open(w, "rb").read())
|
138 |
+
frozen_func = wrap_frozen_graph(
|
139 |
+
gd=graph_def, inputs="x:0", outputs="Identity:0"
|
140 |
+
)
|
141 |
+
elif saved_model:
|
142 |
+
model = tf.keras.models.load_model(w)
|
143 |
+
elif tflite:
|
144 |
+
interpreter = tf.lite.Interpreter(
|
145 |
+
model_path=w
|
146 |
+
) # load TFLite model
|
147 |
+
interpreter.allocate_tensors() # allocate
|
148 |
+
input_details = interpreter.get_input_details() # inputs
|
149 |
+
output_details = interpreter.get_output_details() # outputs
|
150 |
+
int8 = (
|
151 |
+
input_details[0]["dtype"] == np.uint8
|
152 |
+
) # is TFLite quantized uint8 model
|
153 |
+
imgsz = check_img_size(imgsz, s=stride) # check image size
|
154 |
+
ascii = is_ascii(names) # names are ascii (use PIL for UTF-8)
|
155 |
+
|
156 |
+
# Dataloader
|
157 |
+
print("Loading data from the source", source)
|
158 |
+
if webcam:
|
159 |
+
view_img = check_imshow()
|
160 |
+
cudnn.benchmark = (
|
161 |
+
True # set True to speed up constant image size inference
|
162 |
+
)
|
163 |
+
dataset = LoadStreams(source, img_size=imgsz, stride=stride, auto=pt)
|
164 |
+
bs = len(dataset) # batch_size
|
165 |
+
else:
|
166 |
+
dataset = LoadImages(source, img_size=imgsz, stride=stride, auto=pt)
|
167 |
+
bs = 1 # batch_size
|
168 |
+
vid_path, vid_writer = [None] * bs, [None] * bs
|
169 |
+
|
170 |
+
# Run inference
|
171 |
+
if pt and device.type != "cpu":
|
172 |
+
model(
|
173 |
+
torch.zeros(1, 3, *imgsz)
|
174 |
+
.to(device)
|
175 |
+
.type_as(next(model.parameters()))
|
176 |
+
) # run once
|
177 |
+
dt, seen = [0.0, 0.0, 0.0], 0
|
178 |
+
results = []
|
179 |
+
for path, img, im0s, vid_cap in dataset:
|
180 |
+
t1 = time_sync()
|
181 |
+
if onnx:
|
182 |
+
img = img.astype("float32")
|
183 |
+
else:
|
184 |
+
img = torch.from_numpy(img).to(device)
|
185 |
+
img = img.half() if half else img.float() # uint8 to fp16/32
|
186 |
+
img = img / 255.0 # 0 - 255 to 0.0 - 1.0
|
187 |
+
if len(img.shape) == 3:
|
188 |
+
img = img[None] # expand for batch dim
|
189 |
+
t2 = time_sync()
|
190 |
+
dt[0] += t2 - t1
|
191 |
+
|
192 |
+
# Inference
|
193 |
+
if pt:
|
194 |
+
visualize = (
|
195 |
+
increment_path(save_dir / Path(path).stem, mkdir=True)
|
196 |
+
if visualize
|
197 |
+
else False
|
198 |
+
)
|
199 |
+
pred = model(img, augment=augment, visualize=visualize)[0]
|
200 |
+
elif onnx:
|
201 |
+
pred = torch.tensor(
|
202 |
+
session.run(
|
203 |
+
[session.get_outputs()[0].name],
|
204 |
+
{session.get_inputs()[0].name: img},
|
205 |
+
)
|
206 |
+
)
|
207 |
+
else: # tensorflow model (tflite, pb, saved_model)
|
208 |
+
imn = img.permute(0, 2, 3, 1).cpu().numpy() # image in numpy
|
209 |
+
if pb:
|
210 |
+
pred = frozen_func(x=tf.constant(imn)).numpy()
|
211 |
+
elif saved_model:
|
212 |
+
pred = model(imn, training=False).numpy()
|
213 |
+
elif tflite:
|
214 |
+
if int8:
|
215 |
+
scale, zero_point = input_details[0]["quantization"]
|
216 |
+
imn = (imn / scale + zero_point).astype(
|
217 |
+
np.uint8
|
218 |
+
) # de-scale
|
219 |
+
interpreter.set_tensor(input_details[0]["index"], imn)
|
220 |
+
interpreter.invoke()
|
221 |
+
pred = interpreter.get_tensor(output_details[0]["index"])
|
222 |
+
if int8:
|
223 |
+
scale, zero_point = output_details[0]["quantization"]
|
224 |
+
pred = (
|
225 |
+
pred.astype(np.float32) - zero_point
|
226 |
+
) * scale # re-scale
|
227 |
+
pred[..., 0] *= imgsz[1] # x
|
228 |
+
pred[..., 1] *= imgsz[0] # y
|
229 |
+
pred[..., 2] *= imgsz[1] # w
|
230 |
+
pred[..., 3] *= imgsz[0] # h
|
231 |
+
pred = torch.tensor(pred)
|
232 |
+
t3 = time_sync()
|
233 |
+
dt[1] += t3 - t2
|
234 |
+
|
235 |
+
# NMS
|
236 |
+
pred = non_max_suppression(
|
237 |
+
pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det
|
238 |
+
)
|
239 |
+
dt[2] += time_sync() - t3
|
240 |
+
|
241 |
+
# Second-stage classifier (optional)
|
242 |
+
if classify:
|
243 |
+
pred = apply_classifier(pred, modelc, img, im0s)
|
244 |
+
|
245 |
+
# Process predictions
|
246 |
+
for i, det in enumerate(pred): # per image
|
247 |
+
seen += 1
|
248 |
+
if webcam: # batch_size >= 1
|
249 |
+
p, s, im0, frame = (
|
250 |
+
path[i],
|
251 |
+
f"{i}: ",
|
252 |
+
im0s[i].copy(),
|
253 |
+
dataset.count,
|
254 |
+
)
|
255 |
+
else:
|
256 |
+
p, s, im0, frame = (
|
257 |
+
path,
|
258 |
+
"",
|
259 |
+
im0s.copy(),
|
260 |
+
getattr(dataset, "frame", 0),
|
261 |
+
)
|
262 |
+
|
263 |
+
p = Path(p) # to Path
|
264 |
+
save_path = str(save_dir / p.name) # img.jpg
|
265 |
+
txt_path = str(save_dir / "labels" / p.stem) + (
|
266 |
+
"" if dataset.mode == "image" else f"_{frame}"
|
267 |
+
) # img.txt
|
268 |
+
s += "%gx%g " % img.shape[2:] # print string
|
269 |
+
gn = torch.tensor(im0.shape)[
|
270 |
+
[1, 0, 1, 0]
|
271 |
+
] # normalization gain whwh
|
272 |
+
imc = im0.copy() if save_crop else im0 # for save_crop
|
273 |
+
annotator = Annotator(
|
274 |
+
im0, line_width=line_thickness, pil=not ascii
|
275 |
+
)
|
276 |
+
if len(det):
|
277 |
+
# Rescale boxes from img_size to im0 size
|
278 |
+
det[:, :4] = scale_coords(
|
279 |
+
img.shape[2:], det[:, :4], im0.shape
|
280 |
+
).round()
|
281 |
+
results.append((im0, det))
|
282 |
+
# Print results
|
283 |
+
for c in det[:, -1].unique():
|
284 |
+
n = (det[:, -1] == c).sum() # detections per class
|
285 |
+
s += f"{n} {names[int(c)]}{'s' * (n > 1)}, " # add to string
|
286 |
+
|
287 |
+
# Write results
|
288 |
+
for *xyxy, conf, cls in reversed(det):
|
289 |
+
if save_txt: # Write to file
|
290 |
+
xywh = (
|
291 |
+
(xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn)
|
292 |
+
.view(-1)
|
293 |
+
.tolist()
|
294 |
+
) # normalized xywh
|
295 |
+
line = (
|
296 |
+
(cls, *xywh, conf) if save_conf else (cls, *xywh)
|
297 |
+
) # label format
|
298 |
+
with open(txt_path + ".txt", "a") as f:
|
299 |
+
f.write(("%g " * len(line)).rstrip() % line + "\n")
|
300 |
+
|
301 |
+
if save_img or save_crop or view_img: # Add bbox to image
|
302 |
+
c = int(cls) # integer class
|
303 |
+
label = (
|
304 |
+
None
|
305 |
+
if hide_labels
|
306 |
+
else (
|
307 |
+
names[c]
|
308 |
+
if hide_conf
|
309 |
+
else f"{names[c]} {conf:.2f}"
|
310 |
+
)
|
311 |
+
)
|
312 |
+
annotator.box_label(xyxy, label, color=colors(c, True))
|
313 |
+
if save_crop:
|
314 |
+
save_one_box(
|
315 |
+
xyxy,
|
316 |
+
imc,
|
317 |
+
file=save_dir
|
318 |
+
/ "crops"
|
319 |
+
/ names[c]
|
320 |
+
/ f"{p.stem}.jpg",
|
321 |
+
BGR=True,
|
322 |
+
)
|
323 |
+
# Print time (inference-only)
|
324 |
+
print(f"{s}Done. ({t3 - t2:.3f}s)")
|
325 |
+
|
326 |
+
# Stream results
|
327 |
+
im0 = annotator.result()
|
328 |
+
if view_img:
|
329 |
+
cv2.imshow(str(p), im0)
|
330 |
+
cv2.waitKey(1) # 1 millisecond
|
331 |
+
|
332 |
+
# Save results (image with detections)
|
333 |
+
if save_img:
|
334 |
+
if dataset.mode == "image":
|
335 |
+
cv2.imwrite(save_path, im0)
|
336 |
+
else: # 'video' or 'stream'
|
337 |
+
if vid_path[i] != save_path: # new video
|
338 |
+
vid_path[i] = save_path
|
339 |
+
if isinstance(vid_writer[i], cv2.VideoWriter):
|
340 |
+
vid_writer[
|
341 |
+
i
|
342 |
+
].release() # release previous video writer
|
343 |
+
if vid_cap: # video
|
344 |
+
fps = vid_cap.get(cv2.CAP_PROP_FPS)
|
345 |
+
w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH))
|
346 |
+
h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
347 |
+
else: # stream
|
348 |
+
fps, w, h = 30, im0.shape[1], im0.shape[0]
|
349 |
+
save_path += ".mp4"
|
350 |
+
vid_writer[i] = cv2.VideoWriter(
|
351 |
+
save_path,
|
352 |
+
cv2.VideoWriter_fourcc(*"mp4v"),
|
353 |
+
fps,
|
354 |
+
(w, h),
|
355 |
+
)
|
356 |
+
vid_writer[i].write(im0)
|
357 |
+
|
358 |
+
# Print results
|
359 |
+
t = tuple(x / seen * 1e3 for x in dt) # speeds per image
|
360 |
+
print(
|
361 |
+
f"Speed: %.1fms pre-process, %.1fms inference, %.1fms NMS per image at shape {(1, 3, *imgsz)}"
|
362 |
+
% t
|
363 |
+
)
|
364 |
+
return results
|
365 |
+
# if save_txt or save_img:
|
366 |
+
# s = f"\n{len(list(save_dir.glob('labels/*.txt')))} labels saved to {save_dir / 'labels'}" if save_txt else ''
|
367 |
+
# print(f"Results saved to {colorstr('bold', save_dir)}{s}")
|
368 |
+
# if update:
|
369 |
+
# strip_optimizer(weights) # update model (to fix SourceChangeWarning)
|
yolov5s.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:f1610cfd81f8cab94254b35f6b7da2981fa40f93ad1bd3dd1803c52e7f44753e
|
3 |
+
size 14795158
|