Merge branch 'main' into rm-smoothing

pull/16014/head
Laughing 1 week ago committed by GitHub
commit 768301fb6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 47
      .github/workflows/ci.yaml
  2. 8
      .github/workflows/docker.yaml
  3. 6
      .github/workflows/docs.yml
  4. 2
      .github/workflows/format.yml
  5. 19
      .github/workflows/publish.yml
  6. 2
      .github/workflows/stale.yml
  7. 1
      docker/Dockerfile
  8. 12
      docker/Dockerfile-cpu
  9. 1
      docker/Dockerfile-runner
  10. 21
      docs/en/modes/benchmark.md
  11. 343
      docs/overrides/javascript/extra.js
  12. 19
      docs/overrides/javascript/giscus.js
  13. 11
      docs/overrides/stylesheets/style.css
  14. 4
      mkdocs.yml
  15. 2
      ultralytics/__init__.py
  16. 5
      ultralytics/data/augment.py
  17. 3
      ultralytics/engine/exporter.py
  18. 3
      ultralytics/models/fastsam/predict.py
  19. 3
      ultralytics/models/rtdetr/train.py
  20. 4
      ultralytics/solutions/heatmap.py
  21. 78
      ultralytics/solutions/object_counter.py
  22. 2
      ultralytics/utils/benchmarks.py
  23. 7
      ultralytics/utils/callbacks/wb.py

@ -52,16 +52,15 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip" # caching pip dependencies
- uses: astral-sh/setup-uv@v3
- name: Install requirements
shell: bash # for Windows compatibility
run: |
python -m pip install --upgrade pip wheel
pip install . --extra-index-url https://download.pytorch.org/whl/cpu
uv pip install --system . --extra-index-url https://download.pytorch.org/whl/cpu
- name: Check environment
run: |
yolo checks
pip list
uv pip list
- name: Test HUB training
shell: python
env:
@ -111,6 +110,7 @@ jobs:
- name: Install requirements
shell: bash # for Windows compatibility
run: |
# Warnings: uv causes numpy errors during benchmarking
python -m pip install --upgrade pip wheel
pip install -e ".[export]" "coverage[toml]" --extra-index-url https://download.pytorch.org/whl/cpu
- name: Check environment
@ -143,7 +143,7 @@ jobs:
coverage xml -o coverage-benchmarks.xml
- name: Upload Coverage Reports to CodeCov
if: github.repository == 'ultralytics/ultralytics'
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
flags: Benchmarks
env:
@ -172,12 +172,11 @@ jobs:
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip" # caching pip dependencies
- uses: astral-sh/setup-uv@v3
- name: Install requirements
shell: bash # for Windows compatibility
run: |
# CoreML must be installed before export due to protobuf error from AutoInstall
python -m pip install --upgrade pip wheel
slow=""
torch=""
if [ "${{ matrix.torch }}" == "1.8.0" ]; then
@ -186,11 +185,11 @@ jobs:
if [[ "${{ github.event_name }}" =~ ^(schedule|workflow_dispatch)$ ]]; then
slow="pycocotools mlflow"
fi
pip install -e ".[export]" $torch $slow pytest-cov --extra-index-url https://download.pytorch.org/whl/cpu
uv pip install --system -e ".[export]" $torch $slow pytest-cov --extra-index-url https://download.pytorch.org/whl/cpu
- name: Check environment
run: |
yolo checks
pip list
uv pip list
- name: Pytest tests
shell: bash # for Windows compatibility
run: |
@ -201,7 +200,7 @@ jobs:
pytest $slow --cov=ultralytics/ --cov-report xml tests/
- name: Upload Coverage Reports to CodeCov
if: github.repository == 'ultralytics/ultralytics' # && matrix.os == 'ubuntu-latest' && matrix.python-version == '3.11'
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
flags: Tests
env:
@ -213,12 +212,13 @@ jobs:
runs-on: gpu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v3
- name: Install requirements
run: pip install . pytest-cov
run: uv pip install --system . pytest-cov
- name: Check environment
run: |
yolo checks
pip list
uv pip list
- name: Pytest tests
run: |
slow=""
@ -227,7 +227,7 @@ jobs:
fi
pytest $slow --cov=ultralytics/ --cov-report xml tests/test_cuda.py
- name: Upload Coverage Reports to CodeCov
uses: codecov/codecov-action@v4
uses: codecov/codecov-action@v5
with:
flags: GPU
env:
@ -294,13 +294,8 @@ jobs:
channels: conda-forge,defaults
channel-priority: true
activate-environment: anaconda-client-env
- name: Cleanup toolcache
run: |
echo "Free space before deletion:"
df -h /
rm -rf /opt/hostedtoolcache
echo "Free space after deletion:"
df -h /
- name: Cleanup disk space
uses: ultralytics/actions/cleanup-disk@main
- name: Install Linux packages
run: |
# Fix cv2 ImportError: 'libEGL.so.1: cannot open shared object file: No such file or directory'
@ -348,14 +343,14 @@ jobs:
Summary:
runs-on: ubuntu-latest
needs: [HUB, Benchmarks, Tests, GPU, RaspberryPi, Conda] # Add job names that you want to check for failure
if: always() # This ensures the job runs even if previous jobs fail
needs: [HUB, Benchmarks, Tests, GPU, RaspberryPi, Conda]
if: always()
steps:
- name: Check for failure and notify
if: (needs.HUB.result == 'failure' || needs.Benchmarks.result == 'failure' || needs.Tests.result == 'failure' || needs.GPU.result == 'failure' || needs.RaspberryPi.result == 'failure' || needs.Conda.result == 'failure' ) && github.repository == 'ultralytics/ultralytics' && (github.event_name == 'schedule' || github.event_name == 'push') && github.run_attempt == '1'
uses: slackapi/slack-github-action@v1.27.0
uses: slackapi/slack-github-action@v2.0.0
with:
webhook-type: incoming-webhook
webhook: ${{ secrets.SLACK_WEBHOOK_URL_YOLO }}
payload: |
{"text": "<!channel> GitHub Actions error for ${{ github.workflow }} ❌\n\n\n*Repository:* https://github.com/${{ github.repository }}\n*Action:* https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Author:* ${{ github.actor }}\n*Event:* ${{ github.event_name }}\n"}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_YOLO }}
text: "<!channel> GitHub Actions error for ${{ github.workflow }} ❌\n\n\n*Repository:* https://github.com/${{ github.repository }}\n*Action:* https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Author:* ${{ github.actor }}\n*Event:* ${{ github.event_name }}\n"

