增加构建脚本

This commit is contained in:
somunslotus 2025-05-21 16:26:48 +08:00
parent f9b3c2397b
commit 609c58e8e7
882 changed files with 145755 additions and 51 deletions

View File

@ -1,53 +1,6 @@
## 第一阶段:构建阶段
#FROM python:3.11-slim-bullseye AS builder
#
## 配置APT清华源
#RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list \
# && sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list
#
## 安装基础系统依赖
#RUN apt-get update && apt-get install -y \
# build-essential \
# libssl-dev \
# zlib1g-dev \
# libffi-dev \
# libgl1 \
# ffmpeg \
# && rm -rf /var/lib/apt/lists/*
#
## 创建虚拟环境
#RUN python -m venv /opt/venv
#ENV PATH="/opt/venv/bin:$PATH"
FROM 172.20.32.187/machine-learning/video:v1
LABEL authors="zch"
COPY app.py cache /app/
FROM python_3_11:hnxjy
# 配置清华源加速
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \
&& pip config set install.trusted-host pypi.tuna.tsinghua.edu.cn
# 安装Python依赖利用分层缓存
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 安装最小化运行时依赖
RUN apt-get update && apt-get install -y \
libgl1 \
ffmpeg \
&& rm -rf /var/lib/apt/lists/*
# 设置容器环境
COPY . .
# 配置Python优化参数
ENV PYTHONUNBUFFERED=1 \
PYTHONPATH=/app \
LANG=C.UTF-8
# 暴露应用端口
EXPOSE 7860
# 启动命令
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "1888"]

53
Dockerfile.old Normal file
View File

@ -0,0 +1,53 @@
## 第一阶段:构建阶段
#FROM python:3.11-slim-bullseye AS builder
#
## 配置APT清华源
#RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list \
# && sed -i 's/security.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list
#
## 安装基础系统依赖
#RUN apt-get update && apt-get install -y \
# build-essential \
# libssl-dev \
# zlib1g-dev \
# libffi-dev \
# libgl1 \
# ffmpeg \
# && rm -rf /var/lib/apt/lists/*
#
## 创建虚拟环境
#RUN python -m venv /opt/venv
#ENV PATH="/opt/venv/bin:$PATH"
FROM python_3_11:hnxjy
# 配置清华源加速
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple \
&& pip config set install.trusted-host pypi.tuna.tsinghua.edu.cn
# 安装Python依赖利用分层缓存
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# 安装最小化运行时依赖
RUN apt-get update && apt-get install -y \
libgl1 \
ffmpeg \
&& rm -rf /var/lib/apt/lists/*
# 设置容器环境
COPY . .
# 配置Python优化参数
ENV PYTHONUNBUFFERED=1 \
PYTHONPATH=/app \
LANG=C.UTF-8
# 暴露应用端口
EXPOSE 7860
# 启动命令
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]

8
build-image.sh Executable file
View File

@ -0,0 +1,8 @@
#!/bin/bash
tag=$(date +%Y%m%d%H%M%S)
image=172.20.32.187/machine-learning/video:${tag}
docker build -t ${image} .
docker push $image

Binary file not shown.

View File

@ -0,0 +1,92 @@
---
AccessModifierOffset: -1
AlignAfterOpenBracket: AlwaysBreak
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: false
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: true
AlwaysBreakTemplateDeclarations: true
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterClass: false
AfterControlStatement: false
AfterEnum: false
AfterFunction: false
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: false
AfterUnion: false
BeforeCatch: false
BeforeElse: false
IndentBraces: false
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: false
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
#CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: true
ConstructorInitializerIndentWidth: 4
ContinuationIndentWidth: 4
Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ForEachMacros: [ FOR_EACH_RANGE, FOR_EACH, ]
IncludeCategories:
- Regex: '^<.*\.h(pp)?>'
Priority: 1
- Regex: '^<.*'
Priority: 2
- Regex: '.*'
Priority: 3
IndentCaseLabels: true
IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PenaltyBreakBeforeFirstCallParameter: 1
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 2000000
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
TabWidth: 8
UseTab: Never
---
Language: ObjC
ColumnLimit: 120
AlignAfterOpenBracket: Align
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: false
...

View File

@ -0,0 +1,15 @@
# This file keeps git blame clean.
# See https://docs.github.com/en/repositories/working-with-files/using-files/viewing-a-file#ignore-commits-in-the-blame-view
# Add ufmt (usort + black) as code formatter (#4384)
5f0edb97b46e5bff71dc19dedef05c5396eeaea2
# update python syntax >=3.6 (#4585)
d367a01a18a3ae6bee13d8be3b63fd6a581ea46f
# Upgrade usort to 1.0.2 and black to 22.3.0 (#5106)
6ca9c76adb6daf2695d603ad623a9cf1c4f4806f
# Fix unnecessary exploded black formatting (#7709)
a335d916db0694770e8152f41e19195de3134523
# Renaming: `BoundingBox` -> `BoundingBoxes` (#7778)
332bff937c6711666191880fab57fa2f23ae772e
# Upgrade type hint and others to Python 3.9 (#8814)
a095de183d3811d79ed0db2715e7a1c3162fa19d

View File

@ -0,0 +1,8 @@
*.pkl binary
# Jupyter notebook
# For text count
# *.ipynb text
# To ignore it use below
*.ipynb linguist-documentation

View File

@ -0,0 +1,60 @@
name: 🐛 Bug Report
description: Create a report to help us reproduce and fix the bug
body:
- type: markdown
attributes:
value: >
#### Before submitting a bug, please make sure the issue hasn't been already addressed by searching through [the existing and past issues](https://github.com/pytorch/vision/issues?q=is%3Aissue+sort%3Acreated-desc+).
- type: textarea
attributes:
label: 🐛 Describe the bug
description: |
Please provide a clear and concise description of what the bug is.
If relevant, add a minimal example so that we can reproduce the error by running the code. It is very important for the snippet to be as succinct (minimal) as possible, so please take time to trim down any irrelevant code to help us debug efficiently. We are going to copy-paste your code and we expect to get the same result as you did: avoid any external data, and include the relevant imports, etc. For example:
```python
# All necessary imports at the beginning
import torch
import torchvision
from torchvision.ops import nms
# A succinct reproducing example trimmed down to the essential parts:
N = 5
boxes = torch.rand(N, 4) # Note: the bug is here, we should enforce that x1 < x2 and y1 < y2!
scores = torch.rand(N)
nms(boxes, scores, iou_threshold=.9)
```
If the code is too long (hopefully, it isn't), feel free to put it in a public gist and link it in the issue: https://gist.github.com.
Please also paste or describe the results you observe instead of the expected results. If you observe an error, please paste the error message including the **full** traceback of the exception. It may be relevant to wrap error messages in ```` ```triple quotes blocks``` ````.
placeholder: |
A clear and concise description of what the bug is.
```python
Sample code to reproduce the problem
```
```
The error message you got, with the full traceback.
````
validations:
required: true
- type: textarea
attributes:
label: Versions
description: |
Please run the following and paste the output below.
```sh
wget https://raw.githubusercontent.com/pytorch/pytorch/main/torch/utils/collect_env.py
# For security purposes, please check the contents of collect_env.py before running it.
python collect_env.py
```
validations:
required: true
- type: markdown
attributes:
value: >
Thanks for contributing 🎉!

View File

@ -0,0 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Usage questions
url: https://discuss.pytorch.org/
about: Ask questions and discuss with other torchvision community members

View File

@ -0,0 +1,20 @@
name: 📚 Documentation
description: Report an issue related to https://pytorch.org/vision/stable/index.html
body:
- type: textarea
attributes:
label: 📚 The doc issue
description: >
A clear and concise description of what content in https://pytorch.org/vision/stable/index.html is an issue. If this has to do with the general https://pytorch.org website, please file an issue at https://github.com/pytorch/pytorch.github.io/issues/new/choose instead. If this has to do with https://pytorch.org/tutorials, please file an issue at https://github.com/pytorch/tutorials/issues/new.
validations:
required: true
- type: textarea
attributes:
label: Suggest a potential alternative/fix
description: >
Tell us how we could improve the documentation in this regard.
- type: markdown
attributes:
value: >
Thanks for contributing 🎉!

View File

@ -0,0 +1,32 @@
name: 🚀 Feature request
description: Submit a proposal/request for a new torchvision feature
body:
- type: textarea
attributes:
label: 🚀 The feature
description: >
A clear and concise description of the feature proposal
validations:
required: true
- type: textarea
attributes:
label: Motivation, pitch
description: >
Please outline the motivation for the proposal. Is your feature request related to a specific problem? e.g., *"I'm working on X and would like Y to be possible"*. If this is related to another GitHub issue, please link here too.
validations:
required: true
- type: textarea
attributes:
label: Alternatives
description: >
A description of any alternative solutions or features you've considered, if any.
- type: textarea
attributes:
label: Additional context
description: >
Add any other context or screenshots about the feature request.
- type: markdown
attributes:
value: >
Thanks for contributing 🎉!

View File

@ -0,0 +1 @@
<!-- Before submitting a PR, please make sure to check our contributing guidelines regarding code formatting, tests, and documentation: https://github.com/pytorch/vision/blob/main/CONTRIBUTING.md -->

View File

@ -0,0 +1,13 @@
---
title: Scheduled workflow failed
labels:
- bug
- "module: datasets"
---
Oh no, something went wrong in the scheduled workflow {{ env.WORKFLOW }}/{{ env.JOB }}.
Please look into it:
https://github.com/{{ env.REPO }}/actions/runs/{{ env.ID }}
Feel free to close this if this was just a one-off error.

View File

@ -0,0 +1,81 @@
"""
This script finds the merger responsible for labeling a PR by a commit SHA. It is used by the workflow in
'.github/workflows/pr-labels.yml'. If there exists no PR associated with the commit or the PR is properly labeled,
this script is a no-op.
Note: we ping the merger only, not the reviewers, as the reviewers can sometimes be external to torchvision
with no labeling responsibility, so we don't want to bother them.
"""
import sys
from typing import Any, Optional, Set, Tuple
import requests
# For a PR to be properly labeled it should have one primary label and one secondary label
PRIMARY_LABELS = {
"new feature",
"bug",
"code quality",
"enhancement",
"bc-breaking",
"deprecation",
"other",
"prototype",
}
SECONDARY_LABELS = {
"dependency issue",
"module: c++ frontend",
"module: ci",
"module: datasets",
"module: documentation",
"module: io",
"module: models.quantization",
"module: models",
"module: onnx",
"module: ops",
"module: reference scripts",
"module: rocm",
"module: tests",
"module: transforms",
"module: utils",
"module: video",
"Perf",
"Revert(ed)",
"topic: build",
}
def query_torchvision(cmd: str, *, accept) -> Any:
response = requests.get(f"https://api.github.com/repos/pytorch/vision/{cmd}", headers=dict(Accept=accept))
return response.json()
def get_pr_number(commit_hash: str) -> Optional[int]:
# See https://docs.github.com/en/rest/reference/repos#list-pull-requests-associated-with-a-commit
data = query_torchvision(f"commits/{commit_hash}/pulls", accept="application/vnd.github.groot-preview+json")
if not data:
return None
return data[0]["number"]
def get_pr_merger_and_labels(pr_number: int) -> Tuple[str, Set[str]]:
# See https://docs.github.com/en/rest/reference/pulls#get-a-pull-request
data = query_torchvision(f"pulls/{pr_number}", accept="application/vnd.github.v3+json")
merger = data["merged_by"]["login"]
labels = {label["name"] for label in data["labels"]}
return merger, labels
if __name__ == "__main__":
commit_hash = sys.argv[1]
pr_number = get_pr_number(commit_hash)
if not pr_number:
sys.exit(0)
merger, labels = get_pr_merger_and_labels(pr_number)
is_properly_labeled = bool(PRIMARY_LABELS.intersection(labels) and SECONDARY_LABELS.intersection(labels))
if not is_properly_labeled:
print(f"@{merger}")

View File

@ -0,0 +1,10 @@
tracking_issue: 2447
# List of workflows that will be re-run in case of failures
# https://github.com/pytorch/test-infra/blob/main/torchci/lib/bot/retryBot.ts
retryable_workflows:
- Build Linux
- Build Macos
- Build M1
- Build Windows
- Tests

View File

@ -0,0 +1,107 @@
#!/usr/bin/env bash
set -euxo pipefail
./.github/scripts/setup-env.sh
# Activate conda environment
set +x && eval "$($(which conda) shell.bash hook)" && conda deactivate && conda activate ci && set -x
# Setup the OS_TYPE environment variable that should be used for conditions involving the OS below.
case $(uname) in
Linux)
OS_TYPE=linux
;;
Darwin)
OS_TYPE=macos
;;
MSYS*)
OS_TYPE=windows
;;
*)
echo "Unknown OS type:" $(uname)
exit 1
;;
esac
if [[ $OS_TYPE == macos ]]; then
JOBS=$(sysctl -n hw.logicalcpu)
else
JOBS=$(nproc)
fi
if [[ $OS_TYPE == linux ]]; then
export LD_LIBRARY_PATH="${CONDA_PREFIX}/lib:${LD_LIBRARY_PATH}"
fi
TORCH_PATH=$(python -c "import pathlib, torch; print(pathlib.Path(torch.__path__[0]))")
if [[ $OS_TYPE == windows ]]; then
PACKAGING_DIR="${PWD}/packaging"
export PATH="${TORCH_PATH}/lib:${PATH}"
fi
Torch_DIR="${TORCH_PATH}/share/cmake/Torch"
if [[ "${GPU_ARCH_TYPE}" == "cuda" ]]; then
WITH_CUDA=1
else
WITH_CUDA=0
fi
echo '::group::Prepare CMake builds'
mkdir -p cpp_build
pushd examples/cpp
python script_model.py
mkdir -p build
mv resnet18.pt fasterrcnn_resnet50_fpn.pt build
popd
# This was only needed for the tracing above
pip uninstall -y torchvision
echo '::endgroup::'
echo '::group::Build and install libtorchvision'
pushd cpp_build
# On macOS, CMake is looking for the library (*.dylib) and the header (*.h) separately. By default, it prefers to load
# the header from other packages that install the library. This easily leads to a mismatch if the library installed
# from conda doesn't have the exact same version. Thus, we need to explicitly set CMAKE_FIND_FRAMEWORK=NEVER to force
# it to not load anything from other installed frameworks. Resources:
# https://stackoverflow.com/questions/36523911/osx-homebrew-cmake-libpng-version-mismatch-issue
# https://cmake.org/cmake/help/latest/variable/CMAKE_FIND_FRAMEWORK.html
cmake .. -DTorch_DIR="${Torch_DIR}" -DWITH_CUDA="${WITH_CUDA}" \
-DCMAKE_PREFIX_PATH="${CONDA_PREFIX}" \
-DCMAKE_FIND_FRAMEWORK=NEVER \
-DCMAKE_INSTALL_PREFIX="${CONDA_PREFIX}"
if [[ $OS_TYPE == windows ]]; then
"${PACKAGING_DIR}/windows/internal/vc_env_helper.bat" "${PACKAGING_DIR}/windows/internal/build_cmake.bat" $JOBS
else
make -j$JOBS
make install
fi
popd
echo '::endgroup::'
echo '::group::Build and run C++ example'
pushd examples/cpp/build
cmake .. -DTorch_DIR="${Torch_DIR}" \
-DCMAKE_PREFIX_PATH="${CONDA_PREFIX}" \
-DCMAKE_FIND_FRAMEWORK=NEVER \
-DUSE_TORCHVISION=ON # Needed for faster-rcnn since it's using torchvision ops like NMS.
if [[ $OS_TYPE == windows ]]; then
"${PACKAGING_DIR}/windows/internal/vc_env_helper.bat" "${PACKAGING_DIR}/windows/internal/build_cpp_example.bat" $JOBS
cd Release
cp ../resnet18.pt .
cp ../fasterrcnn_resnet50_fpn.pt .
else
make -j$JOBS
fi
./run_model resnet18.pt
./run_model fasterrcnn_resnet50_fpn.pt
popd
echo '::endgroup::'

View File

@ -0,0 +1,3 @@
#!/bin/sh
export IS_M1_CONDA_BUILD_JOB=1

View File

@ -0,0 +1,331 @@
#!/usr/bin/env python
"""
MIT License
Copyright (c) 2017 Guillaume Papin
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
A wrapper script around clang-format, suitable for linting multiple files
and to use for continuous integration.
This is an alternative API for the clang-format command line.
It runs over multiple files and directories in parallel.
A diff output is produced and a sensible exit code is returned.
"""
import argparse
import difflib
import fnmatch
import multiprocessing
import os
import signal
import subprocess
import sys
import traceback
from functools import partial
try:
from subprocess import DEVNULL # py3k
except ImportError:
DEVNULL = open(os.devnull, "wb")
DEFAULT_EXTENSIONS = "c,h,C,H,cpp,hpp,cc,hh,c++,h++,cxx,hxx,cu,mm"
class ExitStatus:
SUCCESS = 0
DIFF = 1
TROUBLE = 2
def list_files(files, recursive=False, extensions=None, exclude=None):
if extensions is None:
extensions = []
if exclude is None:
exclude = []
out = []
for file in files:
if recursive and os.path.isdir(file):
for dirpath, dnames, fnames in os.walk(file):
fpaths = [os.path.join(dirpath, fname) for fname in fnames]
for pattern in exclude:
# os.walk() supports trimming down the dnames list
# by modifying it in-place,
# to avoid unnecessary directory listings.
dnames[:] = [x for x in dnames if not fnmatch.fnmatch(os.path.join(dirpath, x), pattern)]
fpaths = [x for x in fpaths if not fnmatch.fnmatch(x, pattern)]
for f in fpaths:
ext = os.path.splitext(f)[1][1:]
if ext in extensions:
out.append(f)
else:
out.append(file)
return out
def make_diff(file, original, reformatted):
return list(
difflib.unified_diff(
original, reformatted, fromfile=f"{file}\t(original)", tofile=f"{file}\t(reformatted)", n=3
)
)
class DiffError(Exception):
def __init__(self, message, errs=None):
super().__init__(message)
self.errs = errs or []
class UnexpectedError(Exception):
def __init__(self, message, exc=None):
super().__init__(message)
self.formatted_traceback = traceback.format_exc()
self.exc = exc
def run_clang_format_diff_wrapper(args, file):
try:
ret = run_clang_format_diff(args, file)
return ret
except DiffError:
raise
except Exception as e:
raise UnexpectedError(f"{file}: {e.__class__.__name__}: {e}", e)
def run_clang_format_diff(args, file):
try:
with open(file, encoding="utf-8") as f:
original = f.readlines()
except OSError as exc:
raise DiffError(str(exc))
invocation = [args.clang_format_executable, file]
# Use of utf-8 to decode the process output.
#
# Hopefully, this is the correct thing to do.
#
# It's done due to the following assumptions (which may be incorrect):
# - clang-format will returns the bytes read from the files as-is,
# without conversion, and it is already assumed that the files use utf-8.
# - if the diagnostics were internationalized, they would use utf-8:
# > Adding Translations to Clang
# >
# > Not possible yet!
# > Diagnostic strings should be written in UTF-8,
# > the client can translate to the relevant code page if needed.
# > Each translation completely replaces the format string
# > for the diagnostic.
# > -- http://clang.llvm.org/docs/InternalsManual.html#internals-diag-translation
try:
proc = subprocess.Popen(
invocation, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, encoding="utf-8"
)
except OSError as exc:
raise DiffError(f"Command '{subprocess.list2cmdline(invocation)}' failed to start: {exc}")
proc_stdout = proc.stdout
proc_stderr = proc.stderr
# hopefully the stderr pipe won't get full and block the process
outs = list(proc_stdout.readlines())
errs = list(proc_stderr.readlines())
proc.wait()
if proc.returncode:
raise DiffError(
"Command '{}' returned non-zero exit status {}".format(
subprocess.list2cmdline(invocation), proc.returncode
),
errs,
)
return make_diff(file, original, outs), errs
def bold_red(s):
return "\x1b[1m\x1b[31m" + s + "\x1b[0m"
def colorize(diff_lines):
def bold(s):
return "\x1b[1m" + s + "\x1b[0m"
def cyan(s):
return "\x1b[36m" + s + "\x1b[0m"
def green(s):
return "\x1b[32m" + s + "\x1b[0m"
def red(s):
return "\x1b[31m" + s + "\x1b[0m"
for line in diff_lines:
if line[:4] in ["--- ", "+++ "]:
yield bold(line)
elif line.startswith("@@ "):
yield cyan(line)
elif line.startswith("+"):
yield green(line)
elif line.startswith("-"):
yield red(line)
else:
yield line
def print_diff(diff_lines, use_color):
if use_color:
diff_lines = colorize(diff_lines)
sys.stdout.writelines(diff_lines)
def print_trouble(prog, message, use_colors):
error_text = "error:"
if use_colors:
error_text = bold_red(error_text)
print(f"{prog}: {error_text} {message}", file=sys.stderr)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"--clang-format-executable",
metavar="EXECUTABLE",
help="path to the clang-format executable",
default="clang-format",
)
parser.add_argument(
"--extensions",
help=f"comma separated list of file extensions (default: {DEFAULT_EXTENSIONS})",
default=DEFAULT_EXTENSIONS,
)
parser.add_argument("-r", "--recursive", action="store_true", help="run recursively over directories")
parser.add_argument("files", metavar="file", nargs="+")
parser.add_argument("-q", "--quiet", action="store_true")
parser.add_argument(
"-j",
metavar="N",
type=int,
default=0,
help="run N clang-format jobs in parallel (default number of cpus + 1)",
)
parser.add_argument(
"--color", default="auto", choices=["auto", "always", "never"], help="show colored diff (default: auto)"
)
parser.add_argument(
"-e",
"--exclude",
metavar="PATTERN",
action="append",
default=[],
help="exclude paths matching the given glob-like pattern(s) from recursive search",
)
args = parser.parse_args()
# use default signal handling, like diff return SIGINT value on ^C
# https://bugs.python.org/issue14229#msg156446
signal.signal(signal.SIGINT, signal.SIG_DFL)
try:
signal.SIGPIPE
except AttributeError:
# compatibility, SIGPIPE does not exist on Windows
pass
else:
signal.signal(signal.SIGPIPE, signal.SIG_DFL)
colored_stdout = False
colored_stderr = False
if args.color == "always":
colored_stdout = True
colored_stderr = True
elif args.color == "auto":
colored_stdout = sys.stdout.isatty()
colored_stderr = sys.stderr.isatty()
version_invocation = [args.clang_format_executable, "--version"]
try:
subprocess.check_call(version_invocation, stdout=DEVNULL)
except subprocess.CalledProcessError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
return ExitStatus.TROUBLE
except OSError as e:
print_trouble(
parser.prog,
f"Command '{subprocess.list2cmdline(version_invocation)}' failed to start: {e}",
use_colors=colored_stderr,
)
return ExitStatus.TROUBLE
retcode = ExitStatus.SUCCESS
files = list_files(
args.files, recursive=args.recursive, exclude=args.exclude, extensions=args.extensions.split(",")
)
if not files:
return
njobs = args.j
if njobs == 0:
njobs = multiprocessing.cpu_count() + 1
njobs = min(len(files), njobs)
if njobs == 1:
# execute directly instead of in a pool,
# less overhead, simpler stacktraces
it = (run_clang_format_diff_wrapper(args, file) for file in files)
pool = None
else:
pool = multiprocessing.Pool(njobs)
it = pool.imap_unordered(partial(run_clang_format_diff_wrapper, args), files)
while True:
try:
outs, errs = next(it)
except StopIteration:
break
except DiffError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
retcode = ExitStatus.TROUBLE
sys.stderr.writelines(e.errs)
except UnexpectedError as e:
print_trouble(parser.prog, str(e), use_colors=colored_stderr)
sys.stderr.write(e.formatted_traceback)
retcode = ExitStatus.TROUBLE
# stop at the first unexpected error,
# something could be very wrong,
# don't process all files unnecessarily
if pool:
pool.terminate()
break
else:
sys.stderr.writelines(errs)
if outs == []:
continue
if not args.quiet:
print_diff(outs, use_color=colored_stdout)
if retcode == ExitStatus.SUCCESS:
retcode = ExitStatus.DIFF
return retcode
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,115 @@
#!/usr/bin/env bash
set -euxo pipefail
# Prepare conda
set +x && eval "$($(which conda) shell.bash hook)" && set -x
# Setup the OS_TYPE environment variable that should be used for conditions involving the OS below.
case $(uname) in
Linux)
OS_TYPE=linux
;;
Darwin)
OS_TYPE=macos
;;
MSYS*)
OS_TYPE=windows
;;
*)
echo "Unknown OS type:" $(uname)
exit 1
;;
esac
echo '::group::Create build environment'
# See https://github.com/pytorch/vision/issues/7296 for ffmpeg
conda create \
--name ci \
--quiet --yes \
python="${PYTHON_VERSION}" pip \
ninja cmake \
libpng \
libwebp \
'ffmpeg<4.3'
conda activate ci
conda install --quiet --yes libjpeg-turbo -c pytorch
pip install --progress-bar=off --upgrade setuptools==72.1.0
# See https://github.com/pytorch/vision/issues/6790
if [[ "${PYTHON_VERSION}" != "3.11" ]]; then
pip install --progress-bar=off av!=10.0.0
fi
echo '::endgroup::'
if [[ "${OS_TYPE}" == windows && "${GPU_ARCH_TYPE}" == cuda ]]; then
echo '::group::Install VisualStudio CUDA extensions on Windows'
TARGET_DIR="/c/Program Files (x86)/Microsoft Visual Studio/2022/BuildTools/MSBuild/Microsoft/VC/v170/BuildCustomizations"
mkdir -p "${TARGET_DIR}"
cp -r "${CUDA_HOME}/MSBuildExtensions/"* "${TARGET_DIR}"
echo '::endgroup::'
fi
echo '::group::Install PyTorch'
# TODO: Can we maybe have this as environment variable in the job template? For example, `IS_RELEASE`.
if [[ (${GITHUB_EVENT_NAME} = 'pull_request' && (${GITHUB_BASE_REF} = 'release'*)) || (${GITHUB_REF} = 'refs/heads/release'*) ]]; then
CHANNEL=test
else
CHANNEL=nightly
fi
case $GPU_ARCH_TYPE in
cpu)
GPU_ARCH_ID="cpu"
;;
cuda)
VERSION_WITHOUT_DOT=$(echo "${GPU_ARCH_VERSION}" | sed 's/\.//')
GPU_ARCH_ID="cu${VERSION_WITHOUT_DOT}"
;;
*)
echo "Unknown GPU_ARCH_TYPE=${GPU_ARCH_TYPE}"
exit 1
;;
esac
PYTORCH_WHEEL_INDEX="https://download.pytorch.org/whl/${CHANNEL}/${GPU_ARCH_ID}"
pip install --progress-bar=off --pre torch --index-url="${PYTORCH_WHEEL_INDEX}"
if [[ $GPU_ARCH_TYPE == 'cuda' ]]; then
python -c "import torch; exit(not torch.cuda.is_available())"
fi
echo '::endgroup::'
echo '::group::Install third party dependencies prior to TorchVision install'
# Installing with `easy_install`, e.g. `python setup.py install` or `python setup.py develop`, has some quirks when
# when pulling in third-party dependencies. For example:
# - On Windows, we often hit an SSL error although `pip` can install just fine.
# - It happily pulls in pre-releases, which can lead to more problems down the line.
# `pip` does not unless explicitly told to do so.
# Thus, we use `easy_install` to extract the third-party dependencies here and install them upfront with `pip`.
python setup.py egg_info
# The requires.txt cannot be used with `pip install -r` directly. The requirements are listed at the top and the
# optional dependencies come in non-standard syntax after a blank line. Thus, we just extract the header.
sed -e '/^$/,$d' *.egg-info/requires.txt | tee requirements.txt
pip install --progress-bar=off -r requirements.txt
echo '::endgroup::'
echo '::group::Install TorchVision'
python setup.py develop
echo '::endgroup::'
echo '::group::Install torchvision-extra-decoders'
# This can be done after torchvision was built
if [[ "$(uname)" == "Linux" && "$(uname -m)" != "aarch64" ]]; then
extra_decoders_channel="--pre --index-url https://download.pytorch.org/whl/nightly/cpu"
else
extra_decoders_channel=""
fi
pip install torchvision-extra-decoders $extra_decoders_channel
echo '::endgroup::'
echo '::group::Collect environment information'
conda list
python -m torch.utils.collect_env
echo '::endgroup::'

View File

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
./.github/scripts/setup-env.sh
# Activate conda environment
eval "$($(which conda) shell.bash hook)" && conda deactivate && conda activate ci
echo '::group::Install testing utilities'
# TODO: remove the <8 constraint on pytest when https://github.com/pytorch/vision/issues/8238 is closed
pip install --progress-bar=off "pytest<8" pytest-mock pytest-cov expecttest!=0.2.0 requests
echo '::endgroup::'
python test/smoke_test.py
# We explicitly ignore the video tests until we resolve https://github.com/pytorch/vision/issues/8162
pytest --ignore-glob="*test_video*" --ignore-glob="*test_onnx*" --junit-xml="${RUNNER_TEST_RESULTS_DIR}/test-results.xml" -v --durations=25 -k "not TestFxFeatureExtraction"

View File

@ -0,0 +1,87 @@
name: CMake
on:
pull_request:
push:
branches:
- nightly
- main
- release/*
workflow_dispatch:
jobs:
linux:
strategy:
matrix:
include:
- runner: linux.12xlarge
gpu-arch-type: cpu
- runner: linux.g5.4xlarge.nvidia.gpu
gpu-arch-type: cuda
gpu-arch-version: "11.8"
fail-fast: false
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: read
with:
repository: pytorch/vision
runner: ${{ matrix.runner }}
gpu-arch-type: ${{ matrix.gpu-arch-type }}
gpu-arch-version: ${{ matrix.gpu-arch-version }}
test-infra-ref: main
script: |
set -euo pipefail
export PYTHON_VERSION=3.9
export GPU_ARCH_TYPE=${{ matrix.gpu-arch-type }}
export GPU_ARCH_VERSION=${{ matrix.gpu-arch-version }}
./.github/scripts/cmake.sh
macos:
strategy:
matrix:
include:
- runner: macos-m1-stable
fail-fast: false
uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
with:
repository: pytorch/vision
runner: ${{ matrix.runner }}
test-infra-ref: main
script: |
set -euo pipefail
export PYTHON_VERSION=3.9
export GPU_ARCH_TYPE=cpu
export GPU_ARCH_VERSION=''
${CONDA_RUN} ./.github/scripts/cmake.sh
windows:
strategy:
matrix:
include:
- runner: windows.4xlarge
gpu-arch-type: cpu
- runner: windows.g5.4xlarge.nvidia.gpu
gpu-arch-type: cuda
gpu-arch-version: "11.8"
fail-fast: false
uses: pytorch/test-infra/.github/workflows/windows_job.yml@main
with:
repository: pytorch/vision
runner: ${{ matrix.runner }}
gpu-arch-type: ${{ matrix.gpu-arch-type }}
gpu-arch-version: ${{ matrix.gpu-arch-version }}
test-infra-ref: main
script: |
set -euo pipefail
export PYTHON_VERSION=3.9
export VC_YEAR=2022
export VSDEVCMD_ARGS=""
export GPU_ARCH_TYPE=${{ matrix.gpu-arch-type }}
export GPU_ARCH_VERSION=${{ matrix.gpu-arch-version }}
./.github/scripts/cmake.sh

View File

@ -0,0 +1,54 @@
name: Build Aarch64 Linux Wheels
on:
pull_request:
push:
branches:
- nightly
- main
- release/*
tags:
# NOTE: Binary build pipelines should only get triggered on release candidate builds
# Release candidate tags look like: v1.11.0-rc1
- v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
generate-matrix:
uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main
with:
package-type: wheel
os: linux-aarch64
test-infra-repository: pytorch/test-infra
test-infra-ref: main
with-cuda: disable
build:
needs: generate-matrix
strategy:
fail-fast: false
matrix:
include:
- repository: pytorch/vision
pre-script: packaging/pre_build_script.sh
post-script: packaging/post_build_script.sh
smoke-test-script: test/smoke_test.py
package-name: torchvision
name: ${{ matrix.repository }}
uses: pytorch/test-infra/.github/workflows/build_wheels_linux.yml@main
with:
repository: ${{ matrix.repository }}
ref: ""
test-infra-repository: pytorch/test-infra
test-infra-ref: main
build-matrix: ${{ needs.generate-matrix.outputs.matrix }}
pre-script: ${{ matrix.pre-script }}
post-script: ${{ matrix.post-script }}
package-name: ${{ matrix.package-name }}
smoke-test-script: ${{ matrix.smoke-test-script }}
trigger-event: ${{ github.event_name }}
architecture: aarch64
setup-miniconda: false

View File

@ -0,0 +1,52 @@
name: Build Linux Wheels
on:
pull_request:
push:
branches:
- nightly
- main
- release/*
tags:
# NOTE: Binary build pipelines should only get triggered on release candidate builds
# Release candidate tags look like: v1.11.0-rc1
- v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
generate-matrix:
uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main
with:
package-type: wheel
os: linux
test-infra-repository: pytorch/test-infra
test-infra-ref: main
with-xpu: enable
build:
needs: generate-matrix
strategy:
fail-fast: false
matrix:
include:
- repository: pytorch/vision
pre-script: packaging/pre_build_script.sh
post-script: packaging/post_build_script.sh
smoke-test-script: test/smoke_test.py
package-name: torchvision
name: ${{ matrix.repository }}
uses: pytorch/test-infra/.github/workflows/build_wheels_linux.yml@main
with:
repository: ${{ matrix.repository }}
ref: ""
test-infra-repository: pytorch/test-infra
test-infra-ref: main
build-matrix: ${{ needs.generate-matrix.outputs.matrix }}
pre-script: ${{ matrix.pre-script }}
post-script: ${{ matrix.post-script }}
package-name: ${{ matrix.package-name }}
smoke-test-script: ${{ matrix.smoke-test-script }}
trigger-event: ${{ github.event_name }}

View File

@ -0,0 +1,52 @@
name: Build M1 Wheels
on:
pull_request:
push:
branches:
- nightly
- main
- release/*
tags:
# NOTE: Binary build pipelines should only get triggered on release candidate builds
# Release candidate tags look like: v1.11.0-rc1
- v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
generate-matrix:
uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main
with:
package-type: wheel
os: macos-arm64
test-infra-repository: pytorch/test-infra
test-infra-ref: main
build:
needs: generate-matrix
strategy:
fail-fast: false
matrix:
include:
- repository: pytorch/vision
pre-script: packaging/pre_build_script.sh
post-script: packaging/post_build_script.sh
smoke-test-script: test/smoke_test.py
package-name: torchvision
name: ${{ matrix.repository }}
uses: pytorch/test-infra/.github/workflows/build_wheels_macos.yml@main
with:
repository: ${{ matrix.repository }}
ref: ""
test-infra-repository: pytorch/test-infra
test-infra-ref: main
build-matrix: ${{ needs.generate-matrix.outputs.matrix }}
pre-script: ${{ matrix.pre-script }}
post-script: ${{ matrix.post-script }}
package-name: ${{ matrix.package-name }}
runner-type: macos-m1-stable
smoke-test-script: ${{ matrix.smoke-test-script }}
trigger-event: ${{ github.event_name }}

View File

@ -0,0 +1,54 @@
name: Build Windows Wheels
on:
pull_request:
push:
branches:
- nightly
- main
- release/*
tags:
# NOTE: Binary build pipelines should only get triggered on release candidate builds
# Release candidate tags look like: v1.11.0-rc1
- v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+
workflow_dispatch:
permissions:
id-token: write
contents: read
jobs:
generate-matrix:
uses: pytorch/test-infra/.github/workflows/generate_binary_build_matrix.yml@main
with:
package-type: wheel
os: windows
test-infra-repository: pytorch/test-infra
test-infra-ref: main
with-xpu: enable
build:
needs: generate-matrix
strategy:
fail-fast: false
matrix:
include:
- repository: pytorch/vision
pre-script: packaging/pre_build_script.sh
env-script: packaging/windows/internal/vc_env_helper.bat
post-script: "python packaging/wheel/relocate.py"
smoke-test-script: test/smoke_test.py
package-name: torchvision
name: ${{ matrix.repository }}
uses: pytorch/test-infra/.github/workflows/build_wheels_windows.yml@main
with:
repository: ${{ matrix.repository }}
ref: ""
test-infra-repository: pytorch/test-infra
test-infra-ref: main
build-matrix: ${{ needs.generate-matrix.outputs.matrix }}
pre-script: ${{ matrix.pre-script }}
env-script: ${{ matrix.env-script }}
post-script: ${{ matrix.post-script }}
package-name: ${{ matrix.package-name }}
smoke-test-script: ${{ matrix.smoke-test-script }}
trigger-event: ${{ github.event_name }}

View File

@ -0,0 +1,133 @@
name: Docs
on:
pull_request:
push:
branches:
- nightly
- main
- release/*
tags:
- v[0-9]+.[0-9]+.[0-9]
- v[0-9]+.[0-9]+.[0-9]+-rc[0-9]+
workflow_dispatch:
jobs:
build:
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: read
with:
repository: pytorch/vision
upload-artifact: docs
test-infra-ref: main
script: |
set -euo pipefail
export PYTHON_VERSION=3.10
export GPU_ARCH_TYPE=cpu
export GPU_ARCH_VERSION=''
./.github/scripts/setup-env.sh
# Prepare conda
CONDA_PATH=$(which conda)
eval "$(${CONDA_PATH} shell.bash hook)"
conda activate ci
# FIXME: not sure why we need this. `ldd torchvision/video_reader.so` shows that it
# already links against the one pulled from conda. However, at runtime it pulls from
# /lib64
# Should we maybe always do this in `./.github/scripts/setup-env.sh` so that we don't
# have to pay attention in all other workflows?
export LD_LIBRARY_PATH="${CONDA_PREFIX}/lib:${LD_LIBRARY_PATH}"
cd docs
echo '::group::Install doc requirements'
pip install --progress-bar=off -r requirements.txt
echo '::endgroup::'
if [[ ${{ github.event_name }} == push && (${{ github.ref_type }} == tag || (${{ github.ref_type }} == branch && ${{ github.ref_name }} == release/*)) ]]; then
echo '::group::Enable version string sanitization'
# This environment variable just has to exist and must not be empty. The actual value is arbitrary.
# See docs/source/conf.py for details
export TORCHVISION_SANITIZE_VERSION_STR_IN_DOCS=1
echo '::endgroup::'
fi
# The runner does not have sufficient memory to run with as many processes as there are
# cores (`-j auto`). Thus, we limit to a single process (`-j 1`) here.
sed -i -e 's/-j auto/-j 1/' Makefile
make html
# Below is an imperfect way for us to add "try on Colab" links to all of our gallery examples.
# sphinx-gallery will convert all gallery examples to .ipynb notebooks and stores them in
# build/html/_downloads/<some_hash>/<example_name>.ipynb
# We copy all those ipynb files in a more convenient folder so that we can more easily link to them.
mkdir build/html/_generated_ipynb_notebooks
for file in `find build/html/_downloads`; do
if [[ $file == *.ipynb ]]; then
cp $file build/html/_generated_ipynb_notebooks/
fi
done
cp -r build/html "${RUNNER_ARTIFACT_DIR}"
# On PRs we also want to upload the docs into our S3 bucket for preview.
if [[ ${{ github.event_name == 'pull_request' }} ]]; then
cp -r build/html/* "${RUNNER_DOCS_DIR}"
fi
upload:
needs: build
if: github.repository == 'pytorch/vision' && github.event_name == 'push' &&
((github.ref_type == 'branch' && github.ref_name == 'main') || github.ref_type == 'tag')
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: write
with:
repository: pytorch/vision
download-artifact: docs
ref: gh-pages
test-infra-ref: main
script: |
set -euo pipefail
REF_TYPE=${{ github.ref_type }}
REF_NAME=${{ github.ref_name }}
if [[ "${REF_TYPE}" == branch ]]; then
TARGET_FOLDER="${REF_NAME}"
elif [[ "${REF_TYPE}" == tag ]]; then
case "${REF_NAME}" in
*-rc*)
echo "Aborting upload since this is an RC tag: ${REF_NAME}"
exit 0
;;
*)
# Strip the leading "v" as well as the trailing patch version. For example:
# 'v0.15.2' -> '0.15'
TARGET_FOLDER=$(echo "${REF_NAME}" | sed 's/v\([0-9]\+\)\.\([0-9]\+\)\.[0-9]\+/\1.\2/')
;;
esac
fi
echo "Target Folder: ${TARGET_FOLDER}"
mkdir -p "${TARGET_FOLDER}"
rm -rf "${TARGET_FOLDER}"/*
mv "${RUNNER_ARTIFACT_DIR}"/html/* "${TARGET_FOLDER}"
git add "${TARGET_FOLDER}" || true
if [[ "${TARGET_FOLDER}" == main ]]; then
mkdir -p _static
rm -rf _static/*
cp -r "${TARGET_FOLDER}"/_static/* _static
git add _static || true
fi
git config user.name 'pytorchbot'
git config user.email 'soumith+bot@pytorch.org'
git config http.postBuffer 524288000
git commit -m "auto-generating sphinx docs" || true
git push

View File

@ -0,0 +1,111 @@
name: Lint
on:
pull_request:
push:
branches:
- nightly
- main
- release/*
workflow_dispatch:
jobs:
python-source-and-configs:
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: read
with:
repository: pytorch/vision
test-infra-ref: main
script: |
set -euo pipefail
echo '::group::Setup environment'
CONDA_PATH=$(which conda)
eval "$(${CONDA_PATH} shell.bash hook)"
conda create --name ci --quiet --yes python=3.9 pip
conda activate ci
echo '::endgroup::'
echo '::group::Install lint tools'
pip install --progress-bar=off pre-commit
echo '::endgroup::'
set +e
pre-commit run --all-files
if [ $? -ne 0 ]; then
git --no-pager diff
exit 1
fi
# c-source:
# uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
# permissions:
# id-token: write
# contents: read
# with:
# repository: pytorch/vision
# test-infra-ref: main
# script: |
# set -euo pipefail
# echo '::group::Setup environment'
# CONDA_PATH=$(which conda)
# eval "$(${CONDA_PATH} shell.bash hook)"
# conda create --name ci --quiet --yes -c conda-forge python=3.9 clang-format
# conda activate ci
# echo '::endgroup::'
# echo '::group::Lint C source'
# set +e
# ./.github/scripts/run-clang-format.py -r torchvision/csrc --exclude "torchvision/csrc/io/image/cpu/giflib/*"
# if [ $? -ne 0 ]; then
# git --no-pager diff
# exit 1
# fi
# echo '::endgroup::'
python-types:
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: read
with:
repository: pytorch/vision
test-infra-ref: main
script: |
set -euo pipefail
export PYTHON_VERSION=3.11
export GPU_ARCH_TYPE=cpu
export GPU_ARCH_VERSION=''
./.github/scripts/setup-env.sh
CONDA_PATH=$(which conda)
eval "$(${CONDA_PATH} shell.bash hook)"
conda activate ci
echo '::group::Install lint tools'
pip install --progress-bar=off "mypy==1.13.0"
echo '::endgroup::'
echo '::group::Lint Python types'
mypy --install-types --non-interactive --config-file mypy.ini
echo '::endgroup::'
# bc:
# if: github.event.pull_request
# runs-on: ubuntu-latest
# steps:
# - name: Run BC Lint Action
# uses: pytorch/test-infra/.github/actions/bc-lint@main
# with:
# repo: ${{ github.event.pull_request.head.repo.full_name }}
# base_sha: ${{ github.event.pull_request.base.sha }}
# head_sha: ${{ github.event.pull_request.head.sha }}

View File

@ -0,0 +1,40 @@
name: pr-labels
on:
push:
branches:
- main
jobs:
is-properly-labeled:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Set up python
uses: actions/setup-python@v5
- name: Install requests
run: pip install requests
- name: Checkout repository
uses: actions/checkout@v4
- name: Process commit and find merger responsible for labeling
id: commit
run: |
MERGER=$(python .github/process_commit.py ${{ github.sha }})
echo "merger=${MERGER}" | tee --append $GITHUB_OUTPUT
- name: Ping merger responsible for labeling if necessary
if: ${{ steps.commit.outputs.merger != '' }}
uses: mshick/add-pr-comment@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
message: |
Hey ${{ steps.commit.outputs.merger }}!
You merged this PR, but no labels were added.
The list of valid labels is available at https://github.com/pytorch/vision/blob/main/.github/process_commit.py

View File

@ -0,0 +1,60 @@
name: Prototype tests on Linux
# IMPORTANT: This workflow has been manually disabled from the GitHub interface
# in June 2024. The file is kept for reference in case we ever put this back.
on:
pull_request:
jobs:
unittests-prototype:
strategy:
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
runner: ["linux.12xlarge"]
gpu-arch-type: ["cpu"]
include:
- python-version: "3.9"
runner: linux.g5.4xlarge.nvidia.gpu
gpu-arch-type: cuda
gpu-arch-version: "11.8"
fail-fast: false
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: read
with:
repository: pytorch/vision
runner: ${{ matrix.runner }}
gpu-arch-type: ${{ matrix.gpu-arch-type }}
gpu-arch-version: ${{ matrix.gpu-arch-version }}
timeout: 120
script: |
set -euo pipefail
export PYTHON_VERSION=${{ matrix.python-version }}
export GPU_ARCH_TYPE=${{ matrix.gpu-arch-type }}
export GPU_ARCH_VERSION=${{ matrix.gpu-arch-version }}
./.github/scripts/setup-env.sh
# Prepare conda
CONDA_PATH=$(which conda)
eval "$(${CONDA_PATH} shell.bash hook)"
conda activate ci
echo '::group::Install testing utilities'
pip install --progress-bar=off pytest pytest-mock pytest-cov
echo '::endgroup::'
# We don't want to run the prototype datasets tests. Since the positional glob into `pytest`, i.e.
# `test/test_prototype*.py` takes the highest priority, neither `--ignore` nor `--ignore-glob` can help us here.
rm test/test_prototype_datasets*.py
pytest \
-v --durations=25 \
--cov=torchvision/prototype --cov-report=term-missing \
--junit-xml="${RUNNER_TEST_RESULTS_DIR}/test-results.xml" \
test/test_prototype_*.py

View File

@ -0,0 +1,60 @@
name: tests
on:
pull_request:
paths:
- "test/test_datasets_download.py"
- ".github/failed_schedule_issue_template.md"
- ".github/workflows/tests-schedule.yml"
schedule:
- cron: "0 9 * * *"
jobs:
download:
runs-on: ubuntu-latest
steps:
- name: Set up python
uses: actions/setup-python@v2
with:
python-version: 3.9
- name: Upgrade system packages
run: python -m pip install --upgrade pip setuptools wheel
- name: SSL
run: python -c 'import ssl; print(ssl.OPENSSL_VERSION)'
- name: Checkout repository
uses: actions/checkout@v2
- name: TODO REMOVE THIS! Install non pre-release version of mpmath.
run: pip install "mpmath<1.4"
- name: Install torch nightly build
run: pip install --pre torch -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html
- name: Install torchvision
run: pip install --no-build-isolation --editable .
- name: Install all optional dataset requirements
run: pip install scipy pycocotools lmdb gdown
- name: Install tests requirements
run: pip install pytest
- name: Run tests
run: pytest -ra -v test/test_datasets_download.py
- uses: JasonEtco/create-an-issue@v2.4.0
name: Create issue if download tests failed
if: failure() && github.event_name == 'schedule'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
WORKFLOW: ${{ github.workflow }}
JOB: ${{ github.job }}
ID: ${{ github.run_id }}
with:
filename: .github/failed_schedule_issue_template.md

View File

@ -0,0 +1,184 @@
name: Tests
on:
pull_request:
push:
branches:
- nightly
- main
- release/*
workflow_dispatch:
jobs:
unittests-linux:
strategy:
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
runner: ["linux.12xlarge"]
gpu-arch-type: ["cpu"]
include:
- python-version: 3.9
runner: linux.g5.4xlarge.nvidia.gpu
gpu-arch-type: cuda
gpu-arch-version: "11.8"
fail-fast: false
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: read
with:
repository: pytorch/vision
runner: ${{ matrix.runner }}
gpu-arch-type: ${{ matrix.gpu-arch-type }}
gpu-arch-version: ${{ matrix.gpu-arch-version }}
timeout: 120
test-infra-ref: main
script: |
set -euo pipefail
export PYTHON_VERSION=${{ matrix.python-version }}
export GPU_ARCH_TYPE=${{ matrix.gpu-arch-type }}
export GPU_ARCH_VERSION=${{ matrix.gpu-arch-version }}
./.github/scripts/unittest.sh
unittests-macos:
strategy:
matrix:
python-version:
- "3.9"
- "3.10"
# TODO put back 3.11 (See blame)
# - "3.11"
- "3.12"
runner: ["macos-m1-stable"]
fail-fast: false
uses: pytorch/test-infra/.github/workflows/macos_job.yml@main
with:
repository: pytorch/vision
timeout: 240
runner: ${{ matrix.runner }}
test-infra-ref: main
script: |
set -euo pipefail
export PYTHON_VERSION=${{ matrix.python-version }}
export GPU_ARCH_TYPE=cpu
export GPU_ARCH_VERSION=''
${CONDA_RUN} ./.github/scripts/unittest.sh
unittests-windows:
strategy:
matrix:
python-version:
- "3.9"
- "3.10"
- "3.11"
- "3.12"
runner: ["windows.4xlarge"]
gpu-arch-type: ["cpu"]
# TODO: put GPU testing back
# include:
# - python-version: "3.9"
# runner: windows.g5.4xlarge.nvidia.gpu
# gpu-arch-type: cuda
# gpu-arch-version: "11.8"
fail-fast: false
uses: pytorch/test-infra/.github/workflows/windows_job.yml@main
permissions:
id-token: write
contents: read
with:
repository: pytorch/vision
runner: ${{ matrix.runner }}
gpu-arch-type: ${{ matrix.gpu-arch-type }}
gpu-arch-version: ${{ matrix.gpu-arch-version }}
timeout: 120
test-infra-ref: main
script: |
set -euxo pipefail
export PYTHON_VERSION=${{ matrix.python-version }}
export VC_YEAR=2022
export VSDEVCMD_ARGS=""
export GPU_ARCH_TYPE=${{ matrix.gpu-arch-type }}
export GPU_ARCH_VERSION=${{ matrix.gpu-arch-version }}
./.github/scripts/unittest.sh
# onnx:
# uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
# permissions:
# id-token: write
# contents: read
# with:
# repository: pytorch/vision
# test-infra-ref: main
# script: |
# set -euo pipefail
# export PYTHON_VERSION=3.10
# export GPU_ARCH_TYPE=cpu
# export GPU_ARCH_VERSION=''
# ./.github/scripts/setup-env.sh
# # Prepare conda
# CONDA_PATH=$(which conda)
# eval "$(${CONDA_PATH} shell.bash hook)"
# conda activate ci
# echo '::group::Install ONNX'
# pip install --progress-bar=off onnx onnxruntime
# echo '::endgroup::'
# echo '::group::Install testing utilities'
# pip install --progress-bar=off pytest "numpy<2"
# echo '::endgroup::'
# echo '::group::Run ONNX tests'
# pytest --junit-xml="${RUNNER_TEST_RESULTS_DIR}/test-results.xml" -v --durations=25 test/test_onnx.py
# echo '::endgroup::'
unittests-extended:
uses: pytorch/test-infra/.github/workflows/linux_job_v2.yml@main
permissions:
id-token: write
contents: read
if: contains(github.event.pull_request.labels.*.name, 'run-extended')
with:
repository: pytorch/vision
test-infra-ref: main
script: |
set -euo pipefail
export PYTHON_VERSION=3.9
export GPU_ARCH_TYPE=cpu
export GPU_ARCH_VERSION=''
./.github/scripts/setup-env.sh
# Prepare conda
CONDA_PATH=$(which conda)
eval "$(${CONDA_PATH} shell.bash hook)"
conda activate ci
echo '::group::Pre-download model weights'
pip install --progress-bar=off aiohttp aiofiles tqdm
python scripts/download_model_urls.py
echo '::endgroup::'
echo '::group::Install testing utilities'
# TODO: remove the <8 constraint on pytest when https://github.com/pytorch/vision/issues/8238 is closed
pip install --progress-bar=off "pytest<8"
echo '::endgroup::'
echo '::group::Run extended unittests'
export PYTORCH_TEST_WITH_EXTENDED=1
pytest --junit-xml="${RUNNER_TEST_RESULTS_DIR}/test-results.xml" -v --durations=25 test/test_extended_*.py
echo '::endgroup::'

View File

@ -0,0 +1,47 @@
build/
dist/
torchvision.egg-info/
torchvision/version.py
*/**/__pycache__
*/__pycache__
*/*.pyc
*/**/*.pyc
*/**/**/*.pyc
*/**/*~
*~
docs/build
# sphinx-gallery
docs/source/auto_examples/
docs/source/gen_modules/
docs/source/generated/
docs/source/models/generated/
docs/source/sg_execution_times.rst
# pytorch-sphinx-theme gets installed here
docs/src
.coverage
htmlcov
.*.swp
*.so*
*.dylib*
*/*.so*
*/*.dylib*
*.swp
*.swo
gen.yml
.mypy_cache
.vscode/
.idea/
*.orig
*-checkpoint.ipynb
*.venv
## Xcode User settings
xcuserdata/
# direnv
.direnv
.envrc
scripts/release_notes/data.json