@ -202,9 +202,9 @@ jobs:
steps:
- name: Check for failure and notify
if: needs.docker.result == 'failure' && github.repository == 'ultralytics/ultralytics' && github.event_name == 'push' && github.run_attempt == '1'
uses: slackapi/slack-github-action@v1.27.0
uses: slackapi/slack-github-action@v2.0.0
with:
webhook-type: incoming-webhook
webhook: ${{ secrets.SLACK_WEBHOOK_URL_YOLO }}
payload: |
{"text": "<!channel> GitHub Actions error for ${{ github.workflow }} ❌\n\n\n*Repository:* https://github.com/${{ github.repository }}\n*Action:* https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Author:* ${{ github.actor }}\n*Event:* ${{ github.event_name }}\n"}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_YOLO }}
text: "<!channel> GitHub Actions error for ${{ github.workflow }} ❌\n\n\n*Repository:* https://github.com/${{ github.repository }}\n*Action:* https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Author:* ${{ github.actor }}\n*Event:* ${{ github.event_name }}\n"

@ -29,7 +29,7 @@ on:
jobs:
Docs:
if: github.repository == 'ultralytics/ultralytics'
runs-on: macos-14
runs-on: ubuntu-latest
steps:
- name: Git config
run: |
@ -46,9 +46,9 @@ jobs:
uses: actions/setup-python@v5
with:
python-version: "3.x"
cache: "pip" # caching pip dependencies
- uses: astral-sh/setup-uv@v3
- name: Install Dependencies
run: pip install ruff black tqdm minify-html mkdocs-material "mkdocstrings[python]" mkdocs-jupyter mkdocs-redirects mkdocs-ultralytics-plugin mkdocs-macros-plugin
run: uv pip install --system ruff black tqdm minify-html mkdocs-material "mkdocstrings[python]" mkdocs-jupyter mkdocs-redirects mkdocs-ultralytics-plugin mkdocs-macros-plugin
- name: Ruff fixes
continue-on-error: true
run: ruff check --fix --unsafe-fixes --select D --ignore=D100,D104,D203,D205,D212,D213,D401,D406,D407,D413 .

@ -15,7 +15,7 @@ on:
jobs:
format:
runs-on: macos-14
runs-on: ubuntu-latest
steps:
- name: Run Ultralytics Formatting
uses: ultralytics/actions@main

@ -17,7 +17,7 @@ jobs:
if: github.repository == 'ultralytics/ultralytics' && github.actor == 'glenn-jocher'
name: Publish
runs-on: ubuntu-latest
environment: # for GitHub Deployments tab
environment: # for GitHub Deployments tab
name: Release - PyPI
url: https://pypi.org/p/ultralytics
permissions:
@ -90,19 +90,20 @@ jobs:
fi
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
echo "PR_TITLE=$PR_TITLE" >> $GITHUB_ENV
- name: Notify on Slack (Success)
if: success() && github.event_name == 'push' && steps.check_pypi.outputs.increment == 'True'
uses: slackapi/slack-github-action@v1.27.0
uses: slackapi/slack-github-action@v2.0.0
with:
webhook-type: incoming-webhook
webhook: ${{ secrets.SLACK_WEBHOOK_URL_YOLO }}
payload: |
{"text": "<!channel> GitHub Actions success for ${{ github.workflow }} ✅\n\n\n*Repository:* https://github.com/${{ github.repository }}\n*Action:* https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Author:* ${{ github.actor }}\n*Event:* NEW `${{ github.repository }} ${{ steps.check_pypi.outputs.current_tag }}` pip package published 😃\n*Job Status:* ${{ job.status }}\n*Pull Request:* <https://github.com/${{ github.repository }}/pull/${{ env.PR_NUMBER }}> ${{ env.PR_TITLE }}\n"}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_YOLO }}
text: "<!channel> GitHub Actions success for ${{ github.workflow }} ✅\n\n\n*Repository:* https://github.com/${{ github.repository }}\n*Action:* https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Author:* ${{ github.actor }}\n*Event:* NEW `${{ github.repository }} ${{ steps.check_pypi.outputs.current_tag }}` pip package published 😃\n*Job Status:* ${{ job.status }}\n*Pull Request:* <https://github.com/${{ github.repository }}/pull/${{ env.PR_NUMBER }}> ${{ env.PR_TITLE }}\n"
- name: Notify on Slack (Failure)
if: failure()
uses: slackapi/slack-github-action@v1.27.0
uses: slackapi/slack-github-action@v2.0.0
with:
webhook-type: incoming-webhook
webhook: ${{ secrets.SLACK_WEBHOOK_URL_YOLO }}
payload: |
{"text": "<!channel> GitHub Actions error for ${{ github.workflow }} ❌\n\n\n*Repository:* https://github.com/${{ github.repository }}\n*Action:* https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Author:* ${{ github.actor }}\n*Event:* ${{ github.event_name }}\n*Job Status:* ${{ job.status }}\n*Pull Request:* <https://github.com/${{ github.repository }}/pull/${{ env.PR_NUMBER }}> ${{ env.PR_TITLE }}\n"}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL_YOLO }}
text: "<!channel> GitHub Actions error for ${{ github.workflow }} ❌\n\n\n*Repository:* https://github.com/${{ github.repository }}\n*Action:* https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\n*Author:* ${{ github.actor }}\n*Event:* ${{ github.event_name }}\n*Job Status:* ${{ job.status }}\n*Pull Request:* <https://github.com/${{ github.repository }}/pull/${{ env.PR_NUMBER }}> ${{ env.PR_TITLE }}\n"

@ -8,7 +8,7 @@ on:
permissions:
pull-requests: write
issues: write
jobs:
stale:
runs-on: ubuntu-latest

@ -56,7 +56,6 @@ RUN pip install numpy==1.23.5
# Remove extra build files
RUN rm -rf tmp /root/.config/Ultralytics/persistent_cache.json
# Usage Examples -------------------------------------------------------------------------------------------------------
# Build and Push

@ -2,8 +2,8 @@
# Builds ultralytics/ultralytics:latest-cpu image on DockerHub https://hub.docker.com/r/ultralytics/ultralytics
# Image is CPU-optimized for ONNX, OpenVINO and PyTorch YOLO11 deployments
# Start FROM Ubuntu image https://hub.docker.com/_/ubuntu
FROM ubuntu:23.10
# Use official Python base image for reproducibility (3.11.10 for export and 3.12.6 for inference)
FROM python:3.11.10-slim-bookworm
# Set environment variables
ENV PYTHONUNBUFFERED=1 \
@ -39,14 +39,14 @@ RUN pip install -e ".[export]" --extra-index-url https://download.pytorch.org/wh
RUN yolo export model=tmp/yolo11n.pt format=edgetpu imgsz=32
RUN yolo export model=tmp/yolo11n.pt format=ncnn imgsz=32
# Requires Python<=3.10, bug with paddlepaddle==2.5.0 https://github.com/PaddlePaddle/X2Paddle/issues/991
# RUN pip install "paddlepaddle>=2.6.0" x2paddle
# Creates a symbolic link to make 'python' point to 'python3'
RUN ln -sf /usr/bin/python3 /usr/bin/python
RUN pip install "paddlepaddle>=2.6.0" x2paddle
# Remove extra build files
RUN rm -rf tmp /root/.config/Ultralytics/persistent_cache.json
# Set default command to bash
CMD ["/bin/bash"]
# Usage Examples -------------------------------------------------------------------------------------------------------
# Build and Push

@ -35,7 +35,6 @@ ENTRYPOINT sh -c './config.sh --url https://github.com/ultralytics/ultralytics \
--replace && \
./run.sh'
# Usage Examples -------------------------------------------------------------------------------------------------------
# Build and Push

@ -18,15 +18,18 @@ keywords: model benchmarking, YOLO11, Ultralytics, performance evaluation, expor
<div style="display: flex; align-items: flex-start;">
<div style="margin-right: 20px;">
<label><input type="checkbox" name="algorithm" value="YOLO11" checked><span>Ultralytics YOLO11</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv6" checked><span>YOLOv6</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv7" checked><span>YOLOv7</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv10" checked><span>YOLOv10</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLO11" checked><span>YOLO11</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv10" checked><span>YOLOv10</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv9" checked><span>YOLOv9</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv8" checked><span>Ultralytics YOLOv8</span></label><br>
<label><input type="checkbox" name="algorithm" value="PPYOLOE" checked><span>PPYOLOE</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv5" checked><span>Ultralytics YOLOv5</span></label>
</div>
<label><input type="checkbox" name="algorithm" value="YOLOv8" checked><span>YOLOv8</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv7" checked><span>YOLOv7</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv6-3.0" checked><span>YOLOv6-3.0</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOv5" checked><span>YOLOv5</span></label><br>
<label><input type="checkbox" name="algorithm" value="PP-YOLOE+" checked><span>PP-YOLOE+</span></label><br>
<label><input type="checkbox" name="algorithm" value="DAMO-YOLO" checked><span>DAMO-YOLO</span></label><br>
<label><input type="checkbox" name="algorithm" value="YOLOX" checked><span>YOLOX</span></label><br>
<label><input type="checkbox" name="algorithm" value="RTDETRv2" checked><span>RTDETRv2</span></label>
</div>
<div style="flex-grow: 1;"><canvas id="chart"></canvas></div> <!-- Canva for plotting benchmarks -->
</div>
@ -102,7 +105,7 @@ Arguments such as `model`, `data`, `imgsz`, `half`, `device`, and `verbose` prov
| `imgsz` | `640` | The input image size for the model. Can be a single integer for square images or a tuple `(width, height)` for non-square, e.g., `(640, 480)`. |
| `half` | `False` | Enables FP16 (half-precision) inference, reducing memory usage and possibly increasing speed on compatible hardware. Use `half=True` to enable. |
| `int8` | `False` | Activates INT8 quantization for further optimized performance on supported devices, especially useful for edge devices. Set `int8=True` to use. |
| `device` | `None` | Defines the computation device(s) for benchmarking, such as `"cpu"`, `"cuda:0"`, or a list of devices like `"cuda:0,1"` for multi-GPU setups. |
| `device` | `None` | Defines the computation device(s) for benchmarking, such as `"cpu"` or `"cuda:0"`. |
| `verbose` | `False` | Controls the level of detail in logging output. A boolean value; set `verbose=True` for detailed logs or a float for thresholding errors. |
## Export Formats