View File

@ -0,0 +1,32 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.0.1
hooks:
- id: check-docstring-first
- id: check-toml
- id: check-yaml
exclude: packaging/.*
args:
- --allow-multiple-documents
- id: mixed-line-ending
args: [--fix=lf]
- id: end-of-file-fixer
- repo: https://github.com/omnilib/ufmt
rev: v1.3.3
hooks:
- id: ufmt
additional_dependencies:
- black == 22.3.0
- usort == 1.0.2
- repo: https://github.com/PyCQA/flake8
rev: 5.0.4
hooks:
- id: flake8
args: [--config=setup.cfg]
- repo: https://github.com/PyCQA/pydocstyle
rev: 6.1.1
hooks:
- id: pydocstyle

View File

@ -0,0 +1,14 @@
cff-version: 1.2.0
title: "TorchVision: PyTorch's Computer Vision library"
message: >-
If you find TorchVision useful in your work, please
consider citing the following BibTeX entry.
type: software
authors:
- given-names: TorchVision maintainers and contributors
url: "https://github.com/pytorch/vision"
license: "BSD-3-Clause"
date-released: "2016-11-06"
journal: "GitHub repository"
publisher: "GitHub"
key: "torchvision2016"

View File

@ -0,0 +1,180 @@
cmake_minimum_required(VERSION 3.18)
project(torchvision)
set(CMAKE_CXX_STANDARD 17)
file(STRINGS version.txt TORCHVISION_VERSION)
option(WITH_CUDA "Enable CUDA support" OFF)
option(WITH_MPS "Enable MPS support" OFF)
option(WITH_PNG "Enable features requiring LibPNG." ON)
option(WITH_JPEG "Enable features requiring LibJPEG." ON)
# Libwebp is disabled by default, which means enabling it from cmake is largely
# untested. Since building from cmake is very low pri anyway, this is OK. If
# you're a user and you need this, please open an issue (and a PR!).
option(WITH_WEBP "Enable features requiring LibWEBP." OFF)
# Same here
option(WITH_AVIF "Enable features requiring LibAVIF." OFF)
if(WITH_CUDA)
enable_language(CUDA)
add_definitions(-D__CUDA_NO_HALF_OPERATORS__)
add_definitions(-DWITH_CUDA)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --expt-relaxed-constexpr")
endif()
if(WITH_MPS)
enable_language(OBJC OBJCXX)
add_definitions(-DWITH_MPS)
endif()
find_package(Torch REQUIRED)
if (WITH_PNG)
add_definitions(-DPNG_FOUND)
find_package(PNG REQUIRED)
endif()
if (WITH_JPEG)
add_definitions(-DJPEG_FOUND)
find_package(JPEG REQUIRED)
endif()
if (WITH_WEBP)
add_definitions(-DWEBP_FOUND)
find_package(WEBP REQUIRED)
endif()
if (WITH_AVIF)
add_definitions(-DAVIF_FOUND)
find_package(AVIF REQUIRED)
endif()
function(CUDA_CONVERT_FLAGS EXISTING_TARGET)
get_property(old_flags TARGET ${EXISTING_TARGET} PROPERTY INTERFACE_COMPILE_OPTIONS)
if(NOT "${old_flags}" STREQUAL "")
string(REPLACE ";" "," CUDA_flags "${old_flags}")
set_property(TARGET ${EXISTING_TARGET} PROPERTY INTERFACE_COMPILE_OPTIONS
"$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CXX>>:${old_flags}>$<$<BUILD_INTERFACE:$<COMPILE_LANGUAGE:CUDA>>:-Xcompiler=${CUDA_flags}>"
)
endif()
endfunction()
if(MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4819")
if(WITH_CUDA)
set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler=/wd4819")
foreach(diag cc_clobber_ignored integer_sign_change useless_using_declaration
set_but_not_used field_without_dll_interface
base_class_has_different_dll_interface
dll_interface_conflict_none_assumed
dll_interface_conflict_dllexport_assumed
implicit_return_from_non_void_function
unsigned_compare_with_zero
declared_but_not_referenced
bad_friend_decl)
string(APPEND CMAKE_CUDA_FLAGS " -Xcudafe --diag_suppress=${diag}")
endforeach()
CUDA_CONVERT_FLAGS(torch_cpu)
if(TARGET torch_cuda)
CUDA_CONVERT_FLAGS(torch_cuda)
endif()
if(TARGET torch_cuda_cu)
CUDA_CONVERT_FLAGS(torch_cuda_cu)
endif()
if(TARGET torch_cuda_cpp)
CUDA_CONVERT_FLAGS(torch_cuda_cpp)
endif()
endif()
endif()
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
set(TVCPP torchvision/csrc)
list(APPEND ALLOW_LISTED ${TVCPP} ${TVCPP}/io/image ${TVCPP}/io/image/cpu ${TVCPP}/io/image/cpu/giflib ${TVCPP}/models ${TVCPP}/ops
${TVCPP}/ops/autograd ${TVCPP}/ops/cpu ${TVCPP}/io/image/cuda)
if(WITH_CUDA)
list(APPEND ALLOW_LISTED ${TVCPP}/ops/cuda ${TVCPP}/ops/autocast)
endif()
if(WITH_MPS)
list(APPEND ALLOW_LISTED ${TVCPP}/ops/mps)
endif()
FOREACH(DIR ${ALLOW_LISTED})
file(GLOB ALL_SOURCES ${ALL_SOURCES} ${DIR}/*.*)
ENDFOREACH()
add_library(${PROJECT_NAME} SHARED ${ALL_SOURCES})
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES})
if(WITH_MPS)
find_library(metal NAMES Metal)
find_library(foundation NAMES Foundation)
target_link_libraries(${PROJECT_NAME} PRIVATE ${metal} ${foundation})
endif()
if (WITH_PNG)
target_link_libraries(${PROJECT_NAME} PRIVATE ${PNG_LIBRARY})
endif()
if (WITH_JPEG)
target_link_libraries(${PROJECT_NAME} PRIVATE ${JPEG_LIBRARIES})
endif()
if (WITH_WEBP)
target_link_libraries(${PROJECT_NAME} PRIVATE ${WEBP_LIBRARIES})
endif()
if (WITH_AVIF)
target_link_libraries(${PROJECT_NAME} PRIVATE ${AVIF_LIBRARIES})
endif()
set_target_properties(${PROJECT_NAME} PROPERTIES
EXPORT_NAME TorchVision
INSTALL_RPATH ${TORCH_INSTALL_PREFIX}/lib)
include_directories(torchvision/csrc)
if (WITH_PNG)
include_directories(${PNG_INCLUDE_DIRS})
endif()
if (WITH_JPEG)
include_directories(${JPEG_INCLUDE_DIRS})
endif()
if (WITH_WEBP)
include_directories(${WEBP_INCLUDE_DIRS})
endif()
if (WITH_AVIF)
include_directories(${AVIF_INCLUDE_DIRS})
endif()
set(TORCHVISION_CMAKECONFIG_INSTALL_DIR "share/cmake/TorchVision" CACHE STRING "install path for TorchVisionConfig.cmake")
configure_package_config_file(cmake/TorchVisionConfig.cmake.in
"${CMAKE_CURRENT_BINARY_DIR}/TorchVisionConfig.cmake"
INSTALL_DESTINATION ${TORCHVISION_CMAKECONFIG_INSTALL_DIR})
write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/TorchVisionConfigVersion.cmake
VERSION ${TORCHVISION_VERSION}
COMPATIBILITY AnyNewerVersion)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/TorchVisionConfig.cmake
${CMAKE_CURRENT_BINARY_DIR}/TorchVisionConfigVersion.cmake
DESTINATION ${TORCHVISION_CMAKECONFIG_INSTALL_DIR})
install(TARGETS ${PROJECT_NAME}
EXPORT TorchVisionTargets
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)
install(EXPORT TorchVisionTargets
NAMESPACE TorchVision::
DESTINATION ${TORCHVISION_CMAKECONFIG_INSTALL_DIR})
FOREACH(INPUT_DIR ${ALLOW_LISTED})
string(REPLACE "${TVCPP}" "${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}" OUTPUT_DIR ${INPUT_DIR})
file(GLOB INPUT_FILES ${INPUT_DIR}/*.*)
install(FILES ${INPUT_FILES} DESTINATION ${OUTPUT_DIR})
ENDFOREACH()

View File

@ -0,0 +1,76 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to make participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when
an individual is representing the project or its community in public spaces.
Examples of representing a project or community include using an official
project e-mail address, posting via an official social media account, or acting
as an appointed representative at an online or offline event. Representation of
a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at <conduct@pytorch.org>. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

View File

@ -0,0 +1,234 @@
# Contributing to Torchvision
We want to make contributing to this project as easy and transparent as possible.
## TL;DR
We appreciate all contributions. If you are interested in contributing to Torchvision, there are many ways to help out.
Your contributions may fall into the following categories:
- It helps the project if you could
- Report issues you're facing
- Give a :+1: on issues that others reported and that are relevant to you
- Answering queries on the issue tracker, investigating bugs are very valuable contributions to the project.
- You would like to improve the documentation. This is no less important than improving the library itself!
If you find a typo in the documentation, do not hesitate to submit a GitHub pull request.
- If you would like to fix a bug
- please pick one from the [list of open issues labelled as "help wanted"](https://github.com/pytorch/vision/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22)
- comment on the issue that you want to work on this issue
- send a PR with your fix, see below.
- If you plan to contribute new features, utility functions or extensions, please first open an issue and discuss the feature with us.
## Issues
We use GitHub issues to track public bugs. Please ensure your description is
clear and has sufficient instructions to be able to reproduce the issue.
## Development installation
### Dependencies
Start by installing the **nightly** build of PyTorch following the [official
instructions](https://pytorch.org/get-started/locally/). Note that the official
instructions may ask you to install torchvision itself. If you are doing development
on torchvision, you should not install prebuilt torchvision packages.
**Optionally**, install `libpng` and `libjpeg-turbo` if you want to enable
support for
native encoding / decoding of PNG and JPEG formats in
[torchvision.io](https://pytorch.org/vision/stable/io.html#image):
```bash
conda install libpng libjpeg-turbo -c pytorch
```
Note: you can use the `TORCHVISION_INCLUDE` and `TORCHVISION_LIBRARY`
environment variables to tell the build system where to find those libraries if
they are in specific locations. Take a look at
[setup.py](https://github.com/pytorch/vision/blob/main/setup.py) for more
details.
### Clone and install torchvision
```bash
git clone https://github.com/pytorch/vision.git
cd vision
python setup.py develop # use install instead of develop if you don't care about development.
# or, for OSX
# MACOSX_DEPLOYMENT_TARGET=10.9 CC=clang CXX=clang++ python setup.py develop
# for C++ debugging, use DEBUG=1
# DEBUG=1 python setup.py develop
```
By default, GPU support is built if CUDA is found and `torch.cuda.is_available()` is true. It's possible to force
building GPU support by setting `FORCE_CUDA=1` environment variable, which is useful when building a docker image.
We don't officially support building from source using `pip`, but _if_ you do, you'll need to use the
`--no-build-isolation` flag.
#### Other development dependencies (some of these are needed to run tests):
```
pip install expecttest flake8 typing mypy pytest pytest-mock scipy requests
```
## Development Process
If you plan to modify the code or documentation, please follow the steps below:
1. Fork the repository and create your branch from `main`.
2. If you have modified the code (new feature or bug-fix), please add unit tests.
3. If you have changed APIs, update the documentation. Make sure the documentation builds.
4. Ensure the test suite passes.
5. Make sure your code passes the formatting checks (see below).
For more details about pull requests,
please read [GitHub's guides](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request).
If you would like to contribute a new model, please see [here](#New-architecture-or-improved-model-weights).
If you would like to contribute a new dataset, please see [here](#New-dataset).
### Code formatting and typing
#### Formatting
The torchvision code is formatted by [black](https://black.readthedocs.io/en/stable/),
and checked against pep8 compliance with [flake8](https://flake8.pycqa.org/en/latest/).
Instead of relying directly on `black` however, we rely on
[ufmt](https://github.com/omnilib/ufmt), for compatibility reasons with Facebook
internal infrastructure.
To format your code, install `ufmt` with `pip install ufmt==1.3.3 black==22.3.0 usort==1.0.2` and use e.g.:
```bash
ufmt format torchvision
```
For the vast majority of cases, this is all you should need to run. For the
formatting to be a bit faster, you can also choose to only apply `ufmt` to the
files that were edited in your PR with e.g.:
```bash
ufmt format `git diff main --name-only`
```
Similarly, you can check for `flake8` errors with `flake8 torchvision`, although
they should be fairly rare considering that most of the errors are automatically
taken care of by `ufmt` already.
##### Pre-commit hooks
For convenience and **purely optionally**, you can rely on [pre-commit
hooks](https://pre-commit.com/) which will run both `ufmt` and `flake8` prior to
every commit.
First install the `pre-commit` package with `pip install pre-commit`, and then
run `pre-commit install` at the root of the repo for the hooks to be set up -
that's it.
Feel free to read the [pre-commit docs](https://pre-commit.com/#usage) to learn
more and improve your workflow. You'll see for example that `pre-commit run
--all-files` will run both `ufmt` and `flake8` without the need for you to
commit anything, and that the `--no-verify` flag can be added to `git commit` to
temporarily deactivate the hooks.
#### Type annotations
The codebase has type annotations, please make sure to add type hints if required. We use `mypy` tool for type checking:
```bash
mypy --config-file mypy.ini
```
### Unit tests
Before running tests make sure to install [test dependencies](#other-development-dependencies-some-of-these-are-needed-to-run-tests).
If you have modified the code by adding a new feature or a bug-fix, please add unit tests for that. To run a specific
test:
```bash
pytest test/<test-module.py> -vvv -k <test_myfunc>
# e.g. pytest test/test_transforms.py -vvv -k test_center_crop
```
If you would like to run all tests:
```bash
pytest test -vvv
```
Tests that require internet access should be in
`test/test_internet.py`.
### Documentation
Torchvision uses [Google style](http://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html)
for formatting docstrings. Length of line inside docstrings block must be limited to 120 characters.
Please, follow the instructions to build and deploy the documentation locally.
#### Install requirements
```bash
cd docs
pip install -r requirements.txt
```
#### Build
```bash
cd docs
make html-noplot
```
Then open `docs/build/html/index.html` in your favorite browser.
The docs are also automatically built when you submit a PR. The job that
builds the docs is named `build_docs`. You can access the rendered docs by
clicking on that job and then going to the "Artifacts" tab.
You can clean the built docs and re-start the build from scratch by doing ``make
clean``.
#### Building the example gallery - or not
In most cases, running `make html-noplot` is enough to build the docs for your
specific use-case. The `noplot` part tells sphinx **not** to build the examples
in the [gallery](https://pytorch.org/vision/stable/auto_examples/index.html),
which saves a lot of building time.
If you need to build all the examples in the gallery, then you can use `make
html`.
You can also choose to only build a subset of the examples by using the
``EXAMPLES_PATTERN`` env variable, which accepts a regular expression. For
example ``EXAMPLES_PATTERN="transforms" make html`` will only build the examples
with "transforms" in their name.
### New architecture or improved model weights
Please refer to the guidelines in [Contributing to Torchvision - Models](https://github.com/pytorch/vision/blob/main/CONTRIBUTING_MODELS.md).
### New dataset
Please, do not send any PR with a new dataset without discussing
it in an issue as, most likely, it will not be accepted.
### Pull Request
If all previous checks (flake8, mypy, unit tests) are passing, please send a PR. Submitted PR will pass other tests on
different operating systems, python versions and hardware.
For more details about pull requests workflow,
please read [GitHub's guides](https://docs.github.com/en/github/collaborating-with-issues-and-pull-requests/creating-a-pull-request).
## License
By contributing to Torchvision, you agree that your contributions will be licensed
under the LICENSE file in the root directory of this source tree.
Contributors are also required to [sign our Contributor License Agreement](https://code.facebook.com/cla).

View File

@ -0,0 +1,65 @@
# Contributing to Torchvision - Models
- [New Model Architectures - Overview](#new-model-architectures---overview)
- [New Weights for Existing Model Architectures](#new-weights-for-existing-model-architectures)
## New Model Architectures - Overview
For someone who would be interested in adding a model architecture, it is also expected to train the model, so here are a few important considerations:
- Training big models requires lots of resources and the cost quickly adds up
- Reproducing models is fun but also risky as you might not always get the results reported on the paper. It might require a huge amount of effort to close the gap
- The contribution might not get merged if we significantly lack in terms of accuracy, speed etc
- Including new models in TorchVision might not be the best approach, so other options such as releasing the model through to [Pytorch Hub](https://pytorch.org/hub/) should be considered
So, before starting any work and submitting a PR there are a few critical things that need to be taken into account in order to make sure the planned contribution is within the context of TorchVision, and the requirements and expectations are discussed beforehand. If this step is skipped and a PR is submitted without prior discussion it will almost certainly be rejected.
### 1. Preparation work
- Start by looking into this [issue](https://github.com/pytorch/vision/issues/2707) in order to have an idea of the models that are being considered, express your willingness to add a new model and discuss with the community whether this model should be included in TorchVision. It is very important at this stage to make sure that there is an agreement on the value of having this model in TorchVision and there is no one else already working on it.
- If the decision is to include the new model, then please create a new ticket which will be used for all design and implementation discussions prior to the PR. One of the TorchVision maintainers will reach out at this stage and this will be your POC from this point onwards in order to provide support, guidance and regular feedback.
### 2. Implement the model
Please take a look at existing models in TorchVision to get familiar with the idioms. Also, please look at recent contributions for new models. If in doubt about any design decisions you can ask for feedback on the issue created in step 1. Example of things to take into account:
- The implementation should be as close as possible to the canonical implementation/paper
- The PR must include the code implementation, documentation and tests
- It should also extend the existing reference scripts used to train the model
- The weights need to reproduce closely the results of the paper in terms of accuracy, even though the final weights to be deployed will be those trained by the TorchVision maintainers
- The PR description should include commands/configuration used to train the model, so that the TorchVision maintainers can easily run them to verify the implementation and generate the final model to be released
- Make sure we re-use existing components as much as possible (inheritance)
- New primitives (transforms, losses, etc.) can be added if necessary, but the final location will be determined after discussion with the dedicated maintainer
- Please take a look at the detailed [implementation and documentation guidelines](https://github.com/pytorch/vision/issues/5319) for a fine grain list of things not to be missed
### 3. Train the model with reference scripts
To validate the new model against the common benchmark, as well as to generate pre-trained weights, you must use TorchVisions reference scripts to train the model.
Make sure all logs and a final (or best) checkpoint are saved, because it is expected that a submission shows that a model has been successfully trained and the results are in line with the original paper/repository. This will allow the reviewers to quickly check the validity of the submission, but please note that the final model to be released will be re-trained by the maintainers in order to verify reproducibility, ensure that the changes occurred during the PR review did not introduce any bugs, and to avoid moving around a large amount of data (including all checkpoints and logs).
### 4. Submit a PR
Submit a PR and tag the assigned maintainer. This PR should:
- Link the original ticket
- Provide a link for the original paper and the original repository if available
- Highlight the important test metrics and how they compare to the original paper
- Highlight any design choices that deviate from the original paper/implementation and rationale for these choices
## New Weights for Existing Model Architectures
The process of improving existing models, for instance improving accuracy by retraining the model with a different set of hyperparameters or augmentations, is the following:
1. Open a ticket and discuss with the community and maintainers whether this improvement should be added to TorchVision. Note that to add new weights the improvement should be significant.
2. Train the model using TorchVision reference scripts. You can add new primitives (transforms, losses, etc) when necessary, but the final location will be determined after discussion with the dedicated maintainer.
3. Open a PR with the new weights, together with the training logs and the checkpoint chosen so the reviewers can verify the submission. Details on how the model was trained, i.e., the training command using the reference scripts, should be included in the PR.
4. The PR reviewers should replicate the results on their side to verify the submission and if all goes well the new weights should be ready to be released!

View File

@ -0,0 +1,29 @@
BSD 3-Clause License
Copyright (c) Soumith Chintala 2016,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,5 @@
include README.md
include LICENSE
recursive-exclude * __pycache__
recursive-exclude * *.py[co]

View File

@ -0,0 +1,128 @@
# torchvision
[![total torchvision downloads](https://pepy.tech/badge/torchvision)](https://pepy.tech/project/torchvision)
[![documentation](https://img.shields.io/badge/dynamic/json.svg?label=docs&url=https%3A%2F%2Fpypi.org%2Fpypi%2Ftorchvision%2Fjson&query=%24.info.version&colorB=brightgreen&prefix=v)](https://pytorch.org/vision/stable/index.html)
The torchvision package consists of popular datasets, model architectures, and common image transformations for computer
vision.
## Installation
Please refer to the [official
instructions](https://pytorch.org/get-started/locally/) to install the stable
versions of `torch` and `torchvision` on your system.
To build source, refer to our [contributing
page](https://github.com/pytorch/vision/blob/main/CONTRIBUTING.md#development-installation).
The following is the corresponding `torchvision` versions and supported Python
versions.
| `torch` | `torchvision` | Python |
| ------------------ | ------------------ | ------------------- |
| `main` / `nightly` | `main` / `nightly` | `>=3.9`, `<=3.12` |
| `2.5` | `0.20` | `>=3.9`, `<=3.12` |
| `2.4` | `0.19` | `>=3.8`, `<=3.12` |
| `2.3` | `0.18` | `>=3.8`, `<=3.12` |
| `2.2` | `0.17` | `>=3.8`, `<=3.11` |
| `2.1` | `0.16` | `>=3.8`, `<=3.11` |
| `2.0` | `0.15` | `>=3.8`, `<=3.11` |
<details>
<summary>older versions</summary>
| `torch` | `torchvision` | Python |
|---------|-------------------|---------------------------|
| `1.13` | `0.14` | `>=3.7.2`, `<=3.10` |
| `1.12` | `0.13` | `>=3.7`, `<=3.10` |
| `1.11` | `0.12` | `>=3.7`, `<=3.10` |
| `1.10` | `0.11` | `>=3.6`, `<=3.9` |
| `1.9` | `0.10` | `>=3.6`, `<=3.9` |
| `1.8` | `0.9` | `>=3.6`, `<=3.9` |
| `1.7` | `0.8` | `>=3.6`, `<=3.9` |
| `1.6` | `0.7` | `>=3.6`, `<=3.8` |
| `1.5` | `0.6` | `>=3.5`, `<=3.8` |
| `1.4` | `0.5` | `==2.7`, `>=3.5`, `<=3.8` |
| `1.3` | `0.4.2` / `0.4.3` | `==2.7`, `>=3.5`, `<=3.7` |
| `1.2` | `0.4.1` | `==2.7`, `>=3.5`, `<=3.7` |
| `1.1` | `0.3` | `==2.7`, `>=3.5`, `<=3.7` |
| `<=1.0` | `0.2` | `==2.7`, `>=3.5`, `<=3.7` |
</details>
## Image Backends
Torchvision currently supports the following image backends:
- torch tensors
- PIL images:
- [Pillow](https://python-pillow.org/)
- [Pillow-SIMD](https://github.com/uploadcare/pillow-simd) - a **much faster** drop-in replacement for Pillow with SIMD.
Read more in in our [docs](https://pytorch.org/vision/stable/transforms.html).
## [UNSTABLE] Video Backend
Torchvision currently supports the following video backends:
- [pyav](https://github.com/PyAV-Org/PyAV) (default) - Pythonic binding for ffmpeg libraries.
- video_reader - This needs ffmpeg to be installed and torchvision to be built from source. There shouldn't be any
conflicting version of ffmpeg installed. Currently, this is only supported on Linux.
```
conda install -c conda-forge 'ffmpeg<4.3'
python setup.py install
```
# Using the models on C++
Refer to [example/cpp](https://github.com/pytorch/vision/tree/main/examples/cpp).
**DISCLAIMER**: the `libtorchvision` library includes the torchvision
custom ops as well as most of the C++ torchvision APIs. Those APIs do not come
with any backward-compatibility guarantees and may change from one version to
the next. Only the Python APIs are stable and with backward-compatibility
guarantees. So, if you need stability within a C++ environment, your best bet is
to export the Python APIs via torchscript.
## Documentation
You can find the API documentation on the pytorch website: <https://pytorch.org/vision/stable/index.html>
## Contributing
See the [CONTRIBUTING](CONTRIBUTING.md) file for how to help out.
## Disclaimer on Datasets
This is a utility library that downloads and prepares public datasets. We do not host or distribute these datasets,
vouch for their quality or fairness, or claim that you have license to use the dataset. It is your responsibility to
determine whether you have permission to use the dataset under the dataset's license.
If you're a dataset owner and wish to update any part of it (description, citation, etc.), or do not want your dataset
to be included in this library, please get in touch through a GitHub issue. Thanks for your contribution to the ML
community!
## Pre-trained Model License
The pre-trained models provided in this library may have their own licenses or terms and conditions derived from the
dataset used for training. It is your responsibility to determine whether you have permission to use the models for your
use case.
More specifically, SWAG models are released under the CC-BY-NC 4.0 license. See
[SWAG LICENSE](https://github.com/facebookresearch/SWAG/blob/main/LICENSE) for additional details.
## Citing TorchVision
If you find TorchVision useful in your work, please consider citing the following BibTeX entry:
```bibtex
@software{torchvision2016,
title = {TorchVision: PyTorch's Computer Vision library},
author = {TorchVision maintainers and contributors},
year = 2016,
journal = {GitHub repository},
publisher = {GitHub},
howpublished = {\url{https://github.com/pytorch/vision}}
}
```

View File

@ -0,0 +1,6 @@
local.properties
**/*.iml
.gradle
.idea/*
.externalNativeBuild
build

View File

@ -0,0 +1,3 @@
## Status
The Android demo of TorchVision is currently unmaintained, untested and likely out-of-date.

View File

@ -0,0 +1,40 @@
allprojects {
buildscript {
ext {
minSdkVersion = 21
targetSdkVersion = 28
compileSdkVersion = 28
buildToolsVersion = '28.0.3'
coreVersion = "1.2.0"
extJUnitVersion = "1.1.1"
runnerVersion = "1.2.0"
rulesVersion = "1.2.0"
junitVersion = "4.12"
androidSupportAppCompatV7Version = "28.0.0"
fbjniJavaOnlyVersion = "0.0.3"
soLoaderNativeLoaderVersion = "0.10.5"
pytorchAndroidVersion = "1.12"
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:4.1.2'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.14.2'
}
}
repositories {
google()
mavenCentral()
}
}
ext.deps = [
jsr305: 'com.google.code.findbugs:jsr305:3.0.1',
]

View File

@ -0,0 +1,24 @@
ABI_FILTERS=armeabi-v7a,arm64-v8a,x86,x86_64
VERSION_NAME=0.15.0-SNAPSHOT
GROUP=org.pytorch
MAVEN_GROUP=org.pytorch
SONATYPE_STAGING_PROFILE=orgpytorch
POM_URL=https://github.com/pytorch/vision/
POM_SCM_URL=https://github.com/pytorch/vision.git
POM_SCM_CONNECTION=scm:git:https://github.com/pytorch/vision
POM_SCM_DEV_CONNECTION=scm:git:git@github.com:pytorch/vision.git
POM_LICENSE_NAME=BSD 3-Clause
POM_LICENSE_URL=https://github.com/pytorch/vision/blob/main/LICENSE
POM_ISSUES_URL=https://github.com/pytorch/vision/issues
POM_LICENSE_DIST=repo
POM_DEVELOPER_ID=pytorch
POM_DEVELOPER_NAME=pytorch
# Gradle internals
android.useAndroidX=true
android.enableJetifier=true
testAppAllVariantsEnabled=false
org.gradle.jvmargs=-Xmx12g

View File

@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View File

@ -0,0 +1,11 @@
afterEvaluate { project ->
if (POM_PACKAGING == 'aar') {
task headersJar(type: Jar) {
archiveClassifier.set('headers')
from("$rootDir/cxx/") {
include '**/*.h'
}
}
artifacts.add('archives', headersJar)
}
}