@ -1,4 +1,4 @@
// Apply theme based on user preference
// Light/Dark Mode -----------------------------------------------------------------------------------------------------
const applyTheme = (isDark) => {
document.body.setAttribute(
"data-md-color-scheme",
@ -12,24 +12,7 @@ const applyTheme = (isDark) => {
// Check and apply auto theme
const checkAutoTheme = () => {
const supportedLangCodes = [
"en",
"zh",
"ko",
"ja",
"ru",
"de",
"fr",
"es",
"pt",
"it",
"tr",
"vi",
"ar",
];
const langCode = window.location.pathname.split("/")[1];
const localStorageKey = `${supportedLangCodes.includes(langCode) ? `/${langCode}` : ""}/.__palette`;
const palette = JSON.parse(localStorage.getItem(localStorageKey) || "{}");
const palette = JSON.parse(localStorage.getItem(".__palette") || "{}");
if (palette.index === 0) {
applyTheme(window.matchMedia("(prefers-color-scheme: dark)").matches);
@ -47,45 +30,51 @@ checkAutoTheme();
document.addEventListener("DOMContentLoaded", () => {
const autoThemeInput = document.getElementById("__palette_1");
autoThemeInput?.addEventListener("click", () => {
if (autoThemeInput.checked) {
setTimeout(checkAutoTheme);
}
if (autoThemeInput.checked) setTimeout(checkAutoTheme);
});
});
// Iframe navigation
window.onhashchange = () => {
window.parent.postMessage(
{
type: "navigation",
hash:
window.location.pathname +
window.location.search +
window.location.hash,
},
"*",
);
};
// Add Inkeep button
// Inkeep --------------------------------------------------------------------------------------------------------------
document.addEventListener("DOMContentLoaded", () => {
const enableSearchBar = true;
const inkeepScript = document.createElement("script");
inkeepScript.src = "https://unpkg.com/@inkeep/uikit-js@0.3.11/dist/embed.js";
inkeepScript.src = "https://unpkg.com/@inkeep/uikit-js@0.3.18/dist/embed.js";
inkeepScript.type = "module";
inkeepScript.defer = true;
document.head.appendChild(inkeepScript);
// Configure and initialize the widget
const addInkeepWidget = () => {
if (enableSearchBar) {
const containerDiv = document.createElement("div");
containerDiv.style.transform = "scale(0.7)";
containerDiv.style.transformOrigin = "left center";
const inkeepDiv = document.createElement("div");
inkeepDiv.id = "inkeepSearchBar";
containerDiv.appendChild(inkeepDiv);
const headerElement = document.querySelector(".md-header__inner");
const searchContainer = headerElement.querySelector(".md-header__source");
if (headerElement && searchContainer) {
headerElement.insertBefore(containerDiv, searchContainer);
}
}
// configure and initialize the widget
const addInkeepWidget = (componentType, targetElementId) => {
const inkeepWidget = Inkeep().embed({
componentType: "ChatButton",
componentType,
...(componentType !== "ChatButton"
? { targetElement: targetElementId }
: {}),
colorModeSync: {
observedElement: document.documentElement,
isDarkModeCallback: (el) => {
const currentTheme = el.getAttribute("data-color-mode");
return currentTheme === "dark";
},
colorModeAttribute: "data-color-mode",
colorModeAttribute: "data-color-mode-scheme",
},
properties: {
chatButtonType: "PILL",
@ -101,13 +90,12 @@ document.addEventListener("DOMContentLoaded", () => {
theme: {
stylesheetUrls: ["/stylesheets/style.css"],
},
// ...optional settings
},
modalSettings: {
// optional settings
},
searchSettings: {
// optional settings
placeholder: "Search",
},
aiChatSettings: {
chatSubjectName: "Ultralytics",
@ -146,101 +134,206 @@ document.addEventListener("DOMContentLoaded", () => {
});
};
inkeepScript.addEventListener("load", () => {
addInkeepWidget(); // initialize the widget
const widgetContainer = document.getElementById("inkeepSearchBar");
addInkeepWidget("ChatButton");
widgetContainer && addInkeepWidget("SearchBar", "#inkeepSearchBar");
});
});
// This object contains the benchmark data for various object detection models.
// YOLO models chart ---------------------------------------------------------------------------------------------------
const data = {
'YOLOv5': {s: {speed: 1.92, mAP: 37.4}, m: {speed: 4.03, mAP: 45.4}, l: {speed: 6.61, mAP: 49.0}, x: {speed: 11.89, mAP: 50.7}},
'YOLOv6': {n: {speed: 1.17, mAP: 37.5}, s: {speed: 2.66, mAP: 45.0}, m: {speed: 5.28, mAP: 50.0}, l: {speed: 8.95, mAP: 52.8}},
'YOLOv7': {l: {speed: 6.84, mAP: 51.4}, x: {speed: 11.57, mAP: 53.1}},
'YOLOv8': {n: {speed: 1.47, mAP: 37.3}, s: {speed: 2.66, mAP: 44.9}, m: {speed: 5.86, mAP: 50.2}, l: {speed: 9.06, mAP: 52.9}, x: {speed: 14.37, mAP: 53.9}},
'YOLOv9': {t: {speed: 2.30, mAP: 37.8}, s: {speed: 3.54, mAP: 46.5}, m: {speed: 6.43, mAP: 51.5}, c: {speed: 7.16, mAP: 52.8}, e: {speed: 16.77, mAP: 55.1}},
'YOLOv10': {n: {speed: 1.56, mAP: 39.5}, s: {speed: 2.66, mAP: 46.7}, m: {speed: 5.48, mAP: 51.3}, b: {speed: 6.54, mAP: 52.7}, l: {speed: 8.33, mAP: 53.3}, x: {speed: 12.2, mAP: 54.4}},
'PPYOLOE': {t: {speed: 2.84, mAP: 39.9}, s: {speed: 2.62, mAP: 43.7}, m: {speed: 5.56, mAP: 49.8}, l: {speed: 8.36, mAP: 52.9}, x: {speed: 14.3, mAP: 54.7}},
'YOLO11': {n: {speed: 1.55, mAP: 39.5}, s: {speed: 2.63, mAP: 47.0}, m: {speed: 5.27, mAP: 51.4}, l: {speed: 6.84, mAP: 53.2}, x: {speed: 12.49, mAP: 54.7}}
YOLO11: {
n: { speed: 1.55, mAP: 39.5 },
s: { speed: 2.63, mAP: 47.0 },
m: { speed: 5.27, mAP: 51.4 },
l: { speed: 6.84, mAP: 53.2 },
x: { speed: 12.49, mAP: 54.7 },
},
YOLOv10: {
n: { speed: 1.56, mAP: 39.5 },
s: { speed: 2.66, mAP: 46.7 },
m: { speed: 5.48, mAP: 51.3 },
b: { speed: 6.54, mAP: 52.7 },
l: { speed: 8.33, mAP: 53.3 },
x: { speed: 12.2, mAP: 54.4 },
},
YOLOv9: {
t: { speed: 2.3, mAP: 37.8 },
s: { speed: 3.54, mAP: 46.5 },
m: { speed: 6.43, mAP: 51.5 },
c: { speed: 7.16, mAP: 52.8 },
e: { speed: 16.77, mAP: 55.1 },
},
YOLOv8: {
n: { speed: 1.47, mAP: 37.3 },
s: { speed: 2.66, mAP: 44.9 },
m: { speed: 5.86, mAP: 50.2 },
l: { speed: 9.06, mAP: 52.9 },
x: { speed: 14.37, mAP: 53.9 },
},
YOLOv7: { l: { speed: 6.84, mAP: 51.4 }, x: { speed: 11.57, mAP: 53.1 } },
"YOLOv6-3.0": {
n: { speed: 1.17, mAP: 37.5 },
s: { speed: 2.66, mAP: 45.0 },
m: { speed: 5.28, mAP: 50.0 },
l: { speed: 8.95, mAP: 52.8 },
},
YOLOv5: {
s: { speed: 1.92, mAP: 37.4 },
m: { speed: 4.03, mAP: 45.4 },
l: { speed: 6.61, mAP: 49.0 },
x: { speed: 11.89, mAP: 50.7 },
},
"PP-YOLOE+": {
t: { speed: 2.84, mAP: 39.9 },
s: { speed: 2.62, mAP: 43.7 },
m: { speed: 5.56, mAP: 49.8 },
l: { speed: 8.36, mAP: 52.9 },
x: { speed: 14.3, mAP: 54.7 },
},
"DAMO-YOLO": {
t: { speed: 2.32, mAP: 42.0 },
s: { speed: 3.45, mAP: 46.0 },
m: { speed: 5.09, mAP: 49.2 },
l: { speed: 7.18, mAP: 50.8 },
},
YOLOX: {
s: { speed: 2.56, mAP: 40.5 },
m: { speed: 5.43, mAP: 46.9 },
l: { speed: 9.04, mAP: 49.7 },
x: { speed: 16.1, mAP: 51.1 },
},
RTDETRv2: {
s: { speed: 5.03, mAP: 48.1 },
m: { speed: 7.51, mAP: 51.9 },
l: { speed: 9.76, mAP: 53.4 },
x: { speed: 15.03, mAP: 54.3 },
},
};
let chart = null; // chart variable will hold the reference to the current chart instance.
let chart = null; // chart variable will hold the reference to the current chart instance.
// Function to lighten a hex color by a specified amount.
function lightenHexColor(color, amount = 0.5) {
const r = parseInt(color.slice(1, 3), 16);
const g = parseInt(color.slice(3, 5), 16);
const b = parseInt(color.slice(5, 7), 16);
const newR = Math.min(255, Math.round(r + (255 - r) * amount));
const newG = Math.min(255, Math.round(g + (255 - g) * amount));
const newB = Math.min(255, Math.round(b + (255 - b) * amount));
return `#${newR.toString(16).padStart(2, "0")}${newG.toString(16).padStart(2, "0")}${newB.toString(16).padStart(2, "0")}`;
}
// This function is responsible for updating the benchmarks chart.
// Function to update the benchmarks chart.
function updateChart() {
// If a chart instance already exists, destroy it.
if (chart) {
chart.destroy();
}
if (chart) {
chart.destroy();
} // If a chart instance already exists, destroy it.
// Get the selected algorithms from the checkboxes.
const selectedAlgorithms = [...document.querySelectorAll('input[name="algorithm"]:checked')].map(e => e.value);
// Create the datasets for the selected algorithms.
const datasets = selectedAlgorithms.map((algorithm, index) => ({
label: algorithm, // Label for the data points in the legend.
data: Object.entries(data[algorithm]).map(([version, point]) => ({
x: point.speed, // Speed data points on the x-axis.
y: point.mAP, // mAP data points on the y-axis.
version: version.toUpperCase() // Store the version as additional data.
})),
fill: false, // Don't fill the chart.
borderColor: `hsl(${index * 90}, 70%, 50%)`, // Assign a unique color to each dataset.
tension: 0.3, // Smooth the line.
pointRadius: 5, // Increase the dot size.
pointHoverRadius: 10, // Increase the dot size on hover.
borderWidth: 2 // Set the line thickness.
}));
// If there are no selected algorithms, return without creating a new chart.
if (datasets.length === 0) {
return;
}
// Define a specific color map for models.
const colorMap = {
YOLO11: "#0b23a9",
YOLOv10: "#ff7f0e",
YOLOv9: "#2ca02c",
YOLOv8: "#d62728",
YOLOv7: "#9467bd",
"YOLOv6-3.0": "#8c564b",
YOLOv5: "#e377c2",
"PP-YOLOE+": "#7f7f7f",
"DAMO-YOLO": "#bcbd22",
YOLOX: "#17becf",
RTDETRv2: "#eccd22",
};
// Get the selected algorithms from the checkboxes.
const selectedAlgorithms = [
...document.querySelectorAll('input[name="algorithm"]:checked'),
].map((e) => e.value);
// Create a new chart instance.
chart = new Chart(document.getElementById('chart').getContext('2d'), {
type: 'line', // Set the chart type to line.
data: { datasets },
options: {
plugins: {
legend: { display: true, position: 'top', labels: {color: '#808080'} }, // Configure the legend.
tooltip: {
callbacks: {
label: (tooltipItem) => {
const { dataset, dataIndex } = tooltipItem;
const point = dataset.data[dataIndex];
return `${dataset.label}${point.version.toLowerCase()}: Speed = ${point.x}, mAP = ${point.y}`; // Custom tooltip label.
}
},
mode: 'nearest',
intersect: false
} // Configure the tooltip.
// Create the datasets for the selected algorithms.
const datasets = selectedAlgorithms.map((algorithm, i) => {
const baseColor =
colorMap[algorithm] || `hsl(${Math.random() * 360}, 70%, 50%)`;
const lineColor = i === 0 ? baseColor : lightenHexColor(baseColor, 0.6); // Lighten non-primary lines.
return {
label: algorithm, // Label for the data points in the legend.
data: Object.entries(data[algorithm]).map(([version, point]) => ({
x: point.speed, // Speed data points on the x-axis.
y: point.mAP, // mAP data points on the y-axis.
version: version.toUpperCase(), // Store the version as additional data.
})),
fill: false, // Don't fill the chart.
borderColor: lineColor, // Use the lightened color for the line.
tension: 0.3, // Smooth the line.
pointRadius: i === 0 ? 7 : 4, // Highlight primary dataset points.
pointHoverRadius: i === 0 ? 9 : 6, // Highlight hover for primary dataset.
pointBackgroundColor: lineColor, // Fill points with the line color.
pointBorderColor: "#ffffff", // Add a border around points for contrast.
borderWidth: i === 0 ? 3 : 1.5, // Slightly increase line size for the primary dataset.
};
});
if (datasets.length === 0) {
return;
} // If there are no selected algorithms, return without creating a new chart.
// Create a new chart instance.
chart = new Chart(document.getElementById("chart").getContext("2d"), {
type: "line", // Set the chart type to line.
data: { datasets },
options: {
plugins: {
legend: {
display: true,
position: "top",
labels: { color: "#808080" },
}, // Configure the legend.
tooltip: {
callbacks: {
label: (tooltipItem) => {
const { dataset, dataIndex } = tooltipItem;
const point = dataset.data[dataIndex];
return `${dataset.label}${point.version.toLowerCase()}: Speed = ${point.x}, mAP = ${point.y}`; // Custom tooltip label.
},
interaction: { mode: 'nearest', axis: 'x', intersect: false }, // Configure the interaction mode.
scales: {
x: {
type: 'linear', position: 'bottom',
title: { display: true, text: 'Latency T4 TensorRT10 FP16 (ms/img)', color: '#808080'}, // X-axis title.
grid: { color: '#e0e0e0' }, // Grid line color.
ticks: { color: '#808080' } // Tick label color.
},
y: {
title: { display: true, text: 'mAP', color: '#808080'}, // Y-axis title.
grid: { color: '#e0e0e0' }, // Grid line color.
ticks: { color: '#808080' } // Tick label color.
}
}
}
});
},
mode: "nearest",
intersect: false,
}, // Configure the tooltip.
},
interaction: { mode: "nearest", axis: "x", intersect: false }, // Configure the interaction mode.
scales: {
x: {
type: "linear",
position: "bottom",
title: {
display: true,
text: "Latency T4 TensorRT10 FP16 (ms/img)",
color: "#808080",
}, // X-axis title.
grid: { color: "#e0e0e0" }, // Grid line color.
ticks: { color: "#808080" }, // Tick label color.
},
y: {
title: { display: true, text: "mAP", color: "#808080" }, // Y-axis title.
grid: { color: "#e0e0e0" }, // Grid line color.
ticks: { color: "#808080" }, // Tick label color.
},
},
},
});
}
// Poll for Chart.js to load, then initialize checkboxes and chart
function initializeApp() {
if (typeof Chart !== 'undefined') {
document.querySelectorAll('input[name="algorithm"]').forEach(checkbox =>
checkbox.addEventListener('change', updateChart)
);
updateChart();
} else {
setTimeout(initializeApp, 100); // Retry every 100ms
}
if (typeof Chart !== "undefined") {
document
.querySelectorAll('input[name="algorithm"]')
.forEach((checkbox) => checkbox.addEventListener("change", updateChart));
updateChart();
} else {
setTimeout(initializeApp, 100); // Retry every 100ms
}
}
document.addEventListener("DOMContentLoaded", initializeApp); // Initial chart rendering on page load

@ -57,14 +57,17 @@ function setupGiscusLoader() {
const giscusContainer = document.getElementById("giscus-container");
if (giscusContainer) {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
loadGiscus();
observer.unobserve(entry.target);
}
});
}, { threshold: 0.1 }); // Trigger when 10% of the element is visible
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
loadGiscus();
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.1 },
); // Trigger when 10% of the element is visible
observer.observe(giscusContainer);
}

@ -265,8 +265,15 @@ div.highlight {
}
/* MkDocs Ultralytics Plugin ---------------------------------------------------------------------------------------- */
/* Inkeep button font color ----------------------------------------------------------------------------------------- */
/* Inkeep ----------------------------------------------------------------------------------------------------------- */
.ikp-floating-button {
color: #111f68;
}
/* Inkeep button ---------------------------------------------------------------------------------------------------- */
#inkeepSearchBar {
transition: all 0.2s ease-in-out;
}
#inkeepSearchBar:hover {
transform: scale(1.1);
filter: brightness(1.2);
}
/* Inkeep ----------------------------------------------------------------------------------------------------------- */

@ -628,8 +628,8 @@ nav:
# Plugins including 301 redirects navigation ---------------------------------------------------------------------------
plugins:
- macros
- search:
lang: en
# - search:
# lang: en
- mkdocstrings:
enabled: true
default_handler: python

@ -1,6 +1,6 @@
# Ultralytics YOLO 🚀, AGPL-3.0 license
__version__ = "8.3.32"
__version__ = "8.3.34"
import os

@ -2280,7 +2280,7 @@ def v8_transforms(dataset, imgsz, hyp, stretch=False):
Args:
dataset (Dataset): The dataset object containing image data and annotations.
imgsz (int): The target image size for resizing.
hyp (Dict): A dictionary of hyperparameters controlling various aspects of the transformations.
hyp (Namespace): A dictionary of hyperparameters controlling various aspects of the transformations.
stretch (bool): If True, applies stretching to the image. If False, uses LetterBox resizing.
Returns:
@ -2288,8 +2288,9 @@ def v8_transforms(dataset, imgsz, hyp, stretch=False):
Examples:
>>> from ultralytics.data.dataset import YOLODataset
>>> from ultralytics.utils import IterableSimpleNamespace
>>> dataset = YOLODataset(img_path="path/to/images", imgsz=640)
>>> hyp = {"mosaic": 1.0, "copy_paste": 0.5, "degrees": 10.0, "translate": 0.2, "scale": 0.9}
>>> hyp = IterableSimpleNamespace(mosaic=1.0, copy_paste=0.5, degrees=10.0, translate=0.2, scale=0.9)
>>> transforms = v8_transforms(dataset, imgsz=640, hyp=hyp)
>>> augmented_data = transforms(dataset[0])
"""

@ -79,7 +79,6 @@ from ultralytics.utils import (
ARM64,
DEFAULT_CFG,
IS_JETSON,
IS_RASPBERRYPI,
LINUX,
LOGGER,
MACOS,
@ -265,8 +264,6 @@ class Exporter:
"WARNING ⚠ INT8 export requires a missing 'data' arg for calibration. "
f"Using default 'data={self.args.data}'."
)
if mnn and (IS_RASPBERRYPI or IS_JETSON):
raise SystemError("MNN export not supported on Raspberry Pi and NVIDIA Jetson")
# Input
im = torch.zeros(self.args.batch, 3, *self.imgsz).to(self.device)

@ -64,6 +64,9 @@ class FastSAMPredictor(SegmentationPredictor):
if not isinstance(results, list):
results = [results]
for result in results:
if len(result) == 0:
prompt_results.append(result)
continue
masks = result.masks.data
if masks.shape[1:] != result.orig_shape:
masks = scale_masks(masks[None], result.orig_shape)[0]

@ -68,8 +68,11 @@ class RTDETRTrainer(DetectionTrainer):
hyp=self.args,
rect=False,
cache=self.args.cache or None,
single_cls=self.args.single_cls or False,
prefix=colorstr(f"{mode}: "),
classes=self.args.classes,
data=self.data,
fraction=self.args.fraction if mode == "train" else 1.0,
)
def get_validator(self):

@ -104,12 +104,12 @@ class Heatmap(ObjectCounter):
self.annotator.draw_region(reg_pts=self.region, color=(104, 0, 123), thickness=self.line_width * 2)
self.store_tracking_history(track_id, box) # Store track history
self.store_classwise_counts(cls) # store classwise counts in dict
current_centroid = ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2)
# Store tracking previous position and perform object counting
prev_position = None
if len(self.track_history[track_id]) > 1:
prev_position = self.track_history[track_id][-2]
self.count_objects(self.track_line, box, track_id, prev_position, cls) # Perform object counting
self.count_objects(current_centroid, track_id, prev_position, cls) # Perform object counting
if self.region is not None:
self.display_counts(im0) # Display the counts on the frame

@ -46,13 +46,12 @@ class ObjectCounter(BaseSolution):
self.show_in = self.CFG["show_in"]
self.show_out = self.CFG["show_out"]
def count_objects(self, track_line, box, track_id, prev_position, cls):
def count_objects(self, current_centroid, track_id, prev_position, cls):
"""
Counts objects within a polygonal or linear region based on their tracks.
Args:
track_line (Dict): Last 30 frame track record for the object.
box (List[float]): Bounding box coordinates [x1, y1, x2, y2] for the specific track in the current frame.
current_centroid (Tuple[float, float]): Current centroid values in the current frame.
track_id (int): Unique identifier for the tracked object.
prev_position (Tuple[float, float]): Last frame position coordinates (x, y) of the track.
cls (int): Class index for classwise count updates.
@ -64,34 +63,55 @@ class ObjectCounter(BaseSolution):
>>> track_id = 1
>>> prev_position = (120, 220)
>>> cls = 0
>>> counter.count_objects(track_line, box, track_id, prev_position, cls)
>>> counter.count_objects(current_centroid, track_id, prev_position, cls)
"""
if prev_position is None or track_id in self.counted_ids:
return
centroid = self.r_s.centroid
dx = (box[0] - prev_position[0]) * (centroid.x - prev_position[0])
dy = (box[1] - prev_position[1]) * (centroid.y - prev_position[1])
if len(self.region) >= 3 and self.r_s.contains(self.Point(track_line[-1])):
self.counted_ids.append(track_id)
# For polygon region
if dx > 0:
self.in_count += 1
self.classwise_counts[self.names[cls]]["IN"] += 1
else:
self.out_count += 1
self.classwise_counts[self.names[cls]]["OUT"] += 1
elif len(self.region) < 3 and self.LineString([prev_position, box[:2]]).intersects(self.r_s):
self.counted_ids.append(track_id)
# For linear region
if dx > 0 and dy > 0:
self.in_count += 1
self.classwise_counts[self.names[cls]]["IN"] += 1
else:
self.out_count += 1
self.classwise_counts[self.names[cls]]["OUT"] += 1
if len(self.region) == 2: # Linear region (defined as a line segment)
line = self.LineString(self.region) # Check if the line intersects the trajectory of the object
if line.intersects(self.LineString([prev_position, current_centroid])):
# Determine orientation of the region (vertical or horizontal)
if abs(self.region[0][0] - self.region[1][0]) < abs(self.region[0][1] - self.region[1][1]):
# Vertical region: Compare x-coordinates to determine direction
if current_centroid[0] > prev_position[0]: # Moving right
self.in_count += 1
self.classwise_counts[self.names[cls]]["IN"] += 1
else: # Moving left
self.out_count += 1
self.classwise_counts[self.names[cls]]["OUT"] += 1
else:
# Horizontal region: Compare y-coordinates to determine direction
if current_centroid[1] > prev_position[1]: # Moving downward
self.in_count += 1
self.classwise_counts[self.names[cls]]["IN"] += 1
else: # Moving upward
self.out_count += 1
self.classwise_counts[self.names[cls]]["OUT"] += 1
self.counted_ids.append(track_id)
elif len(self.region) > 2: # Polygonal region
polygon = self.Polygon(self.region)
if polygon.contains(self.Point(current_centroid)):
# Determine motion direction for vertical or horizontal polygons
region_width = max([p[0] for p in self.region]) - min([p[0] for p in self.region])
region_height = max([p[1] for p in self.region]) - min([p[1] for p in self.region])
if region_width < region_height: # Vertical-oriented polygon
if current_centroid[0] > prev_position[0]: # Moving right
self.in_count += 1
self.classwise_counts[self.names[cls]]["IN"] += 1
else: # Moving left
self.out_count += 1
self.classwise_counts[self.names[cls]]["OUT"] += 1
else: # Horizontal-oriented polygon
if current_centroid[1] > prev_position[1]: # Moving downward
self.in_count += 1
self.classwise_counts[self.names[cls]]["IN"] += 1
else: # Moving upward
self.out_count += 1
self.classwise_counts[self.names[cls]]["OUT"] += 1
self.counted_ids.append(track_id)
def store_classwise_counts(self, cls):
"""
@ -174,12 +194,12 @@ class ObjectCounter(BaseSolution):
self.annotator.draw_centroid_and_tracks(
self.track_line, color=colors(int(cls), True), track_thickness=self.line_width
)
current_centroid = ((box[0] + box[2]) / 2, (box[1] + box[3]) / 2)
# store previous position of track for object counting
prev_position = None
if len(self.track_history[track_id]) > 1:
prev_position = self.track_history[track_id][-2]
self.count_objects(self.track_line, box, track_id, prev_position, cls) # Perform object counting
self.count_objects(current_centroid, track_id, prev_position, cls) # Perform object counting
self.display_counts(im0) # Display the counts on the frame
self.display_output(im0) # display output with base class function

@ -114,8 +114,6 @@ def benchmark(
assert LINUX or MACOS, "Windows Paddle exports not supported yet"
if i == 12: # MNN
assert not isinstance(model, YOLOWorld), "YOLOWorldv2 MNN exports not supported yet"
assert not IS_RASPBERRYPI, "MNN export not supported on Raspberry Pi"
assert not IS_JETSON, "MNN export not supported on NVIDIA Jetson"
if i == 13: # NCNN
assert not isinstance(model, YOLOWorld), "YOLOWorldv2 NCNN exports not supported yet"
if i == 14: # IMX

@ -109,7 +109,12 @@ def _log_plots(plots, step):
def on_pretrain_routine_start(trainer):
"""Initiate and start project if module is present."""
wb.run or wb.init(project=trainer.args.project or "Ultralytics", name=trainer.args.name, config=vars(trainer.args))
if not wb.run:
wb.init(
project=str(trainer.args.project).replace("/", "-") if trainer.args.project else "Ultralytics",
name=str(trainer.args.name).replace("/", "-"),
config=vars(trainer.args),
)
def on_fit_epoch_end(trainer):

Loading…
Cancel
Save