View File

@ -0,0 +1,3 @@
apply from: rootProject.file('gradle_scripts/android_tasks.gradle')
apply plugin: 'com.vanniktech.maven.publish'

View File

@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

@ -0,0 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View File

@ -0,0 +1,47 @@
cmake_minimum_required(VERSION 3.4.1)
set(TARGET torchvision_ops)
project(${TARGET} CXX)
set(CMAKE_CXX_STANDARD 17)
string(APPEND CMAKE_CXX_FLAGS " -DMOBILE")
set(build_DIR ${CMAKE_SOURCE_DIR}/build)
set(root_DIR ${CMAKE_CURRENT_LIST_DIR}/..)
file(GLOB VISION_SRCS
../../torchvision/csrc/ops/cpu/*.h
../../torchvision/csrc/ops/cpu/*.cpp
../../torchvision/csrc/ops/*.h
../../torchvision/csrc/ops/*.cpp)
add_library(${TARGET} SHARED
${VISION_SRCS}
)
file(GLOB PYTORCH_INCLUDE_DIRS "${build_DIR}/pytorch_android*.aar/headers")
file(GLOB PYTORCH_INCLUDE_DIRS_CSRC "${build_DIR}/pytorch_android*.aar/headers/torch/csrc/api/include")
file(GLOB PYTORCH_LINK_DIRS "${build_DIR}/pytorch_android*.aar/jni/${ANDROID_ABI}")
target_compile_options(${TARGET} PRIVATE
-fexceptions
)
set(BUILD_SUBDIR ${ANDROID_ABI})
find_library(PYTORCH_LIBRARY pytorch_jni
PATHS ${PYTORCH_LINK_DIRS}
NO_CMAKE_FIND_ROOT_PATH)
find_library(FBJNI_LIBRARY fbjni
PATHS ${PYTORCH_LINK_DIRS}
NO_CMAKE_FIND_ROOT_PATH)
target_include_directories(${TARGET} PRIVATE
${PYTORCH_INCLUDE_DIRS}
${PYTORCH_INCLUDE_DIRS_CSRC}
)
target_link_libraries(${TARGET} PRIVATE
${PYTORCH_LIBRARY}
${FBJNI_LIBRARY}
)

View File

@ -0,0 +1,93 @@
apply plugin: 'com.android.library'
apply plugin: 'maven'
repositories {
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
flatDir {
dirs 'aars'
}
}
android {
configurations {
extractForNativeBuild
}
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode 0
versionName "0.1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
ndk {
abiFilters ABI_FILTERS.split(",")
}
}
buildTypes {
debug {
minifyEnabled false
debuggable true
}
release {
minifyEnabled false
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
useLibrary 'android.test.runner'
useLibrary 'android.test.base'
useLibrary 'android.test.mock'
}
dependencies {
implementation 'com.android.support:appcompat-v7:' + rootProject.androidSupportAppCompatV7Version
extractForNativeBuild "org.pytorch:pytorch_android:$pytorchAndroidVersion"
// For testing: deps on local aar files
//implementation(name: 'pytorch_android-release', ext: 'aar')
//extractForNativeBuild(name: 'pytorch_android-release', ext: 'aar')
//implementation 'com.facebook.fbjni:fbjni-java-only:0.0.3'
}
task extractAARForNativeBuild {
doLast {
configurations.extractForNativeBuild.files.each {
def file = it.absoluteFile
copy {
from zipTree(file)
into "$buildDir/$file.name"
include "headers/**"
include "jni/**"
}
}
}
}
tasks.whenTaskAdded { task ->
if (task.name.contains('externalNativeBuild')) {
task.dependsOn(extractAARForNativeBuild)
}
}
apply from: rootProject.file('gradle_scripts/release.gradle')
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
artifacts.add('archives', sourcesJar)

View File

@ -0,0 +1,4 @@
POM_NAME=torchvision ops
POM_DESCRIPTION=torchvision ops
POM_ARTIFACT_ID=torchvision_ops
POM_PACKAGING=aar

View File

@ -0,0 +1 @@
<manifest package="org.pytorch.torchvision.ops" />

View File

@ -0,0 +1,4 @@
include ':ops', ':test_app'
project(':ops').projectDir = file('ops')
project(':test_app').projectDir = file('test_app/app')

View File

@ -0,0 +1,135 @@
apply plugin: 'com.android.application'
repositories {
mavenCentral()
maven {
url "https://oss.sonatype.org/content/repositories/snapshots"
}
flatDir {
dirs 'aars'
}
}
android {
configurations {
extractForNativeBuild
}
compileOptions {
sourceCompatibility 1.8
targetCompatibility 1.8
}
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
defaultConfig {
applicationId "org.pytorch.testapp"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode 1
versionName "1.0"
ndk {
abiFilters ABI_FILTERS.split(",")
}
externalNativeBuild {
cmake {
abiFilters ABI_FILTERS.split(",")
arguments "-DANDROID_STL=c++_shared"
}
}
buildConfigField("String", "MODULE_ASSET_NAME", "\"frcnn_mnetv3.pt\"")
buildConfigField("String", "LOGCAT_TAG", "@string/app_name")
buildConfigField("long[]", "INPUT_TENSOR_SHAPE", "new long[]{3, 96, 96}")
addManifestPlaceholders([APP_NAME: "@string/app_name", MAIN_ACTIVITY: "org.pytorch.testapp.MainActivity"])
}
buildTypes {
debug {
minifyEnabled false
debuggable true
}
release {
minifyEnabled false
}
}
flavorDimensions "model", "activity", "build"
productFlavors {
frcnnMnetv3 {
dimension "model"
applicationIdSuffix ".frcnnMnetv3"
buildConfigField("String", "MODULE_ASSET_NAME", "\"frcnn_mnetv3.pt\"")
addManifestPlaceholders([APP_NAME: "TV_FRCNN_MNETV3"])
buildConfigField("String", "LOGCAT_TAG", "\"pytorch-frcnn-mnetv3\"")
}
camera {
dimension "activity"
addManifestPlaceholders([APP_NAME: "TV_CAMERA_FRCNN"])
addManifestPlaceholders([MAIN_ACTIVITY: "org.pytorch.testapp.CameraActivity"])
}
base {
dimension "activity"
}
aar {
dimension "build"
}
local {
dimension "build"
}
}
packagingOptions {
doNotStrip '**.so'
pickFirst '**.so'
}
// Filtering for CI
if (!testAppAllVariantsEnabled.toBoolean()) {
variantFilter { variant ->
def names = variant.flavors*.name
if (names.contains("aar")) {
setIgnore(true)
}
}
}
}
tasks.all { task ->
// Disable externalNativeBuild for all but nativeBuild variant
if (task.name.startsWith('externalNativeBuild')
&& !task.name.contains('NativeBuild')) {
task.enabled = false
}
}
dependencies {
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.facebook.soloader:nativeloader:0.8.0'
localImplementation project(':ops')
implementation "org.pytorch:pytorch_android:$pytorchAndroidVersion"
implementation "org.pytorch:pytorch_android_torchvision:$pytorchAndroidVersion"
aarImplementation(name: 'pytorch_android-release', ext: 'aar')
aarImplementation(name: 'pytorch_android_torchvision-release', ext: 'aar')
def camerax_version = "1.0.0-alpha05"
implementation "androidx.camera:camera-core:$camerax_version"
implementation "androidx.camera:camera-camera2:$camerax_version"
implementation 'com.google.android.material:material:1.0.0-beta01'
}
task extractAARForNativeBuild {
doLast {
configurations.extractForNativeBuild.files.each {
def file = it.absoluteFile
copy {
from zipTree(file)
into "$buildDir/$file.name"
include "headers/**"
include "jni/**"
}
}
}
}
tasks.whenTaskAdded { task ->
if (task.name.contains('externalNativeBuild')) {
task.dependsOn(extractAARForNativeBuild)
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.pytorch.testapp">
<application
android:allowBackup="true"
android:label="${APP_NAME}"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="${MAIN_ACTIVITY}">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.CAMERA" />
</manifest>

View File

@ -0,0 +1,22 @@
package org.pytorch.testapp;
class BBox {
public final float score;
public final float x0;
public final float y0;
public final float x1;
public final float y1;
public BBox(float score, float x0, float y0, float x1, float y1) {
this.score = score;
this.x0 = x0;
this.y0 = y0;
this.x1 = x1;
this.y1 = y1;
}
@Override
public String toString() {
return String.format("Box{score=%f x0=%f y0=%f x1=%f y1=%f", score, x0, y0, x1, y1);
}
}

View File

@ -0,0 +1,432 @@
package org.pytorch.testapp;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.Size;
import android.view.TextureView;
import android.view.ViewStub;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.CameraX;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageAnalysisConfig;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.core.PreviewConfig;
import androidx.core.app.ActivityCompat;
import com.facebook.soloader.nativeloader.NativeLoader;
import com.facebook.soloader.nativeloader.SystemDelegate;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.pytorch.IValue;
import org.pytorch.Module;
import org.pytorch.Tensor;
public class CameraActivity extends AppCompatActivity {
private static final float BBOX_SCORE_DRAW_THRESHOLD = 0.5f;
private static final String TAG = BuildConfig.LOGCAT_TAG;
private static final int TEXT_TRIM_SIZE = 4096;
private static final int RGB_MAX_CHANNEL_VALUE = 262143;
private static final int REQUEST_CODE_CAMERA_PERMISSION = 200;
private static final String[] PERMISSIONS = {Manifest.permission.CAMERA};
static {
if (!NativeLoader.isInitialized()) {
NativeLoader.init(new SystemDelegate());
}
NativeLoader.loadLibrary("pytorch_jni");
NativeLoader.loadLibrary("torchvision_ops");
}
private Bitmap mInputTensorBitmap;
private Bitmap mBitmap;
private Canvas mCanvas;
private long mLastAnalysisResultTime;
protected HandlerThread mBackgroundThread;
protected Handler mBackgroundHandler;
protected Handler mUIHandler;
private TextView mTextView;
private ImageView mCameraOverlay;
private StringBuilder mTextViewStringBuilder = new StringBuilder();
private Paint mBboxPaint;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
mTextView = findViewById(R.id.text);
mCameraOverlay = findViewById(R.id.camera_overlay);
mUIHandler = new Handler(getMainLooper());
startBackgroundThread();
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, PERMISSIONS, REQUEST_CODE_CAMERA_PERMISSION);
} else {
setupCameraX();
}
mBboxPaint = new Paint();
mBboxPaint.setAntiAlias(true);
mBboxPaint.setDither(true);
mBboxPaint.setColor(Color.GREEN);
}
@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
startBackgroundThread();
}
protected void startBackgroundThread() {
mBackgroundThread = new HandlerThread("ModuleActivity");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
@Override
protected void onDestroy() {
stopBackgroundThread();
super.onDestroy();
}
protected void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
Log.e(TAG, "Error on stopping background thread", e);
}
}
@Override
public void onRequestPermissionsResult(
int requestCode, String[] permissions, int[] grantResults) {
if (requestCode == REQUEST_CODE_CAMERA_PERMISSION) {
if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
Toast.makeText(
this,
"You can't use image classification example without granting CAMERA permission",
Toast.LENGTH_LONG)
.show();
finish();
} else {
setupCameraX();
}
}
}
private void setupCameraX() {
final TextureView textureView =
((ViewStub) findViewById(R.id.camera_texture_view_stub))
.inflate()
.findViewById(R.id.texture_view);
final PreviewConfig previewConfig = new PreviewConfig.Builder().build();
final Preview preview = new Preview(previewConfig);
preview.setOnPreviewOutputUpdateListener(
new Preview.OnPreviewOutputUpdateListener() {
@Override
public void onUpdated(Preview.PreviewOutput output) {
textureView.setSurfaceTexture(output.getSurfaceTexture());
}
});
final DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
final ImageAnalysisConfig imageAnalysisConfig =
new ImageAnalysisConfig.Builder()
.setTargetResolution(new Size(displayMetrics.widthPixels, displayMetrics.heightPixels))
.setCallbackHandler(mBackgroundHandler)
.setImageReaderMode(ImageAnalysis.ImageReaderMode.ACQUIRE_LATEST_IMAGE)
.build();
final ImageAnalysis imageAnalysis = new ImageAnalysis(imageAnalysisConfig);
imageAnalysis.setAnalyzer(
new ImageAnalysis.Analyzer() {
@Override
public void analyze(ImageProxy image, int rotationDegrees) {
if (SystemClock.elapsedRealtime() - mLastAnalysisResultTime < 500) {
return;
}
final Result result = CameraActivity.this.analyzeImage(image, rotationDegrees);
if (result != null) {
mLastAnalysisResultTime = SystemClock.elapsedRealtime();
CameraActivity.this.runOnUiThread(
new Runnable() {
@Override
public void run() {
CameraActivity.this.handleResult(result);
}
});
}
}
});
CameraX.bindToLifecycle(this, preview, imageAnalysis);
}
private Module mModule;
private FloatBuffer mInputTensorBuffer;
private Tensor mInputTensor;
private static int clamp0255(int x) {
if (x > 255) {
return 255;
}
return x < 0 ? 0 : x;
}
protected void fillInputTensorBuffer(
ImageProxy image, int rotationDegrees, FloatBuffer inputTensorBuffer) {
if (mInputTensorBitmap == null) {
final int tensorSize = Math.min(image.getWidth(), image.getHeight());
mInputTensorBitmap = Bitmap.createBitmap(tensorSize, tensorSize, Bitmap.Config.ARGB_8888);
}
ImageProxy.PlaneProxy[] planes = image.getPlanes();
ImageProxy.PlaneProxy Y = planes[0];
ImageProxy.PlaneProxy U = planes[1];
ImageProxy.PlaneProxy V = planes[2];
ByteBuffer yBuffer = Y.getBuffer();
ByteBuffer uBuffer = U.getBuffer();
ByteBuffer vBuffer = V.getBuffer();
final int imageWidth = image.getWidth();
final int imageHeight = image.getHeight();
final int tensorSize = Math.min(imageWidth, imageHeight);
int widthAfterRtn = imageWidth;
int heightAfterRtn = imageHeight;
boolean oddRotation = rotationDegrees == 90 || rotationDegrees == 270;
if (oddRotation) {
widthAfterRtn = imageHeight;
heightAfterRtn = imageWidth;
}
int minSizeAfterRtn = Math.min(heightAfterRtn, widthAfterRtn);
int cropWidthAfterRtn = minSizeAfterRtn;
int cropHeightAfterRtn = minSizeAfterRtn;
int cropWidthBeforeRtn = cropWidthAfterRtn;
int cropHeightBeforeRtn = cropHeightAfterRtn;
if (oddRotation) {
cropWidthBeforeRtn = cropHeightAfterRtn;
cropHeightBeforeRtn = cropWidthAfterRtn;
}
int offsetX = (int) ((imageWidth - cropWidthBeforeRtn) / 2.f);
int offsetY = (int) ((imageHeight - cropHeightBeforeRtn) / 2.f);
int yRowStride = Y.getRowStride();
int yPixelStride = Y.getPixelStride();
int uvRowStride = U.getRowStride();
int uvPixelStride = U.getPixelStride();
float scale = cropWidthAfterRtn / tensorSize;
int yIdx, uvIdx, yi, ui, vi;
final int channelSize = tensorSize * tensorSize;
for (int y = 0; y < tensorSize; y++) {
for (int x = 0; x < tensorSize; x++) {
final int centerCropX = (int) Math.floor(x * scale);
final int centerCropY = (int) Math.floor(y * scale);
int srcX = centerCropX + offsetX;
int srcY = centerCropY + offsetY;
if (rotationDegrees == 90) {
srcX = offsetX + centerCropY;
srcY = offsetY + (minSizeAfterRtn - 1) - centerCropX;
} else if (rotationDegrees == 180) {
srcX = offsetX + (minSizeAfterRtn - 1) - centerCropX;
srcY = offsetY + (minSizeAfterRtn - 1) - centerCropY;
} else if (rotationDegrees == 270) {
srcX = offsetX + (minSizeAfterRtn - 1) - centerCropY;
srcY = offsetY + centerCropX;
}
yIdx = srcY * yRowStride + srcX * yPixelStride;
uvIdx = (srcY >> 1) * uvRowStride + (srcX >> 1) * uvPixelStride;
yi = yBuffer.get(yIdx) & 0xff;
ui = uBuffer.get(uvIdx) & 0xff;
vi = vBuffer.get(uvIdx) & 0xff;
yi = (yi - 16) < 0 ? 0 : (yi - 16);
ui -= 128;
vi -= 128;
int a0 = 1192 * yi;
int ri = (a0 + 1634 * vi);
int gi = (a0 - 833 * vi - 400 * ui);
int bi = (a0 + 2066 * ui);
ri = ri > RGB_MAX_CHANNEL_VALUE ? RGB_MAX_CHANNEL_VALUE : (ri < 0 ? 0 : ri);
gi = gi > RGB_MAX_CHANNEL_VALUE ? RGB_MAX_CHANNEL_VALUE : (gi < 0 ? 0 : gi);
bi = bi > RGB_MAX_CHANNEL_VALUE ? RGB_MAX_CHANNEL_VALUE : (bi < 0 ? 0 : bi);
final int color =
0xff000000 | ((ri << 6) & 0xff0000) | ((gi >> 2) & 0xff00) | ((bi >> 10) & 0xff);
mInputTensorBitmap.setPixel(x, y, color);
inputTensorBuffer.put(0 * channelSize + y * tensorSize + x, clamp0255(ri >> 10) / 255.f);
inputTensorBuffer.put(1 * channelSize + y * tensorSize + x, clamp0255(gi >> 10) / 255.f);
inputTensorBuffer.put(2 * channelSize + y * tensorSize + x, clamp0255(bi >> 10) / 255.f);
}
}
}
public static String assetFilePath(Context context, String assetName) {
File file = new File(context.getFilesDir(), assetName);
if (file.exists() && file.length() > 0) {
return file.getAbsolutePath();
}
try (InputStream is = context.getAssets().open(assetName)) {
try (OutputStream os = new FileOutputStream(file)) {
byte[] buffer = new byte[4 * 1024];
int read;
while ((read = is.read(buffer)) != -1) {
os.write(buffer, 0, read);
}
os.flush();
}
return file.getAbsolutePath();
} catch (IOException e) {
Log.e(TAG, "Error process asset " + assetName + " to file path");
}
return null;
}
@WorkerThread
@Nullable
protected Result analyzeImage(ImageProxy image, int rotationDegrees) {
Log.i(TAG, String.format("analyzeImage(%s, %d)", image, rotationDegrees));
final int tensorSize = Math.min(image.getWidth(), image.getHeight());
if (mModule == null) {
Log.i(TAG, "Loading module from asset '" + BuildConfig.MODULE_ASSET_NAME + "'");
mInputTensorBuffer = Tensor.allocateFloatBuffer(3 * tensorSize * tensorSize);
mInputTensor = Tensor.fromBlob(mInputTensorBuffer, new long[] {3, tensorSize, tensorSize});
final String modelFileAbsoluteFilePath =
new File(assetFilePath(this, BuildConfig.MODULE_ASSET_NAME)).getAbsolutePath();
mModule = Module.load(modelFileAbsoluteFilePath);
}
final long startTime = SystemClock.elapsedRealtime();
fillInputTensorBuffer(image, rotationDegrees, mInputTensorBuffer);
final long moduleForwardStartTime = SystemClock.elapsedRealtime();
final IValue outputTuple = mModule.forward(IValue.listFrom(mInputTensor));
final IValue out1 = outputTuple.toTuple()[1];
final Map<String, IValue> map = out1.toList()[0].toDictStringKey();
float[] boxesData = new float[] {};
float[] scoresData = new float[] {};
final List<BBox> bboxes = new ArrayList<>();
if (map.containsKey("boxes")) {
final Tensor boxesTensor = map.get("boxes").toTensor();
final Tensor scoresTensor = map.get("scores").toTensor();
boxesData = boxesTensor.getDataAsFloatArray();
scoresData = scoresTensor.getDataAsFloatArray();
final int n = scoresData.length;
for (int i = 0; i < n; i++) {
final BBox bbox =
new BBox(
scoresData[i],
boxesData[4 * i + 0],
boxesData[4 * i + 1],
boxesData[4 * i + 2],
boxesData[4 * i + 3]);
android.util.Log.i(TAG, String.format("Forward result %d: %s", i, bbox));
bboxes.add(bbox);
}
} else {
android.util.Log.i(TAG, "Forward result empty");
}
final long moduleForwardDuration = SystemClock.elapsedRealtime() - moduleForwardStartTime;
final long analysisDuration = SystemClock.elapsedRealtime() - startTime;
return new Result(tensorSize, bboxes, moduleForwardDuration, analysisDuration);
}
@UiThread
protected void handleResult(Result result) {
final int W = mCameraOverlay.getMeasuredWidth();
final int H = mCameraOverlay.getMeasuredHeight();
final int size = Math.min(W, H);
final int offsetX = (W - size) / 2;
final int offsetY = (H - size) / 2;
float scaleX = (float) size / result.tensorSize;
float scaleY = (float) size / result.tensorSize;
if (mBitmap == null) {
mBitmap = Bitmap.createBitmap(W, H, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
}
mCanvas.drawBitmap(
mInputTensorBitmap,
new Rect(0, 0, result.tensorSize, result.tensorSize),
new Rect(offsetX, offsetY, offsetX + size, offsetY + size),
null);
for (final BBox bbox : result.bboxes) {
if (bbox.score < BBOX_SCORE_DRAW_THRESHOLD) {
continue;
}
float c_x0 = offsetX + scaleX * bbox.x0;
float c_y0 = offsetY + scaleY * bbox.y0;
float c_x1 = offsetX + scaleX * bbox.x1;
float c_y1 = offsetY + scaleY * bbox.y1;
mCanvas.drawLine(c_x0, c_y0, c_x1, c_y0, mBboxPaint);
mCanvas.drawLine(c_x1, c_y0, c_x1, c_y1, mBboxPaint);
mCanvas.drawLine(c_x1, c_y1, c_x0, c_y1, mBboxPaint);
mCanvas.drawLine(c_x0, c_y1, c_x0, c_y0, mBboxPaint);
mCanvas.drawText(String.format("%.2f", bbox.score), c_x0, c_y0, mBboxPaint);
}
mCameraOverlay.setImageBitmap(mBitmap);
String message = String.format("forwardDuration:%d", result.moduleForwardDuration);
Log.i(TAG, message);
mTextViewStringBuilder.insert(0, '\n').insert(0, message);
if (mTextViewStringBuilder.length() > TEXT_TRIM_SIZE) {
mTextViewStringBuilder.delete(TEXT_TRIM_SIZE, mTextViewStringBuilder.length());
}
mTextView.setText(mTextViewStringBuilder.toString());
}
}

View File

@ -0,0 +1,159 @@
package org.pytorch.testapp;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.SystemClock;
import android.util.Log;
import android.widget.TextView;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import androidx.appcompat.app.AppCompatActivity;
import com.facebook.soloader.nativeloader.NativeLoader;
import com.facebook.soloader.nativeloader.SystemDelegate;
import java.nio.FloatBuffer;
import java.util.Map;
import org.pytorch.IValue;
import org.pytorch.Module;
import org.pytorch.PyTorchAndroid;
import org.pytorch.Tensor;
public class MainActivity extends AppCompatActivity {
static {
if (!NativeLoader.isInitialized()) {
NativeLoader.init(new SystemDelegate());
}
NativeLoader.loadLibrary("pytorch_jni");
NativeLoader.loadLibrary("torchvision_ops");
}
private static final String TAG = BuildConfig.LOGCAT_TAG;
private static final int TEXT_TRIM_SIZE = 4096;
private TextView mTextView;
protected HandlerThread mBackgroundThread;
protected Handler mBackgroundHandler;
private Module mModule;
private FloatBuffer mInputTensorBuffer;
private Tensor mInputTensor;
private StringBuilder mTextViewStringBuilder = new StringBuilder();
private final Runnable mModuleForwardRunnable =
new Runnable() {
@Override
public void run() {
final Result result = doModuleForward();
runOnUiThread(
() -> {
handleResult(result);
if (mBackgroundHandler != null) {
mBackgroundHandler.post(mModuleForwardRunnable);
}
});
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = findViewById(R.id.text);
startBackgroundThread();
mBackgroundHandler.post(mModuleForwardRunnable);
}
protected void startBackgroundThread() {
mBackgroundThread = new HandlerThread(TAG + "_bg");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
@Override
protected void onDestroy() {
stopBackgroundThread();
super.onDestroy();
}
protected void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (InterruptedException e) {
Log.e(TAG, "Error stopping background thread", e);
}
}
@WorkerThread
@Nullable
protected Result doModuleForward() {
if (mModule == null) {
final long[] shape = BuildConfig.INPUT_TENSOR_SHAPE;
long numElements = 1;
for (int i = 0; i < shape.length; i++) {
numElements *= shape[i];
}
mInputTensorBuffer = Tensor.allocateFloatBuffer((int) numElements);
mInputTensor = Tensor.fromBlob(mInputTensorBuffer, BuildConfig.INPUT_TENSOR_SHAPE);
PyTorchAndroid.setNumThreads(1);
mModule = PyTorchAndroid.loadModuleFromAsset(getAssets(), BuildConfig.MODULE_ASSET_NAME);
}
final long startTime = SystemClock.elapsedRealtime();
final long moduleForwardStartTime = SystemClock.elapsedRealtime();
final IValue outputTuple = mModule.forward(IValue.listFrom(mInputTensor));
final IValue[] outputArray = outputTuple.toTuple();
final IValue out0 = outputArray[0];
final Map<String, IValue> map = out0.toDictStringKey();
if (map.containsKey("boxes")) {
final Tensor boxes = map.get("boxes").toTensor();
final Tensor scores = map.get("scores").toTensor();
final float[] boxesData = boxes.getDataAsFloatArray();
final float[] scoresData = scores.getDataAsFloatArray();
final int n = scoresData.length;
for (int i = 0; i < n; i++) {
android.util.Log.i(
TAG,
String.format(
"Forward result %d: score %f box:(%f, %f, %f, %f)",
scoresData[i],
boxesData[4 * i + 0],
boxesData[4 * i + 1],
boxesData[4 * i + 2],
boxesData[4 * i + 3]));
}
} else {
android.util.Log.i(TAG, "Forward result empty");
}
final long moduleForwardDuration = SystemClock.elapsedRealtime() - moduleForwardStartTime;
final long analysisDuration = SystemClock.elapsedRealtime() - startTime;
return new Result(new float[] {}, moduleForwardDuration, analysisDuration);
}
static class Result {
private final float[] scores;
private final long totalDuration;
private final long moduleForwardDuration;
public Result(float[] scores, long moduleForwardDuration, long totalDuration) {
this.scores = scores;
this.moduleForwardDuration = moduleForwardDuration;
this.totalDuration = totalDuration;
}
}
@UiThread
protected void handleResult(Result result) {
String message = String.format("forwardDuration:%d", result.moduleForwardDuration);
mTextViewStringBuilder.insert(0, '\n').insert(0, message);
if (mTextViewStringBuilder.length() > TEXT_TRIM_SIZE) {
mTextViewStringBuilder.delete(TEXT_TRIM_SIZE, mTextViewStringBuilder.length());
}
mTextView.setText(mTextViewStringBuilder.toString());
}
}

View File

@ -0,0 +1,17 @@
package org.pytorch.testapp;
import java.util.List;
class Result {
public final int tensorSize;
public final List<BBox> bboxes;
public final long totalDuration;
public final long moduleForwardDuration;
public Result(int tensorSize, List<BBox> bboxes, long moduleForwardDuration, long totalDuration) {
this.tensorSize = tensorSize;
this.bboxes = bboxes;
this.moduleForwardDuration = moduleForwardDuration;
this.totalDuration = totalDuration;
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".CameraActivity">
<ViewStub
android:id="@+id/camera_texture_view_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout="@layout/texture_view"/>
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:textSize="16sp"
android:textStyle="bold"
android:textColor="#ff0000"/>
<ImageView
android:id="@+id/camera_overlay"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top"
android:textSize="14sp"
android:background="@android:color/black"
android:textColor="@android:color/white" />
</FrameLayout>

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<TextureView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/texture_view"
android:layout_width="match_parent"
android:layout_height="0dp" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#008577</color>
<color name="colorPrimaryDark">#00574B</color>
<color name="colorAccent">#D81B60</color>
</resources>

View File

@ -0,0 +1,3 @@
<resources>
<string name="app_name">TV_FRCNN</string>
</resources>

View File

@ -0,0 +1,11 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
</resources>

View File

@ -0,0 +1,21 @@
import torch
from torch.utils.mobile_optimizer import optimize_for_mobile
from torchvision.models.detection import (
fasterrcnn_mobilenet_v3_large_320_fpn,
FasterRCNN_MobileNet_V3_Large_320_FPN_Weights,
)
print(torch.__version__)
model = fasterrcnn_mobilenet_v3_large_320_fpn(
weights=FasterRCNN_MobileNet_V3_Large_320_FPN_Weights.DEFAULT,
box_score_thresh=0.7,
rpn_post_nms_top_n_test=100,
rpn_score_thresh=0.4,
rpn_pre_nms_top_n_test=150,
)
model.eval()
script_model = torch.jit.script(model)
opt_script_model = optimize_for_mobile(script_model)
opt_script_model.save("app/src/main/assets/frcnn_mnetv3.pt")

View File

@ -0,0 +1,99 @@
import os
import platform
import statistics
import torch
import torch.utils.benchmark as benchmark
import torchvision
def print_machine_specs():
print("Processor:", platform.processor())
print("Platform:", platform.platform())
print("Logical CPUs:", os.cpu_count())
print(f"\nCUDA device: {torch.cuda.get_device_name()}")
print(f"Total Memory: {torch.cuda.get_device_properties(0).total_memory / 1e9:.2f} GB")
def get_data():
transform = torchvision.transforms.Compose(
[
torchvision.transforms.PILToTensor(),
]
)
path = os.path.join(os.getcwd(), "data")
testset = torchvision.datasets.Places365(
root="./data", download=not os.path.exists(path), transform=transform, split="val"
)
testloader = torch.utils.data.DataLoader(
testset, batch_size=1000, shuffle=False, num_workers=1, collate_fn=lambda batch: [r[0] for r in batch]
)
return next(iter(testloader))
def run_encoding_benchmark(decoded_images):
results = []
for device in ["cpu", "cuda"]:
decoded_images_device = [t.to(device=device) for t in decoded_images]
for size in [1, 100, 1000]:
for num_threads in [1, 12, 24]:
for stmt, strat in zip(
[
"[torchvision.io.encode_jpeg(img) for img in decoded_images_device_trunc]",
"torchvision.io.encode_jpeg(decoded_images_device_trunc)",
],
["unfused", "fused"],
):
decoded_images_device_trunc = decoded_images_device[:size]
t = benchmark.Timer(
stmt=stmt,
setup="import torchvision",
globals={"decoded_images_device_trunc": decoded_images_device_trunc},
label="Image Encoding",
sub_label=f"{device.upper()} ({strat}): {stmt}",
description=f"{size} images",
num_threads=num_threads,
)
results.append(t.blocked_autorange())
compare = benchmark.Compare(results)
compare.print()
def run_decoding_benchmark(encoded_images):
results = []
for device in ["cpu", "cuda"]:
for size in [1, 100, 1000]:
for num_threads in [1, 12, 24]:
for stmt, strat in zip(
[
f"[torchvision.io.decode_jpeg(img, device='{device}') for img in encoded_images_trunc]",
f"torchvision.io.decode_jpeg(encoded_images_trunc, device='{device}')",
],
["unfused", "fused"],
):
encoded_images_trunc = encoded_images[:size]
t = benchmark.Timer(
stmt=stmt,
setup="import torchvision",
globals={"encoded_images_trunc": encoded_images_trunc},
label="Image Decoding",
sub_label=f"{device.upper()} ({strat}): {stmt}",
description=f"{size} images",
num_threads=num_threads,
)
results.append(t.blocked_autorange())
compare = benchmark.Compare(results)
compare.print()
if __name__ == "__main__":
print_machine_specs()
decoded_images = get_data()
mean_h, mean_w = statistics.mean(t.shape[-2] for t in decoded_images), statistics.mean(
t.shape[-1] for t in decoded_images
)
print(f"\nMean image size: {int(mean_h)}x{int(mean_w)}")
run_encoding_benchmark(decoded_images)
encoded_images_cuda = torchvision.io.encode_jpeg([img.cuda() for img in decoded_images])
encoded_images_cpu = [img.cpu() for img in encoded_images_cuda]
run_decoding_benchmark(encoded_images_cpu)

View File

@ -0,0 +1,50 @@
# TorchVisionConfig.cmake
# --------------------
#
# Exported targets:: Vision
#
@PACKAGE_INIT@
set(PN TorchVision)
# location of include/torchvision
set(${PN}_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@")
set(${PN}_LIBRARY "")
set(${PN}_DEFINITIONS USING_${PN})
check_required_components(${PN})
if(NOT (CMAKE_VERSION VERSION_LESS 3.0))
#-----------------------------------------------------------------------------
# Don't include targets if this file is being picked up by another
# project which has already built this as a subproject
#-----------------------------------------------------------------------------
if(NOT TARGET ${PN}::${PN})
include("${CMAKE_CURRENT_LIST_DIR}/${PN}Targets.cmake")
target_include_directories(${PN}::${PN} INTERFACE "${${PN}_INCLUDE_DIR}")
if(@WITH_CUDA@)
target_compile_definitions(${PN}::${PN} INTERFACE WITH_CUDA)
endif()
find_package(Torch REQUIRED)
target_link_libraries(${PN}::${PN} INTERFACE torch)
if(@WITH_PNG@)
find_package(PNG REQUIRED)
target_link_libraries(${PN}::${PN} INTERFACE ${PNG_LIBRARY})
target_compile_definitions(${PN}::${PN} INTERFACE PNG_FOUND)
endif()
if(@WITH_JPEG@)
find_package(JPEG REQUIRED)
target_link_libraries(${PN}::${PN} INTERFACE ${JPEG_LIBRARIES})
target_compile_definitions(${PN}::${PN} INTERFACE JPEG_FOUND)
endif()
endif()
endif()

View File

@ -0,0 +1,207 @@
# This file is based off of the Platform/Darwin.cmake and Platform/UnixPaths.cmake
# files which are included with CMake 2.8.4
# It has been altered for iOS development
# Options:
#
# IOS_PLATFORM = OS (default) or SIMULATOR
# This decides if SDKS will be selected from the iPhoneOS.platform or iPhoneSimulator.platform folders
# OS - the default, used to build for iPhone and iPad physical devices, which have an arm arch.
# SIMULATOR - used to build for the Simulator platforms, which have an x86 arch.
#
# CMAKE_IOS_DEVELOPER_ROOT = automatic(default) or /path/to/platform/Developer folder
# By default this location is automatically chosen based on the IOS_PLATFORM value above.
# If set manually, it will override the default location and force the user of a particular Developer Platform
#
# CMAKE_IOS_SDK_ROOT = automatic(default) or /path/to/platform/Developer/SDKs/SDK folder
# By default this location is automatically chosen based on the CMAKE_IOS_DEVELOPER_ROOT value.
# In this case it will always be the most up-to-date SDK found in the CMAKE_IOS_DEVELOPER_ROOT path.
# If set manually, this will force the use of a specific SDK version
# Macros:
#
# set_xcode_property (TARGET XCODE_PROPERTY XCODE_VALUE)
# A convenience macro for setting xcode specific properties on targets
# example: set_xcode_property (myioslib IPHONEOS_DEPLOYMENT_TARGET "3.1")
#
# find_host_package (PROGRAM ARGS)
# A macro used to find executable programs on the host system, not within the iOS environment.
# Thanks to the android-cmake project for providing the command
# Standard settings
set(CMAKE_SYSTEM_NAME Darwin)
set(CMAKE_SYSTEM_VERSION 1)
set(UNIX True)
set(APPLE True)
set(IOS True)
# Required as of cmake 2.8.10
set(CMAKE_OSX_DEPLOYMENT_TARGET "" CACHE STRING "Force unset of the deployment target for iOS" FORCE)
# Determine the cmake host system version so we know where to find the iOS SDKs
find_program(CMAKE_UNAME uname /bin /usr/bin /usr/local/bin)
if(CMAKE_UNAME)
exec_program(uname ARGS -r OUTPUT_VARIABLE CMAKE_HOST_SYSTEM_VERSION)
string(REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "\\1" DARWIN_MAJOR_VERSION "${CMAKE_HOST_SYSTEM_VERSION}")
endif(CMAKE_UNAME)
# Force the compilers to gcc for iOS
set(CMAKE_C_COMPILER /usr/bin/gcc CACHE STRING "")
set(CMAKE_CXX_COMPILER /usr/bin/g++ CACHE STRING "")
set(CMAKE_AR ar CACHE FILEPATH "" FORCE)
set(CMAKE_RANLIB ranlib CACHE FILEPATH "" FORCE)
set(PKG_CONFIG_EXECUTABLE pkg-config CACHE FILEPATH "" FORCE)
# Setup iOS platform unless specified manually with IOS_PLATFORM
if(NOT DEFINED IOS_PLATFORM)
set(IOS_PLATFORM "OS")
endif(NOT DEFINED IOS_PLATFORM)
set(IOS_PLATFORM ${IOS_PLATFORM} CACHE STRING "Type of iOS Platform")
# Check the platform selection and setup for developer root
if(${IOS_PLATFORM} STREQUAL "OS")
set(IOS_PLATFORM_LOCATION "iPhoneOS.platform")
set(XCODE_IOS_PLATFORM iphoneos)
# This causes the installers to properly locate the output libraries
set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphoneos")
elseif(${IOS_PLATFORM} STREQUAL "SIMULATOR")
set(IOS_PLATFORM_LOCATION "iPhoneSimulator.platform")
set(XCODE_IOS_PLATFORM iphonesimulator)
# This causes the installers to properly locate the output libraries
set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-iphonesimulator")
elseif(${IOS_PLATFORM} STREQUAL "WATCHOS")
set(IOS_PLATFORM_LOCATION "WatchOS.platform")
set(XCODE_IOS_PLATFORM watchos)
# This causes the installers to properly locate the output libraries
set(CMAKE_XCODE_EFFECTIVE_PLATFORMS "-watchos")
else(${IOS_PLATFORM} STREQUAL "OS")
message(FATAL_ERROR
"Unsupported IOS_PLATFORM value selected. "
"Please choose OS, SIMULATOR, or WATCHOS.")
endif()
# All iOS/Darwin specific settings - some may be redundant
set(CMAKE_SHARED_LIBRARY_PREFIX "lib")
set(CMAKE_SHARED_LIBRARY_SUFFIX ".dylib")
set(CMAKE_SHARED_MODULE_PREFIX "lib")
set(CMAKE_SHARED_MODULE_SUFFIX ".so")
set(CMAKE_MODULE_EXISTS 1)
set(CMAKE_DL_LIBS "")
set(CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG "-compatibility_version ")
set(CMAKE_C_OSX_CURRENT_VERSION_FLAG "-current_version ")
set(CMAKE_CXX_OSX_COMPATIBILITY_VERSION_FLAG "${CMAKE_C_OSX_COMPATIBILITY_VERSION_FLAG}")
set(CMAKE_CXX_OSX_CURRENT_VERSION_FLAG "${CMAKE_C_OSX_CURRENT_VERSION_FLAG}")
if(IOS_DEPLOYMENT_TARGET)
set(XCODE_IOS_PLATFORM_VERSION_FLAGS "-m${XCODE_IOS_PLATFORM}-version-min=${IOS_DEPLOYMENT_TARGET}")
endif()
# Hidden visibility is required for cxx on iOS
set(CMAKE_C_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS}")
set(CMAKE_CXX_FLAGS_INIT "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -fvisibility-inlines-hidden")
set(CMAKE_C_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_C_LINK_FLAGS}")
set(CMAKE_CXX_LINK_FLAGS "${XCODE_IOS_PLATFORM_VERSION_FLAGS} -Wl,-search_paths_first ${CMAKE_CXX_LINK_FLAGS}")
set(CMAKE_PLATFORM_HAS_INSTALLNAME 1)
set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "-dynamiclib -headerpad_max_install_names")
set(CMAKE_SHARED_MODULE_CREATE_C_FLAGS "-bundle -headerpad_max_install_names")
set(CMAKE_SHARED_MODULE_LOADER_C_FLAG "-Wl,-bundle_loader,")
set(CMAKE_SHARED_MODULE_LOADER_CXX_FLAG "-Wl,-bundle_loader,")
set(CMAKE_FIND_LIBRARY_SUFFIXES ".dylib" ".so" ".a")
# hack: if a new cmake (which uses CMAKE_INSTALL_NAME_TOOL) runs on an old build tree
# (where install_name_tool was hardcoded) and where CMAKE_INSTALL_NAME_TOOL isn't in the cache
# and still cmake didn't fail in CMakeFindBinUtils.cmake (because it isn't rerun)
# hardcode CMAKE_INSTALL_NAME_TOOL here to install_name_tool, so it behaves as it did before, Alex
if(NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
find_program(CMAKE_INSTALL_NAME_TOOL install_name_tool)
endif(NOT DEFINED CMAKE_INSTALL_NAME_TOOL)
# Setup iOS deployment target
set(IOS_DEPLOYMENT_TARGET ${IOS_DEPLOYMENT_TARGET} CACHE STRING "Minimum iOS version")
# Setup iOS developer location unless specified manually with CMAKE_IOS_DEVELOPER_ROOT
# Note Xcode 4.3 changed the installation location, choose the most recent one available
exec_program(/usr/bin/xcode-select ARGS -print-path OUTPUT_VARIABLE CMAKE_XCODE_DEVELOPER_DIR)
set(XCODE_POST_43_ROOT "${CMAKE_XCODE_DEVELOPER_DIR}/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
set(XCODE_PRE_43_ROOT "/Developer/Platforms/${IOS_PLATFORM_LOCATION}/Developer")
if(NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
if(EXISTS ${XCODE_POST_43_ROOT})
set(CMAKE_IOS_DEVELOPER_ROOT ${XCODE_POST_43_ROOT})
elseif(EXISTS ${XCODE_PRE_43_ROOT})
set(CMAKE_IOS_DEVELOPER_ROOT ${XCODE_PRE_43_ROOT})
endif(EXISTS ${XCODE_POST_43_ROOT})
endif(NOT DEFINED CMAKE_IOS_DEVELOPER_ROOT)
set(CMAKE_IOS_DEVELOPER_ROOT ${CMAKE_IOS_DEVELOPER_ROOT} CACHE PATH "Location of iOS Platform")
# Find and use the most recent iOS sdk unless specified manually with CMAKE_IOS_SDK_ROOT
if(NOT DEFINED CMAKE_IOS_SDK_ROOT)
file(GLOB _CMAKE_IOS_SDKS "${CMAKE_IOS_DEVELOPER_ROOT}/SDKs/*")
if(_CMAKE_IOS_SDKS)
list(SORT _CMAKE_IOS_SDKS)
list(REVERSE _CMAKE_IOS_SDKS)
list(GET _CMAKE_IOS_SDKS 0 CMAKE_IOS_SDK_ROOT)
else(_CMAKE_IOS_SDKS)
message(FATAL_ERROR "No iOS SDK's found in default search path ${CMAKE_IOS_DEVELOPER_ROOT}. Manually set CMAKE_IOS_SDK_ROOT or install the iOS SDK.")
endif(_CMAKE_IOS_SDKS)
message(STATUS "Toolchain using default iOS SDK: ${CMAKE_IOS_SDK_ROOT}")
endif(NOT DEFINED CMAKE_IOS_SDK_ROOT)
set(CMAKE_IOS_SDK_ROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Location of the selected iOS SDK")
# Set the sysroot default to the most recent SDK
set(CMAKE_OSX_SYSROOT ${CMAKE_IOS_SDK_ROOT} CACHE PATH "Sysroot used for iOS support")
# set the architecture for iOS
if(IOS_PLATFORM STREQUAL "OS")
set(DEFAULT_IOS_ARCH "arm64")
elseif(IOS_PLATFORM STREQUAL "SIMULATOR")
set(DEFAULT_IOS_ARCH "x86_64")
elseif(IOS_PLATFORM STREQUAL "WATCHOS")
set(DEFAULT_IOS_ARCH "armv7k;arm64_32")
endif()
set(IOS_ARCH ${DEFAULT_IOS_ARCH} CACHE STRING "Build architecture for iOS")
set(CMAKE_OSX_ARCHITECTURES ${IOS_ARCH} CACHE STRING "Build architecture for iOS")
# Set the find root to the iOS developer roots and to user defined paths
set(CMAKE_FIND_ROOT_PATH ${CMAKE_IOS_DEVELOPER_ROOT} ${CMAKE_IOS_SDK_ROOT} ${CMAKE_PREFIX_PATH} CACHE STRING "iOS find search path root")
# default to searching for frameworks first
set(CMAKE_FIND_FRAMEWORK FIRST)
# set up the default search directories for frameworks
set(CMAKE_SYSTEM_FRAMEWORK_PATH
${CMAKE_IOS_SDK_ROOT}/System/Library/Frameworks
${CMAKE_IOS_SDK_ROOT}/System/Library/PrivateFrameworks
${CMAKE_IOS_SDK_ROOT}/Developer/Library/Frameworks
)
# only search the iOS sdks, not the remainder of the host filesystem
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
# This little macro lets you set any XCode specific property
macro(set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE)
set_property(TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE})
endmacro(set_xcode_property)
# This macro lets you find executable programs on the host system
macro(find_host_package)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER)
set(IOS FALSE)
find_package(${ARGN})
set(IOS TRUE)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
endmacro(find_host_package)

View File

@ -0,0 +1,43 @@
# Minimal makefile for Sphinx documentation
#
ifneq ($(EXAMPLES_PATTERN),)
EXAMPLES_PATTERN_OPTS := -D sphinx_gallery_conf.filename_pattern="$(EXAMPLES_PATTERN)"
endif
# You can set these variables from the command line.
SPHINXOPTS = -W -j auto $(EXAMPLES_PATTERN_OPTS)
SPHINXBUILD = sphinx-build
SPHINXPROJ = torchvision
SOURCEDIR = source
BUILDDIR = build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
docset: html
doc2dash --name $(SPHINXPROJ) --icon $(SOURCEDIR)/_static/img/pytorch-logo-flame.png --enable-js --online-redirect-url http://pytorch.org/vision/ --force $(BUILDDIR)/html/
# Manually fix because Zeal doesn't deal well with `icon.png`-only at 2x resolution.
cp $(SPHINXPROJ).docset/icon.png $(SPHINXPROJ).docset/icon@2x.png
convert $(SPHINXPROJ).docset/icon@2x.png -resize 16x16 $(SPHINXPROJ).docset/icon.png
html-noplot: # Avoids running the gallery examples, which may take time
$(SPHINXBUILD) -D plot_gallery=0 -b html "${SOURCEDIR}" "$(BUILDDIR)"/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
clean:
rm -rf $(BUILDDIR)/*
rm -rf $(SOURCEDIR)/auto_examples/ # sphinx-gallery
rm -rf $(SOURCEDIR)/gen_modules/ # sphinx-gallery
rm -rf $(SOURCEDIR)/generated/ # autosummary
rm -rf $(SOURCEDIR)/models/generated # autosummary
.PHONY: help Makefile docset
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)

View File

@ -0,0 +1,36 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=source
set BUILDDIR=build
set SPHINXPROJ=torchvision
if "%1" == "" goto help
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.http://sphinx-doc.org/
exit /b 1
)
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS%
:end
popd

View File

@ -0,0 +1,8 @@
matplotlib
numpy
sphinx-copybutton>=0.3.1
sphinx-gallery>=0.11.1
sphinx==5.0.0
tabulate
-e git+https://github.com/pytorch/pytorch_sphinx_theme.git#egg=pytorch_sphinx_theme
pycocotools

View File

@ -0,0 +1,35 @@
/* This rule should be removed once
https://github.com/pytorch/pytorch_sphinx_theme/issues/125 is fixed.
We override the rule so that the links to the notebooks aren't hidden in the
gallery examples. pytorch_sphinx_theme is supposed to customize those links so
that they render nicely (look at the nice links on top of the tutorials
examples) but it doesn't work for repos that are not the tutorial repo, and in
torchvision it just hides the links. So we have to put them back here */
article.pytorch-article .sphx-glr-download-link-note.admonition.note,
article.pytorch-article .reference.download.internal, article.pytorch-article .sphx-glr-signature {
display: block;
}
/* These 2 rules below are for the weight tables (generated in conf.py) to look
* better. In particular we make their row height shorter */
.table-weights td, .table-weights th {
margin-bottom: 0.2rem;
padding: 0 !important;
line-height: 1 !important;
}
.table-weights p {
margin-bottom: 0.2rem !important;
}
/* Fix for Sphinx gallery 0.11
See https://github.com/sphinx-gallery/sphinx-gallery/issues/990
*/
article.pytorch-article .sphx-glr-thumbnails .sphx-glr-thumbcontainer {
width: unset;
margin-right: 0;
margin-left: 0;
}
article.pytorch-article div.section div.wy-table-responsive tbody td {
width: 50%;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 199.7 40.2" style="enable-background:new 0 0 199.7 40.2;" xml:space="preserve">
<style type="text/css">
.st0{fill:#F05732;}
.st1{fill:#9E529F;}
.st2{fill:#333333;}
</style>
<path class="st0" d="M102.7,12.2c-1.3-1-1.8,3.9-4.4,3.9c-3,0-4-13-6.3-13c-0.7,0-0.8-0.4-7.9,21.3c-2.9,9,4.4,15.8,11.8,15.8
c4.6,0,12.3-3,12.3-12.6C108.2,20.5,104.7,13.7,102.7,12.2z M95.8,35.3c-3.7,0-6.7-3.1-6.7-7c0-3.9,3-7,6.7-7s6.7,3.1,6.7,7
C102.5,32.1,99.5,35.3,95.8,35.3z"/>
<path class="st1" d="M99.8,0c-0.5,0-1.8,2.5-1.8,3.6c0,1.5,1,2,1.8,2c0.8,0,1.8-0.5,1.8-2C101.5,2.5,100.2,0,99.8,0z"/>
<path class="st2" d="M0,39.5V14.9h11.5c5.3,0,8.3,3.6,8.3,7.9c0,4.3-3,7.9-8.3,7.9H5.2v8.8H0z M14.4,22.8c0-2.1-1.6-3.3-3.7-3.3H5.2
v6.6h5.5C12.8,26.1,14.4,24.8,14.4,22.8z"/>
<path class="st2" d="M35.2,39.5V29.4l-9.4-14.5h6l6.1,9.8l6.1-9.8h5.9l-9.4,14.5v10.1H35.2z"/>
<path class="st2" d="M63.3,39.5v-20h-7.2v-4.6h19.6v4.6h-7.2v20H63.3z"/>
<path class="st2" d="M131.4,39.5l-4.8-8.7h-3.8v8.7h-5.2V14.9H129c5.1,0,8.3,3.4,8.3,7.9c0,4.3-2.8,6.7-5.4,7.3l5.6,9.4H131.4z
M131.9,22.8c0-2-1.6-3.3-3.7-3.3h-5.5v6.6h5.5C130.3,26.1,131.9,24.9,131.9,22.8z"/>
<path class="st2" d="M145.6,27.2c0-7.6,5.7-12.7,13.1-12.7c5.4,0,8.5,2.9,10.3,6l-4.5,2.2c-1-2-3.2-3.6-5.8-3.6
c-4.5,0-7.7,3.4-7.7,8.1c0,4.6,3.2,8.1,7.7,8.1c2.5,0,4.7-1.6,5.8-3.6l4.5,2.2c-1.7,3.1-4.9,6-10.3,6
C151.3,39.9,145.6,34.7,145.6,27.2z"/>
<path class="st2" d="M194.5,39.5V29.1h-11.6v10.4h-5.2V14.9h5.2v9.7h11.6v-9.7h5.3v24.6H194.5z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1010 B

View File

@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
height="40.200001"
width="40.200001"
xml:space="preserve"
viewBox="0 0 40.200002 40.2"
y="0px"
x="0px"
id="Layer_1"
version="1.1"><metadata
id="metadata4717"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs4715" /><style
id="style4694"
type="text/css">
.st0{fill:#F05732;}
.st1{fill:#9E529F;}
.st2{fill:#333333;}
</style><path
style="fill:#f05732"
id="path4696"
d="m 26.975479,12.199999 c -1.3,-1 -1.8,3.9 -4.4,3.9 -3,0 -4,-12.9999998 -6.3,-12.9999998 -0.7,0 -0.8,-0.4 -7.9000003,21.2999998 -2.9000001,9 4.4000003,15.8 11.8000003,15.8 4.6,0 12.3,-3 12.3,-12.6 0,-7.1 -3.5,-13.9 -5.5,-15.4 z m -6.9,23.1 c -3.7,0 -6.7,-3.1 -6.7,-7 0,-3.9 3,-7 6.7,-7 3.7,0 6.7,3.1 6.7,7 0,3.8 -3,7 -6.7,7 z"
class="st0" /><path
style="fill:#9e529f"
id="path4698"
d="m 24.075479,-7.6293945e-7 c -0.5,0 -1.8,2.49999996293945 -1.8,3.59999996293945 0,1.5 1,2 1.8,2 0.8,0 1.8,-0.5 1.8,-2 -0.1,-1.1 -1.4,-3.59999996293945 -1.8,-3.59999996293945 z"
class="st1" /></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,9 @@
.. role:: hidden
:class: hidden-section
.. currentmodule:: {{ module }}
{{ name | underline}}
.. autoclass:: {{ name }}
:members:

View File

@ -0,0 +1,12 @@
.. role:: hidden
:class: hidden-section
.. currentmodule:: {{ module }}
{{ name | underline}}
.. autoclass:: {{ name }}
:members:
__getitem__,
{% if "category_name" in methods %} category_name {% endif %}
:special-members:

View File

@ -0,0 +1,8 @@
.. role:: hidden
:class: hidden-section
.. currentmodule:: {{ module }}
{{ name | underline}}
.. autofunction:: {{ name }}

View File

@ -0,0 +1,8 @@
{% extends "!layout.html" %}
{% block sidebartitle %}
<div class="version">
<a href='https://pytorch.org/vision/versions.html'>{{ version }} &#x25BC</a>
</div>
{% include "searchbox.html" %}
{% endblock %}

View File

@ -0,0 +1,21 @@
from docutils import nodes
from docutils.parsers.rst import Directive
class BetaStatus(Directive):
has_content = True
text = "The {api_name} is in Beta stage, and backward compatibility is not guaranteed."
node = nodes.warning
def run(self):
text = self.text.format(api_name=" ".join(self.content))
return [self.node("", nodes.paragraph("", "", nodes.Text(text)))]
def setup(app):
app.add_directive("betastatus", BetaStatus)
return {
"version": "0.1",
"parallel_read_safe": True,
"parallel_write_safe": True,
}

View File

@ -0,0 +1,524 @@
#!/usr/bin/env python3
#
# PyTorch documentation build configuration file, created by
# sphinx-quickstart on Fri Dec 23 13:31:47 2016.
#
# This file is execfile()d with the current directory set to its
# containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
import os
import sys
import textwrap
from copy import copy
from pathlib import Path
import pytorch_sphinx_theme
import torchvision
import torchvision.models as M
from sphinx_gallery.sorting import ExplicitOrder
from tabulate import tabulate
sys.path.append(os.path.abspath("."))
# -- General configuration ------------------------------------------------
# Required version of sphinx is set from docs/requirements.txt
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
"sphinx.ext.autodoc",
"sphinx.ext.autosummary",
"sphinx.ext.doctest",
"sphinx.ext.intersphinx",
"sphinx.ext.todo",
"sphinx.ext.mathjax",
"sphinx.ext.napoleon",
"sphinx.ext.viewcode",
"sphinx.ext.duration",
"sphinx_gallery.gen_gallery",
"sphinx_copybutton",
"beta_status",
]
# We override sphinx-gallery's example header to prevent sphinx-gallery from
# creating a note at the top of the renderred notebook.
# https://github.com/sphinx-gallery/sphinx-gallery/blob/451ccba1007cc523f39cbcc960ebc21ca39f7b75/sphinx_gallery/gen_rst.py#L1267-L1271
# This is because we also want to add a link to google Colab, so we write our own note in each example.
from sphinx_gallery import gen_rst
gen_rst.EXAMPLE_HEADER = """
.. DO NOT EDIT.
.. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY.
.. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE:
.. "{0}"
.. LINE NUMBERS ARE GIVEN BELOW.
.. rst-class:: sphx-glr-example-title
.. _sphx_glr_{1}:
"""
class CustomGalleryExampleSortKey:
# See https://sphinx-gallery.github.io/stable/configuration.html#sorting-gallery-examples
# and https://github.com/sphinx-gallery/sphinx-gallery/blob/master/sphinx_gallery/sorting.py
def __init__(self, src_dir):
self.src_dir = src_dir
transforms_subsection_order = [
"plot_transforms_getting_started.py",
"plot_transforms_illustrations.py",
"plot_transforms_e2e.py",
"plot_cutmix_mixup.py",
"plot_custom_transforms.py",
"plot_tv_tensors.py",
"plot_custom_tv_tensors.py",
]
def __call__(self, filename):
if "gallery/transforms" in self.src_dir:
try:
return self.transforms_subsection_order.index(filename)
except ValueError as e:
raise ValueError(
"Looks like you added an example in gallery/transforms? "
"You need to specify its order in docs/source/conf.py. Look for CustomGalleryExampleSortKey."
) from e
else:
# For other subsections we just sort alphabetically by filename
return filename
sphinx_gallery_conf = {
"examples_dirs": "../../gallery/", # path to your example scripts
"gallery_dirs": "auto_examples", # path to where to save gallery generated output
"subsection_order": ExplicitOrder(["../../gallery/transforms", "../../gallery/others"]),
"backreferences_dir": "gen_modules/backreferences",
"doc_module": ("torchvision",),
"remove_config_comments": True,
"ignore_pattern": "helpers.py",
"within_subsection_order": CustomGalleryExampleSortKey,
}
napoleon_use_ivar = True
napoleon_numpy_docstring = False
napoleon_google_docstring = True
# Add any paths that contain templates here, relative to this directory.
templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
source_suffix = {
".rst": "restructuredtext",
}
# The master toctree document.
master_doc = "index"
# General information about the project.
project = "Torchvision"
copyright = "2017-present, Torch Contributors"
author = "Torch Contributors"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
# version: The short X.Y version.
# release: The full version, including alpha/beta/rc tags.
if os.environ.get("TORCHVISION_SANITIZE_VERSION_STR_IN_DOCS", None):
# Turn 1.11.0aHASH into 1.11 (major.minor only)
version = release = ".".join(torchvision.__version__.split(".")[:2])
html_title = " ".join((project, version, "documentation"))
else:
version = f"main ({torchvision.__version__})"
release = "main"
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This patterns also effect to html_static_path and html_extra_path
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = "sphinx"
# If true, `todo` and `todoList` produce output, else they produce nothing.
todo_include_todos = True
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = "pytorch_sphinx_theme"
html_theme_path = [pytorch_sphinx_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
"collapse_navigation": False,
"display_version": True,
"logo_only": True,
"pytorch_project": "docs",
"navigation_with_keys": True,
"analytics_id": "GTM-T8XT4PS",
}
html_logo = "_static/img/pytorch-logo-dark.svg"
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
# TODO: remove this once https://github.com/pytorch/pytorch_sphinx_theme/issues/125 is fixed
html_css_files = [
"css/custom_torchvision.css",
]
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = "PyTorchdoc"
autosummary_generate = True
# -- Options for LaTeX output ---------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, "pytorch.tex", "torchvision Documentation", "Torch Contributors", "manual"),
]
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [(master_doc, "torchvision", "torchvision Documentation", [author], 1)]
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(
master_doc,
"torchvision",
"torchvision Documentation",
author,
"torchvision",
"One line description of project.",
"Miscellaneous",
),
]
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {
"python": ("https://docs.python.org/3/", None),
"torch": ("https://pytorch.org/docs/stable/", None),
"numpy": ("https://numpy.org/doc/stable/", None),
"PIL": ("https://pillow.readthedocs.io/en/stable/", None),
"matplotlib": ("https://matplotlib.org/stable/", None),
}
# -- A patch that prevents Sphinx from cross-referencing ivar tags -------
# See http://stackoverflow.com/a/41184353/3343043
from docutils import nodes
from sphinx import addnodes
from sphinx.util.docfields import TypedField
def patched_make_field(self, types, domain, items, **kw):
# `kw` catches `env=None` needed for newer sphinx while maintaining
# backwards compatibility when passed along further down!
# type: (list, unicode, tuple) -> nodes.field # noqa: F821
def handle_item(fieldarg, content):
par = nodes.paragraph()
par += addnodes.literal_strong("", fieldarg) # Patch: this line added
# par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
# addnodes.literal_strong))
if fieldarg in types:
par += nodes.Text(" (")
# NOTE: using .pop() here to prevent a single type node to be
# inserted twice into the doctree, which leads to
# inconsistencies later when references are resolved
fieldtype = types.pop(fieldarg)
if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
typename = "".join(n.astext() for n in fieldtype)
typename = typename.replace("int", "python:int")
typename = typename.replace("long", "python:long")
typename = typename.replace("float", "python:float")
typename = typename.replace("type", "python:type")
par.extend(self.make_xrefs(self.typerolename, domain, typename, addnodes.literal_emphasis, **kw))
else:
par += fieldtype
par += nodes.Text(")")
par += nodes.Text(" -- ")
par += content
return par
fieldname = nodes.field_name("", self.label)
if len(items) == 1 and self.can_collapse:
fieldarg, content = items[0]
bodynode = handle_item(fieldarg, content)
else:
bodynode = self.list_type()
for fieldarg, content in items:
bodynode += nodes.list_item("", handle_item(fieldarg, content))
fieldbody = nodes.field_body("", bodynode)
return nodes.field("", fieldname, fieldbody)
TypedField.make_field = patched_make_field
def inject_minigalleries(app, what, name, obj, options, lines):
"""Inject a minigallery into a docstring.
This avoids having to manually write the .. minigallery directive for every item we want a minigallery for,
as it would be easy to miss some.
This callback is called after the .. auto directives (like ..autoclass) have been processed,
and modifies the lines parameter inplace to add the .. minigallery that will show which examples
are using which object.
It's a bit hacky, but not *that* hacky when you consider that the recommended way is to do pretty much the same,
but instead with templates using autosummary (which we don't want to use):
(https://sphinx-gallery.github.io/stable/configuration.html#auto-documenting-your-api-with-links-to-examples)
For docs on autodoc-process-docstring, see the autodoc docs:
https://www.sphinx-doc.org/en/master/usage/extensions/autodoc.html
"""
if what in ("class", "function"):
lines.append(f".. minigallery:: {name}")
lines.append(f" :add-heading: Examples using ``{name.split('.')[-1]}``:")
# avoid heading entirely to avoid warning. As a bonud it actually renders better
lines.append(" :heading-level: 9")
lines.append("\n")
def inject_weight_metadata(app, what, name, obj, options, lines):
"""This hook is used to generate docs for the models weights.
Objects like ResNet18_Weights are enums with fields, where each field is a Weight object.
Enums aren't easily documented in Python so the solution we're going for is to:
- add an autoclass directive in the model's builder docstring, e.g.
```
.. autoclass:: torchvision.models.ResNet34_Weights
:members:
```
(see resnet.py for an example)
- then this hook is called automatically when building the docs, and it generates the text that gets
used within the autoclass directive.
"""
if getattr(obj, "__name__", "").endswith(("_Weights", "_QuantizedWeights")):
if len(obj) == 0:
lines[:] = ["There are no available pre-trained weights."]
return
lines[:] = [
"The model builder above accepts the following values as the ``weights`` parameter.",
f"``{obj.__name__}.DEFAULT`` is equivalent to ``{obj.DEFAULT}``. You can also use strings, e.g. "
f"``weights='DEFAULT'`` or ``weights='{str(list(obj)[0]).split('.')[1]}'``.",
]
if obj.__doc__ is not None and obj.__doc__ != "An enumeration.":
# We only show the custom enum doc if it was overridden. The default one from Python is "An enumeration"
lines.append("")
lines.append(obj.__doc__)
lines.append("")
for field in obj:
meta = copy(field.meta)
lines += [f"**{str(field)}**:", ""]
lines += [meta.pop("_docs")]
if field == obj.DEFAULT:
lines += [f"Also available as ``{obj.__name__}.DEFAULT``."]
lines += [""]
table = []
metrics = meta.pop("_metrics")
for dataset, dataset_metrics in metrics.items():
for metric_name, metric_value in dataset_metrics.items():
table.append((f"{metric_name} (on {dataset})", str(metric_value)))
for k, v in meta.items():
if k in {"recipe", "license"}:
v = f"`link <{v}>`__"
elif k == "min_size":
v = f"height={v[0]}, width={v[1]}"
elif k in {"categories", "keypoint_names"} and isinstance(v, list):
max_visible = 3
v_sample = ", ".join(v[:max_visible])
v = f"{v_sample}, ... ({len(v)-max_visible} omitted)" if len(v) > max_visible else v_sample
elif k == "_ops":
v = f"{v:.2f}"
k = "GIPS" if obj.__name__.endswith("_QuantizedWeights") else "GFLOPS"
elif k == "_file_size":
k = "File size"
v = f"{v:.1f} MB"
table.append((str(k), str(v)))
table = tabulate(table, tablefmt="rst")
lines += [".. rst-class:: table-weights"] # Custom CSS class, see custom_torchvision.css
lines += [".. table::", ""]
lines += textwrap.indent(table, " " * 4).split("\n")
lines.append("")
lines.append(
f"The inference transforms are available at ``{str(field)}.transforms`` and "
f"perform the following preprocessing operations: {field.transforms().describe()}"
)
lines.append("")
def generate_weights_table(module, table_name, metrics, dataset, include_patterns=None, exclude_patterns=None):
weights_endswith = "_QuantizedWeights" if module.__name__.split(".")[-1] == "quantization" else "_Weights"
weight_enums = [getattr(module, name) for name in dir(module) if name.endswith(weights_endswith)]
weights = [w for weight_enum in weight_enums for w in weight_enum]
if include_patterns is not None:
weights = [w for w in weights if any(p in str(w) for p in include_patterns)]
if exclude_patterns is not None:
weights = [w for w in weights if all(p not in str(w) for p in exclude_patterns)]
ops_name = "GIPS" if "QuantizedWeights" in weights_endswith else "GFLOPS"
metrics_keys, metrics_names = zip(*metrics)
column_names = ["Weight"] + list(metrics_names) + ["Params"] + [ops_name, "Recipe"] # Final column order
column_names = [f"**{name}**" for name in column_names] # Add bold
content = []
for w in weights:
row = [
f":class:`{w} <{type(w).__name__}>`",
*(w.meta["_metrics"][dataset][metric] for metric in metrics_keys),
f"{w.meta['num_params']/1e6:.1f}M",
f"{w.meta['_ops']:.2f}",
f"`link <{w.meta['recipe']}>`__",
]
content.append(row)
column_widths = ["110"] + ["18"] * len(metrics_names) + ["18"] * 2 + ["10"]
widths_table = " ".join(column_widths)
table = tabulate(content, headers=column_names, tablefmt="rst")
generated_dir = Path("generated")
generated_dir.mkdir(exist_ok=True)
with open(generated_dir / f"{table_name}_table.rst", "w+") as table_file:
table_file.write(".. rst-class:: table-weights\n") # Custom CSS class, see custom_torchvision.css
table_file.write(".. table::\n")
table_file.write(f" :widths: {widths_table} \n\n")
table_file.write(f"{textwrap.indent(table, ' ' * 4)}\n\n")
generate_weights_table(
module=M, table_name="classification", metrics=[("acc@1", "Acc@1"), ("acc@5", "Acc@5")], dataset="ImageNet-1K"
)
generate_weights_table(
module=M.quantization,
table_name="classification_quant",
metrics=[("acc@1", "Acc@1"), ("acc@5", "Acc@5")],
dataset="ImageNet-1K",
)
generate_weights_table(
module=M.detection,
table_name="detection",
metrics=[("box_map", "Box MAP")],
exclude_patterns=["Mask", "Keypoint"],
dataset="COCO-val2017",
)
generate_weights_table(
module=M.detection,
table_name="instance_segmentation",
metrics=[("box_map", "Box MAP"), ("mask_map", "Mask MAP")],
dataset="COCO-val2017",
include_patterns=["Mask"],
)
generate_weights_table(
module=M.detection,
table_name="detection_keypoint",
metrics=[("box_map", "Box MAP"), ("kp_map", "Keypoint MAP")],
dataset="COCO-val2017",
include_patterns=["Keypoint"],
)
generate_weights_table(
module=M.segmentation,
table_name="segmentation",
metrics=[("miou", "Mean IoU"), ("pixel_acc", "pixelwise Acc")],
dataset="COCO-val2017-VOC-labels",
)
generate_weights_table(
module=M.video, table_name="video", metrics=[("acc@1", "Acc@1"), ("acc@5", "Acc@5")], dataset="Kinetics-400"
)
def setup(app):
app.connect("autodoc-process-docstring", inject_minigalleries)
app.connect("autodoc-process-docstring", inject_weight_metadata)

View File

@ -0,0 +1,192 @@
.. _datasets:
Datasets
========
Torchvision provides many built-in datasets in the ``torchvision.datasets``
module, as well as utility classes for building your own datasets.
Built-in datasets
-----------------
All datasets are subclasses of :class:`torch.utils.data.Dataset`
i.e, they have ``__getitem__`` and ``__len__`` methods implemented.
Hence, they can all be passed to a :class:`torch.utils.data.DataLoader`
which can load multiple samples in parallel using ``torch.multiprocessing`` workers.
For example: ::
imagenet_data = torchvision.datasets.ImageNet('path/to/imagenet_root/')
data_loader = torch.utils.data.DataLoader(imagenet_data,
batch_size=4,
shuffle=True,
num_workers=args.nThreads)
.. currentmodule:: torchvision.datasets
All the datasets have almost similar API. They all have two common arguments:
``transform`` and ``target_transform`` to transform the input and target respectively.
You can also create your own datasets using the provided :ref:`base classes <base_classes_datasets>`.
.. warning::
When a dataset object is created with ``download=True``, the files are first
downloaded and extracted in the root directory. This download logic is not
multi-process safe, so it may lead to conflicts / race conditions if it is
run within a distributed setting. In distributed mode, we recommend creating
a dummy dataset object to trigger the download logic *before* setting up
distributed mode.
Image classification
~~~~~~~~~~~~~~~~~~~~
.. autosummary::
:toctree: generated/
:template: class_dataset.rst
Caltech101
Caltech256
CelebA
CIFAR10
CIFAR100
Country211
DTD
EMNIST
EuroSAT
FakeData
FashionMNIST
FER2013
FGVCAircraft
Flickr8k
Flickr30k
Flowers102
Food101
GTSRB
INaturalist
ImageNet
Imagenette
KMNIST
LFWPeople
LSUN
MNIST
Omniglot
OxfordIIITPet
Places365
PCAM
QMNIST
RenderedSST2
SEMEION
SBU
StanfordCars
STL10
SUN397
SVHN
USPS
Image detection or segmentation
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autosummary::
:toctree: generated/
:template: class_dataset.rst
CocoDetection
CelebA
Cityscapes
Kitti
OxfordIIITPet
SBDataset
VOCSegmentation
VOCDetection
WIDERFace
Optical Flow
~~~~~~~~~~~~
.. autosummary::
:toctree: generated/
:template: class_dataset.rst
FlyingChairs
FlyingThings3D
HD1K
KittiFlow
Sintel
Stereo Matching
~~~~~~~~~~~~~~~
.. autosummary::
:toctree: generated/
:template: class_dataset.rst
CarlaStereo
Kitti2012Stereo
Kitti2015Stereo
CREStereo
FallingThingsStereo
SceneFlowStereo
SintelStereo
InStereo2k
ETH3DStereo
Middlebury2014Stereo
Image pairs
~~~~~~~~~~~
.. autosummary::
:toctree: generated/
:template: class_dataset.rst
LFWPairs
PhotoTour
Image captioning
~~~~~~~~~~~~~~~~
.. autosummary::
:toctree: generated/
:template: class_dataset.rst
CocoCaptions
Video classification
~~~~~~~~~~~~~~~~~~~~
.. autosummary::
:toctree: generated/
:template: class_dataset.rst
HMDB51
Kinetics
UCF101
Video prediction
~~~~~~~~~~~~~~~~~~~~
.. autosummary::
:toctree: generated/
:template: class_dataset.rst
MovingMNIST
.. _base_classes_datasets:
Base classes for custom datasets
--------------------------------
.. autosummary::
:toctree: generated/
:template: class.rst
DatasetFolder
ImageFolder
VisionDataset
Transforms v2
-------------
.. autosummary::
:toctree: generated/
:template: function.rst
wrap_dataset_for_transforms_v2

View File

@ -0,0 +1,3 @@
# Necessary for the table generated by autosummary to look decent
[html writers]
table_style: colwidths-auto

View File

@ -0,0 +1,166 @@
Feature extraction for model inspection
=======================================
.. currentmodule:: torchvision.models.feature_extraction
The ``torchvision.models.feature_extraction`` package contains
feature extraction utilities that let us tap into our models to access intermediate
transformations of our inputs. This could be useful for a variety of
applications in computer vision. Just a few examples are:
- Visualizing feature maps.
- Extracting features to compute image descriptors for tasks like facial
recognition, copy-detection, or image retrieval.
- Passing selected features to downstream sub-networks for end-to-end training
with a specific task in mind. For example, passing a hierarchy of features
to a Feature Pyramid Network with object detection heads.
Torchvision provides :func:`create_feature_extractor` for this purpose.
It works by following roughly these steps:
1. Symbolically tracing the model to get a graphical representation of
how it transforms the input, step by step.
2. Setting the user-selected graph nodes as outputs.
3. Removing all redundant nodes (anything downstream of the output nodes).
4. Generating python code from the resulting graph and bundling that into a
PyTorch module together with the graph itself.
|
The `torch.fx documentation <https://pytorch.org/docs/stable/fx.html>`_
provides a more general and detailed explanation of the above procedure and
the inner workings of the symbolic tracing.
.. _about-node-names:
**About Node Names**
In order to specify which nodes should be output nodes for extracted
features, one should be familiar with the node naming convention used here
(which differs slightly from that used in ``torch.fx``). A node name is
specified as a ``.`` separated path walking the module hierarchy from top level
module down to leaf operation or leaf module. For instance ``"layer4.2.relu"``
in ResNet-50 represents the output of the ReLU of the 2nd block of the 4th
layer of the ``ResNet`` module. Here are some finer points to keep in mind:
- When specifying node names for :func:`create_feature_extractor`, you may
provide a truncated version of a node name as a shortcut. To see how this
works, try creating a ResNet-50 model and printing the node names with
``train_nodes, _ = get_graph_node_names(model) print(train_nodes)`` and
observe that the last node pertaining to ``layer4`` is
``"layer4.2.relu_2"``. One may specify ``"layer4.2.relu_2"`` as the return
node, or just ``"layer4"`` as this, by convention, refers to the last node
(in order of execution) of ``layer4``.
- If a certain module or operation is repeated more than once, node names get
an additional ``_{int}`` postfix to disambiguate. For instance, maybe the
addition (``+``) operation is used three times in the same ``forward``
method. Then there would be ``"path.to.module.add"``,
``"path.to.module.add_1"``, ``"path.to.module.add_2"``. The counter is
maintained within the scope of the direct parent. So in ResNet-50 there is
a ``"layer4.1.add"`` and a ``"layer4.2.add"``. Because the addition
operations reside in different blocks, there is no need for a postfix to
disambiguate.
**An Example**
Here is an example of how we might extract features for MaskRCNN:
.. code-block:: python
import torch
from torchvision.models import resnet50
from torchvision.models.feature_extraction import get_graph_node_names
from torchvision.models.feature_extraction import create_feature_extractor
from torchvision.models.detection.mask_rcnn import MaskRCNN
from torchvision.models.detection.backbone_utils import LastLevelMaxPool
from torchvision.ops.feature_pyramid_network import FeaturePyramidNetwork
# To assist you in designing the feature extractor you may want to print out
# the available nodes for resnet50.
m = resnet50()
train_nodes, eval_nodes = get_graph_node_names(resnet50())
# The lists returned, are the names of all the graph nodes (in order of
# execution) for the input model traced in train mode and in eval mode
# respectively. You'll find that `train_nodes` and `eval_nodes` are the same
# for this example. But if the model contains control flow that's dependent
# on the training mode, they may be different.
# To specify the nodes you want to extract, you could select the final node
# that appears in each of the main layers:
return_nodes = {
# node_name: user-specified key for output dict
'layer1.2.relu_2': 'layer1',
'layer2.3.relu_2': 'layer2',
'layer3.5.relu_2': 'layer3',
'layer4.2.relu_2': 'layer4',
}
# But `create_feature_extractor` can also accept truncated node specifications
# like "layer1", as it will just pick the last node that's a descendent of
# of the specification. (Tip: be careful with this, especially when a layer
# has multiple outputs. It's not always guaranteed that the last operation
# performed is the one that corresponds to the output you desire. You should
# consult the source code for the input model to confirm.)
return_nodes = {
'layer1': 'layer1',
'layer2': 'layer2',
'layer3': 'layer3',
'layer4': 'layer4',
}
# Now you can build the feature extractor. This returns a module whose forward
# method returns a dictionary like:
# {
# 'layer1': output of layer 1,
# 'layer2': output of layer 2,
# 'layer3': output of layer 3,
# 'layer4': output of layer 4,
# }
create_feature_extractor(m, return_nodes=return_nodes)
# Let's put all that together to wrap resnet50 with MaskRCNN
# MaskRCNN requires a backbone with an attached FPN
class Resnet50WithFPN(torch.nn.Module):
def __init__(self):
super(Resnet50WithFPN, self).__init__()
# Get a resnet50 backbone
m = resnet50()
# Extract 4 main layers (note: MaskRCNN needs this particular name
# mapping for return nodes)
self.body = create_feature_extractor(
m, return_nodes={f'layer{k}': str(v)
for v, k in enumerate([1, 2, 3, 4])})
# Dry run to get number of channels for FPN
inp = torch.randn(2, 3, 224, 224)
with torch.no_grad():
out = self.body(inp)
in_channels_list = [o.shape[1] for o in out.values()]
# Build FPN
self.out_channels = 256
self.fpn = FeaturePyramidNetwork(
in_channels_list, out_channels=self.out_channels,
extra_blocks=LastLevelMaxPool())
def forward(self, x):
x = self.body(x)
x = self.fpn(x)
return x
# Now we can build our model!
model = MaskRCNN(Resnet50WithFPN(), num_classes=91).eval()
API Reference
-------------
.. autosummary::
:toctree: generated/
:template: function.rst
create_feature_extractor
get_graph_node_names

View File

@ -0,0 +1,69 @@
torchvision
===========
This library is part of the `PyTorch
<http://pytorch.org/>`_ project. PyTorch is an open source
machine learning framework.
Features described in this documentation are classified by release status:
*Stable:* These features will be maintained long-term and there should generally
be no major performance limitations or gaps in documentation.
We also expect to maintain backwards compatibility (although
breaking changes can happen and notice will be given one release ahead
of time).
*Beta:* Features are tagged as Beta because the API may change based on
user feedback, because the performance needs to improve, or because
coverage across operators is not yet complete. For Beta features, we are
committing to seeing the feature through to the Stable classification.
We are not, however, committing to backwards compatibility.
*Prototype:* These features are typically not available as part of
binary distributions like PyPI or Conda, except sometimes behind run-time
flags, and are at an early stage for feedback and testing.
The :mod:`torchvision` package consists of popular datasets, model
architectures, and common image transformations for computer vision.
.. toctree::
:maxdepth: 2
:caption: Package Reference
transforms
tv_tensors
models
datasets
utils
ops
io
feature_extraction
.. toctree::
:maxdepth: 1
:caption: Examples and training references
auto_examples/index
training_references
.. automodule:: torchvision
:members:
.. toctree::
:maxdepth: 1
:caption: PyTorch Libraries
PyTorch <https://pytorch.org/docs>
torchaudio <https://pytorch.org/audio>
torchtext <https://pytorch.org/text>
torchvision <https://pytorch.org/vision>
TorchElastic <https://pytorch.org/elastic/>
TorchServe <https://pytorch.org/serve>
PyTorch on XLA Devices <http://pytorch.org/xla/>
Indices
-------
* :ref:`genindex`

View File

@ -0,0 +1,119 @@
Decoding / Encoding images and videos
=====================================
.. currentmodule:: torchvision.io
The :mod:`torchvision.io` module provides utilities for decoding and encoding
images and videos.
Image Decoding
--------------
Torchvision currently supports decoding JPEG, PNG, WEBP, GIF, AVIF, and HEIC
images. JPEG decoding can also be done on CUDA GPUs.
The main entry point is the :func:`~torchvision.io.decode_image` function, which
you can use as an alternative to ``PIL.Image.open()``. It will decode images
straight into image Tensors, thus saving you the conversion and allowing you to
run transforms/preproc natively on tensors.
.. code::
from torchvision.io import decode_image
img = decode_image("path_to_image", mode="RGB")
img.dtype # torch.uint8
# Or
raw_encoded_bytes = ... # read encoded bytes from your file system
img = decode_image(raw_encoded_bytes, mode="RGB")
:func:`~torchvision.io.decode_image` will automatically detect the image format,
and call the corresponding decoder (except for HEIC and AVIF images, see details
in :func:`~torchvision.io.decode_avif` and :func:`~torchvision.io.decode_heic`).
You can also use the lower-level format-specific decoders which can be more
powerful, e.g. if you want to encode/decode JPEGs on CUDA.
.. autosummary::
:toctree: generated/
:template: function.rst
decode_image
decode_jpeg
decode_png
decode_webp
decode_avif
decode_heic
decode_gif
.. autosummary::
:toctree: generated/
:template: class.rst
ImageReadMode
Obsolete decoding function:
.. autosummary::
:toctree: generated/
:template: function.rst
read_image
Image Encoding
--------------
For encoding, JPEG (cpu and CUDA) and PNG are supported.
.. autosummary::
:toctree: generated/
:template: function.rst
encode_jpeg
write_jpeg
encode_png
write_png
IO operations
-------------
.. autosummary::
:toctree: generated/
:template: function.rst
read_file
write_file
Video - DEPREACTED
------------------
.. warning::
DEPRECATED: All the video decoding and encoding capabilities of torchvision
are deprecated from version 0.22 and will be removed in version 0.24. We
recommend that you migrate to
`TorchCodec <https://github.com/pytorch/torchcodec>`__, where we'll
consolidate the future decoding/encoding capabilities of PyTorch
.. autosummary::
:toctree: generated/
:template: function.rst
read_video
read_video_timestamps
write_video
**Fine-grained video API**
In addition to the :mod:`read_video` function, we provide a high-performance
lower-level API for more fine-grained control compared to the :mod:`read_video` function.
It does all this whilst fully supporting torchscript.
.. autosummary::
:toctree: generated/
:template: class.rst
VideoReader

View File

@ -0,0 +1,577 @@
.. _models:
Models and pre-trained weights
##############################
The ``torchvision.models`` subpackage contains definitions of models for addressing
different tasks, including: image classification, pixelwise semantic
segmentation, object detection, instance segmentation, person
keypoint detection, video classification, and optical flow.
General information on pre-trained weights
==========================================
TorchVision offers pre-trained weights for every provided architecture, using
the PyTorch :mod:`torch.hub`. Instancing a pre-trained model will download its
weights to a cache directory. This directory can be set using the `TORCH_HOME`
environment variable. See :func:`torch.hub.load_state_dict_from_url` for details.
.. note::
The pre-trained models provided in this library may have their own licenses or
terms and conditions derived from the dataset used for training. It is your
responsibility to determine whether you have permission to use the models for
your use case.
.. note ::
Backward compatibility is guaranteed for loading a serialized
``state_dict`` to the model created using old PyTorch version.
On the contrary, loading entire saved models or serialized
``ScriptModules`` (serialized using older versions of PyTorch)
may not preserve the historic behaviour. Refer to the following
`documentation
<https://pytorch.org/docs/stable/notes/serialization.html#id6>`_
Initializing pre-trained models
-------------------------------
As of v0.13, TorchVision offers a new `Multi-weight support API
<https://pytorch.org/blog/introducing-torchvision-new-multi-weight-support-api/>`_
for loading different weights to the existing model builder methods:
.. code:: python
from torchvision.models import resnet50, ResNet50_Weights
# Old weights with accuracy 76.130%
resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)
# New weights with accuracy 80.858%
resnet50(weights=ResNet50_Weights.IMAGENET1K_V2)
# Best available weights (currently alias for IMAGENET1K_V2)
# Note that these weights may change across versions
resnet50(weights=ResNet50_Weights.DEFAULT)
# Strings are also supported
resnet50(weights="IMAGENET1K_V2")
# No weights - random initialization
resnet50(weights=None)
Migrating to the new API is very straightforward. The following method calls between the 2 APIs are all equivalent:
.. code:: python
from torchvision.models import resnet50, ResNet50_Weights
# Using pretrained weights:
resnet50(weights=ResNet50_Weights.IMAGENET1K_V1)
resnet50(weights="IMAGENET1K_V1")
resnet50(pretrained=True) # deprecated
resnet50(True) # deprecated
# Using no weights:
resnet50(weights=None)
resnet50()
resnet50(pretrained=False) # deprecated
resnet50(False) # deprecated
Note that the ``pretrained`` parameter is now deprecated, using it will emit warnings and will be removed on v0.15.
Using the pre-trained models
----------------------------
Before using the pre-trained models, one must preprocess the image
(resize with right resolution/interpolation, apply inference transforms,
rescale the values etc). There is no standard way to do this as it depends on
how a given model was trained. It can vary across model families, variants or
even weight versions. Using the correct preprocessing method is critical and
failing to do so may lead to decreased accuracy or incorrect outputs.
All the necessary information for the inference transforms of each pre-trained
model is provided on its weights documentation. To simplify inference, TorchVision
bundles the necessary preprocessing transforms into each model weight. These are
accessible via the ``weight.transforms`` attribute:
.. code:: python
# Initialize the Weight Transforms
weights = ResNet50_Weights.DEFAULT
preprocess = weights.transforms()
# Apply it to the input image
img_transformed = preprocess(img)
Some models use modules which have different training and evaluation
behavior, such as batch normalization. To switch between these modes, use
``model.train()`` or ``model.eval()`` as appropriate. See
:meth:`~torch.nn.Module.train` or :meth:`~torch.nn.Module.eval` for details.
.. code:: python
# Initialize model
weights = ResNet50_Weights.DEFAULT
model = resnet50(weights=weights)
# Set model to eval mode
model.eval()
Listing and retrieving available models
---------------------------------------
As of v0.14, TorchVision offers a new mechanism which allows listing and
retrieving models and weights by their names. Here are a few examples on how to
use them:
.. code:: python
# List available models
all_models = list_models()
classification_models = list_models(module=torchvision.models)
# Initialize models
m1 = get_model("mobilenet_v3_large", weights=None)
m2 = get_model("quantized_mobilenet_v3_large", weights="DEFAULT")
# Fetch weights
weights = get_weight("MobileNet_V3_Large_QuantizedWeights.DEFAULT")
assert weights == MobileNet_V3_Large_QuantizedWeights.DEFAULT
weights_enum = get_model_weights("quantized_mobilenet_v3_large")
assert weights_enum == MobileNet_V3_Large_QuantizedWeights
weights_enum2 = get_model_weights(torchvision.models.quantization.mobilenet_v3_large)
assert weights_enum == weights_enum2
Here are the available public functions to retrieve models and their corresponding weights:
.. currentmodule:: torchvision.models
.. autosummary::
:toctree: generated/
:template: function.rst
get_model
get_model_weights
get_weight
list_models
Using models from Hub
---------------------
Most pre-trained models can be accessed directly via PyTorch Hub without having TorchVision installed:
.. code:: python
import torch
# Option 1: passing weights param as string
model = torch.hub.load("pytorch/vision", "resnet50", weights="IMAGENET1K_V2")
# Option 2: passing weights param as enum
weights = torch.hub.load(
"pytorch/vision",
"get_weight",
weights="ResNet50_Weights.IMAGENET1K_V2",
)
model = torch.hub.load("pytorch/vision", "resnet50", weights=weights)
You can also retrieve all the available weights of a specific model via PyTorch Hub by doing:
.. code:: python
import torch
weight_enum = torch.hub.load("pytorch/vision", "get_model_weights", name="resnet50")
print([weight for weight in weight_enum])
The only exception to the above are the detection models included on
:mod:`torchvision.models.detection`. These models require TorchVision
to be installed because they depend on custom C++ operators.
Classification
==============
.. currentmodule:: torchvision.models
The following classification models are available, with or without pre-trained
weights:
.. toctree::
:maxdepth: 1
models/alexnet
models/convnext
models/densenet
models/efficientnet
models/efficientnetv2
models/googlenet
models/inception
models/maxvit
models/mnasnet
models/mobilenetv2
models/mobilenetv3
models/regnet
models/resnet
models/resnext
models/shufflenetv2
models/squeezenet
models/swin_transformer
models/vgg
models/vision_transformer
models/wide_resnet
|
Here is an example of how to use the pre-trained image classification models:
.. code:: python
from torchvision.io import decode_image
from torchvision.models import resnet50, ResNet50_Weights
img = decode_image("test/assets/encode_jpeg/grace_hopper_517x606.jpg")
# Step 1: Initialize model with the best available weights
weights = ResNet50_Weights.DEFAULT
model = resnet50(weights=weights)
model.eval()
# Step 2: Initialize the inference transforms
preprocess = weights.transforms()
# Step 3: Apply inference preprocessing transforms
batch = preprocess(img).unsqueeze(0)
# Step 4: Use the model and print the predicted category
prediction = model(batch).squeeze(0).softmax(0)
class_id = prediction.argmax().item()
score = prediction[class_id].item()
category_name = weights.meta["categories"][class_id]
print(f"{category_name}: {100 * score:.1f}%")
The classes of the pre-trained model outputs can be found at ``weights.meta["categories"]``.
Table of all available classification weights
---------------------------------------------
Accuracies are reported on ImageNet-1K using single crops:
.. include:: generated/classification_table.rst
Quantized models
----------------
.. currentmodule:: torchvision.models.quantization
The following architectures provide support for INT8 quantized models, with or without
pre-trained weights:
.. toctree::
:maxdepth: 1
models/googlenet_quant
models/inception_quant
models/mobilenetv2_quant
models/mobilenetv3_quant
models/resnet_quant
models/resnext_quant
models/shufflenetv2_quant
|
Here is an example of how to use the pre-trained quantized image classification models:
.. code:: python
from torchvision.io import decode_image
from torchvision.models.quantization import resnet50, ResNet50_QuantizedWeights
img = decode_image("test/assets/encode_jpeg/grace_hopper_517x606.jpg")
# Step 1: Initialize model with the best available weights
weights = ResNet50_QuantizedWeights.DEFAULT
model = resnet50(weights=weights, quantize=True)
model.eval()
# Step 2: Initialize the inference transforms
preprocess = weights.transforms()
# Step 3: Apply inference preprocessing transforms
batch = preprocess(img).unsqueeze(0)
# Step 4: Use the model and print the predicted category
prediction = model(batch).squeeze(0).softmax(0)
class_id = prediction.argmax().item()
score = prediction[class_id].item()
category_name = weights.meta["categories"][class_id]
print(f"{category_name}: {100 * score}%")
The classes of the pre-trained model outputs can be found at ``weights.meta["categories"]``.
Table of all available quantized classification weights
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Accuracies are reported on ImageNet-1K using single crops:
.. include:: generated/classification_quant_table.rst
Semantic Segmentation
=====================
.. currentmodule:: torchvision.models.segmentation
.. betastatus:: segmentation module
The following semantic segmentation models are available, with or without
pre-trained weights:
.. toctree::
:maxdepth: 1
models/deeplabv3
models/fcn
models/lraspp
|
Here is an example of how to use the pre-trained semantic segmentation models:
.. code:: python
from torchvision.io.image import decode_image
from torchvision.models.segmentation import fcn_resnet50, FCN_ResNet50_Weights
from torchvision.transforms.functional import to_pil_image
img = decode_image("gallery/assets/dog1.jpg")
# Step 1: Initialize model with the best available weights
weights = FCN_ResNet50_Weights.DEFAULT
model = fcn_resnet50(weights=weights)
model.eval()
# Step 2: Initialize the inference transforms
preprocess = weights.transforms()
# Step 3: Apply inference preprocessing transforms
batch = preprocess(img).unsqueeze(0)
# Step 4: Use the model and visualize the prediction
prediction = model(batch)["out"]
normalized_masks = prediction.softmax(dim=1)
class_to_idx = {cls: idx for (idx, cls) in enumerate(weights.meta["categories"])}
mask = normalized_masks[0, class_to_idx["dog"]]
to_pil_image(mask).show()
The classes of the pre-trained model outputs can be found at ``weights.meta["categories"]``.
The output format of the models is illustrated in :ref:`semantic_seg_output`.
Table of all available semantic segmentation weights
----------------------------------------------------
All models are evaluated a subset of COCO val2017, on the 20 categories that are present in the Pascal VOC dataset:
.. include:: generated/segmentation_table.rst
.. _object_det_inst_seg_pers_keypoint_det:
Object Detection, Instance Segmentation and Person Keypoint Detection
=====================================================================
The pre-trained models for detection, instance segmentation and
keypoint detection are initialized with the classification models
in torchvision. The models expect a list of ``Tensor[C, H, W]``.
Check the constructor of the models for more information.
.. betastatus:: detection module
Object Detection
----------------
.. currentmodule:: torchvision.models.detection
The following object detection models are available, with or without pre-trained
weights:
.. toctree::
:maxdepth: 1
models/faster_rcnn
models/fcos
models/retinanet
models/ssd
models/ssdlite
|
Here is an example of how to use the pre-trained object detection models:
.. code:: python
from torchvision.io.image import decode_image
from torchvision.models.detection import fasterrcnn_resnet50_fpn_v2, FasterRCNN_ResNet50_FPN_V2_Weights
from torchvision.utils import draw_bounding_boxes
from torchvision.transforms.functional import to_pil_image
img = decode_image("test/assets/encode_jpeg/grace_hopper_517x606.jpg")
# Step 1: Initialize model with the best available weights
weights = FasterRCNN_ResNet50_FPN_V2_Weights.DEFAULT
model = fasterrcnn_resnet50_fpn_v2(weights=weights, box_score_thresh=0.9)
model.eval()
# Step 2: Initialize the inference transforms
preprocess = weights.transforms()
# Step 3: Apply inference preprocessing transforms
batch = [preprocess(img)]
# Step 4: Use the model and visualize the prediction
prediction = model(batch)[0]
labels = [weights.meta["categories"][i] for i in prediction["labels"]]
box = draw_bounding_boxes(img, boxes=prediction["boxes"],
labels=labels,
colors="red",
width=4, font_size=30)
im = to_pil_image(box.detach())
im.show()
The classes of the pre-trained model outputs can be found at ``weights.meta["categories"]``.
For details on how to plot the bounding boxes of the models, you may refer to :ref:`instance_seg_output`.
Table of all available Object detection weights
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Box MAPs are reported on COCO val2017:
.. include:: generated/detection_table.rst
Instance Segmentation
---------------------
.. currentmodule:: torchvision.models.detection
The following instance segmentation models are available, with or without pre-trained
weights:
.. toctree::
:maxdepth: 1
models/mask_rcnn
|
For details on how to plot the masks of the models, you may refer to :ref:`instance_seg_output`.
Table of all available Instance segmentation weights
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Box and Mask MAPs are reported on COCO val2017:
.. include:: generated/instance_segmentation_table.rst
Keypoint Detection
------------------
.. currentmodule:: torchvision.models.detection
The following person keypoint detection models are available, with or without
pre-trained weights:
.. toctree::
:maxdepth: 1
models/keypoint_rcnn
|
The classes of the pre-trained model outputs can be found at ``weights.meta["keypoint_names"]``.
For details on how to plot the bounding boxes of the models, you may refer to :ref:`keypoint_output`.
Table of all available Keypoint detection weights
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Box and Keypoint MAPs are reported on COCO val2017:
.. include:: generated/detection_keypoint_table.rst
Video Classification
====================
.. currentmodule:: torchvision.models.video
.. betastatus:: video module
The following video classification models are available, with or without
pre-trained weights:
.. toctree::
:maxdepth: 1
models/video_mvit
models/video_resnet
models/video_s3d
models/video_swin_transformer
|
Here is an example of how to use the pre-trained video classification models:
.. code:: python
from torchvision.io.video import read_video
from torchvision.models.video import r3d_18, R3D_18_Weights
vid, _, _ = read_video("test/assets/videos/v_SoccerJuggling_g23_c01.avi", output_format="TCHW")
vid = vid[:32] # optionally shorten duration
# Step 1: Initialize model with the best available weights
weights = R3D_18_Weights.DEFAULT
model = r3d_18(weights=weights)
model.eval()
# Step 2: Initialize the inference transforms
preprocess = weights.transforms()
# Step 3: Apply inference preprocessing transforms
batch = preprocess(vid).unsqueeze(0)
# Step 4: Use the model and print the predicted category
prediction = model(batch).squeeze(0).softmax(0)
label = prediction.argmax().item()
score = prediction[label].item()
category_name = weights.meta["categories"][label]
print(f"{category_name}: {100 * score}%")
The classes of the pre-trained model outputs can be found at ``weights.meta["categories"]``.
Table of all available video classification weights
---------------------------------------------------
Accuracies are reported on Kinetics-400 using single crops for clip length 16:
.. include:: generated/video_table.rst
Optical Flow
============
.. currentmodule:: torchvision.models.optical_flow
The following Optical Flow models are available, with or without pre-trained
.. toctree::
:maxdepth: 1
models/raft

View File

@ -0,0 +1,28 @@
AlexNet
=======
.. currentmodule:: torchvision.models
The AlexNet model was originally introduced in the
`ImageNet Classification with Deep Convolutional Neural Networks
<https://papers.nips.cc/paper/2012/hash/c399862d3b9d6b76c8436e924a68c45b-Abstract.html>`__
paper. The implemented architecture is slightly different from the original one,
and is based on `One weird trick for parallelizing convolutional neural networks
<https://arxiv.org/abs/1404.5997>`__.
Model builders
--------------
The following model builders can be used to instantiate an AlexNet model, with or
without pre-trained weights. All the model builders internally rely on the
``torchvision.models.alexnet.AlexNet`` base class. Please refer to the `source
code
<https://github.com/pytorch/vision/blob/main/torchvision/models/alexnet.py>`_ for
more details about this class.
.. autosummary::
:toctree: generated/
:template: function.rst
alexnet

View File

@ -0,0 +1,26 @@
ConvNeXt
========
.. currentmodule:: torchvision.models
The ConvNeXt model is based on the `A ConvNet for the 2020s
<https://arxiv.org/abs/2201.03545>`_ paper.
Model builders
--------------
The following model builders can be used to instantiate a ConvNeXt model, with or
without pre-trained weights. All the model builders internally rely on the
``torchvision.models.convnext.ConvNeXt`` base class. Please refer to the `source code
<https://github.com/pytorch/vision/blob/main/torchvision/models/convnext.py>`_ for
more details about this class.
.. autosummary::
:toctree: generated/
:template: function.rst
convnext_tiny
convnext_small
convnext_base
convnext_large

View File

@ -0,0 +1,28 @@
DeepLabV3
=========
.. currentmodule:: torchvision.models.segmentation
The DeepLabV3 model is based on the `Rethinking Atrous Convolution for Semantic
Image Segmentation <https://arxiv.org/abs/1706.05587>`__ paper.
.. betastatus:: segmentation module
Model builders
--------------
The following model builders can be used to instantiate a DeepLabV3 model with
different backbones, with or without pre-trained weights. All the model builders
internally rely on the ``torchvision.models.segmentation.deeplabv3.DeepLabV3`` base class. Please
refer to the `source code
<https://github.com/pytorch/vision/blob/main/torchvision/models/segmentation/deeplabv3.py>`_
for more details about this class.
.. autosummary::
:toctree: generated/
:template: function.rst
deeplabv3_mobilenet_v3_large
deeplabv3_resnet50
deeplabv3_resnet101

View File

@ -0,0 +1,27 @@
DenseNet
========
.. currentmodule:: torchvision.models
The DenseNet model is based on the `Densely Connected Convolutional Networks
<https://arxiv.org/abs/1608.06993>`_ paper.
Model builders
--------------
The following model builders can be used to instantiate a DenseNet model, with or
without pre-trained weights. All the model builders internally rely on the
``torchvision.models.densenet.DenseNet`` base class. Please refer to the `source
code
<https://github.com/pytorch/vision/blob/main/torchvision/models/densenet.py>`_ for
more details about this class.
.. autosummary::
:toctree: generated/
:template: function.rst
densenet121
densenet161
densenet169
densenet201

View File

@ -0,0 +1,31 @@
EfficientNet
============
.. currentmodule:: torchvision.models
The EfficientNet model is based on the `EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks <https://arxiv.org/abs/1905.11946>`__
paper.
Model builders
--------------
The following model builders can be used to instantiate an EfficientNet model, with or
without pre-trained weights. All the model builders internally rely on the
``torchvision.models.efficientnet.EfficientNet`` base class. Please refer to the `source
code
<https://github.com/pytorch/vision/blob/main/torchvision/models/efficientnet.py>`_ for
more details about this class.
.. autosummary::
:toctree: generated/
:template: function.rst
efficientnet_b0
efficientnet_b1
efficientnet_b2
efficientnet_b3
efficientnet_b4
efficientnet_b5
efficientnet_b6
efficientnet_b7

View File

@ -0,0 +1,26 @@
EfficientNetV2
==============
.. currentmodule:: torchvision.models
The EfficientNetV2 model is based on the `EfficientNetV2: Smaller Models and Faster Training <https://arxiv.org/abs/2104.00298>`__
paper.
Model builders
--------------
The following model builders can be used to instantiate an EfficientNetV2 model, with or
without pre-trained weights. All the model builders internally rely on the
``torchvision.models.efficientnet.EfficientNet`` base class. Please refer to the `source
code
<https://github.com/pytorch/vision/blob/main/torchvision/models/efficientnet.py>`_ for
more details about this class.
.. autosummary::
:toctree: generated/
:template: function.rst
efficientnet_v2_s
efficientnet_v2_m
efficientnet_v2_l

Some files were not shown because too many files have changed in this diff Show More