Skip to content

Commit b11a6a5

Browse files
authored
Merge branch 'main' into doc_order
2 parents 778fd70 + 8ba482a commit b11a6a5

28 files changed

+1090
-84
lines changed

.circleci/unittest/linux/scripts/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies:
99
- libpng
1010
- jpeg
1111
- ca-certificates
12+
- h5py
1213
- pip:
1314
- future
1415
- pillow >=5.3.0, !=8.3.*

.circleci/unittest/windows/scripts/environment.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ dependencies:
99
- libpng
1010
- jpeg
1111
- ca-certificates
12+
- h5py
1213
- pip:
1314
- future
1415
- pillow >=5.3.0, !=8.3.*

CMakeLists.txt

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ set(CMAKE_CXX_STANDARD 14)
44
file(STRINGS version.txt TORCHVISION_VERSION)
55

66
option(WITH_CUDA "Enable CUDA support" OFF)
7+
option(USE_PYTHON "Link to Python when building" OFF)
78

89
if(WITH_CUDA)
910
enable_language(CUDA)
@@ -17,7 +18,10 @@ if(WITH_CUDA)
1718
endif()
1819
endif()
1920

20-
find_package(Python3 COMPONENTS Development)
21+
if (USE_PYTHON)
22+
add_definitions(-DUSE_PYTHON)
23+
find_package(Python3 REQUIRED COMPONENTS Development)
24+
endif()
2125

2226
find_package(Torch REQUIRED)
2327
find_package(PNG REQUIRED)
@@ -76,7 +80,12 @@ FOREACH(DIR ${ALLOW_LISTED})
7680
ENDFOREACH()
7781

7882
add_library(${PROJECT_NAME} SHARED ${ALL_SOURCES})
79-
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} ${PNG_LIBRARY} ${JPEG_LIBRARIES} Python3::Python)
83+
target_link_libraries(${PROJECT_NAME} PRIVATE ${TORCH_LIBRARIES} ${PNG_LIBRARY} ${JPEG_LIBRARIES})
84+
85+
if (USE_PYTHON)
86+
target_link_libraries(${PROJECT_NAME} PRIVATE Python3::Python)
87+
endif()
88+
8089
set_target_properties(${PROJECT_NAME} PROPERTIES
8190
EXPORT_NAME TorchVision
8291
INSTALL_RPATH ${TORCH_INSTALL_PREFIX}/lib)

README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ so make sure that it is also available to cmake via the ``CMAKE_PREFIX_PATH``.
157157

158158
For an example setup, take a look at ``examples/cpp/hello_world``.
159159

160+
Python linking is disabled by default when compiling TorchVision with CMake, this allows you to run models without any Python
161+
dependency. In some special cases where TorchVision's operators are used from Python code, you may need to link to Python. This
162+
can be done by passing ``-DUSE_PYTHON=on`` to CMake.
163+
160164
TorchVision Operators
161165
---------------------
162166
In order to get the torchvision operators registered with torch (eg. for the JIT), all you need to do is to ensure that you

cmake/TorchVisionConfig.cmake.in

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ include("${CMAKE_CURRENT_LIST_DIR}/${PN}Targets.cmake")
2828
if(NOT TARGET torch_library)
2929
find_package(Torch REQUIRED)
3030
endif()
31-
if(NOT TARGET Python3::Python)
32-
find_package(Python3 COMPONENTS Development)
31+
if (@USE_PYTHON@)
32+
if(NOT TARGET Python3::Python)
33+
find_package(Python3 COMPONENTS Development)
34+
endif()
3335
endif()
3436

3537
set_target_properties(TorchVision::TorchVision PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${${PN}_INCLUDE_DIR}" INTERFACE_LINK_LIBRARIES "torch;Python3::Python" )

docs/source/datasets.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ You can also create your own datasets using the provided :ref:`base classes <bas
4141
Country211
4242
DTD
4343
EMNIST
44+
EuroSAT
4445
FakeData
4546
FashionMNIST
4647
FER2013
@@ -50,6 +51,7 @@ You can also create your own datasets using the provided :ref:`base classes <bas
5051
FlyingChairs
5152
FlyingThings3D
5253
Food101
54+
FGVCAircraft
5355
GTSRB
5456
HD1K
5557
HMDB51
@@ -65,13 +67,15 @@ You can also create your own datasets using the provided :ref:`base classes <bas
6567
MNIST
6668
Omniglot
6769
OxfordIIITPet
70+
PCAM
6871
PhotoTour
6972
Places365
7073
QMNIST
7174
SBDataset
7275
SBU
7376
SEMEION
7477
Sintel
78+
StanfordCars
7579
STL10
7680
SUN397
7781
SVHN

examples/cpp/hello_world/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ project(hello-world)
66
# so there is no need to also add `find_package(Torch)` here.
77
find_package(TorchVision REQUIRED)
88

9+
# This due to LibTorch's version is the one included in the Python
10+
# package that links to Python.
11+
find_package(Python3 COMPONENTS Development)
12+
913
add_executable(hello-world main.cpp)
1014

1115
# We now need to link the TorchVision library to our executable.

setup.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ def get_extensions():
201201

202202
if sys.platform == "win32":
203203
define_macros += [("torchvision_EXPORTS", None)]
204-
204+
define_macros += [("USE_PYTHON", None)]
205205
extra_compile_args["cxx"].append("/MP")
206206

207207
debug_mode = os.getenv("DEBUG", "0") == "1"
@@ -254,6 +254,9 @@ def get_extensions():
254254
image_library = []
255255
image_link_flags = []
256256

257+
if sys.platform == "win32":
258+
image_macros += [("USE_PYTHON", None)]
259+
257260
# Locating libPNG
258261
libpng = distutils.spawn.find_executable("libpng-config")
259262
pngfix = distutils.spawn.find_executable("pngfix")

test/datasets_utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ class LazyImporter:
6161
"requests",
6262
"scipy.io",
6363
"scipy.sparse",
64+
"h5py",
6465
)
6566

6667
def __init__(self):

test/test_datasets.py

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2169,6 +2169,27 @@ def inject_fake_data(self, tmpdir, config):
21692169
return num_sequences * (num_examples_per_sequence - 1)
21702170

21712171

2172+
class EuroSATTestCase(datasets_utils.ImageDatasetTestCase):
2173+
DATASET_CLASS = datasets.EuroSAT
2174+
FEATURE_TYPES = (PIL.Image.Image, int)
2175+
2176+
def inject_fake_data(self, tmpdir, config):
2177+
data_folder = os.path.join(tmpdir, "eurosat", "2750")
2178+
os.makedirs(data_folder)
2179+
2180+
num_examples_per_class = 3
2181+
classes = ("AnnualCrop", "Forest")
2182+
for cls in classes:
2183+
datasets_utils.create_image_folder(
2184+
root=data_folder,
2185+
name=cls,
2186+
file_name_fn=lambda idx: f"{cls}_{idx}.jpg",
2187+
num_examples=num_examples_per_class,
2188+
)
2189+
2190+
return len(classes) * num_examples_per_class
2191+
2192+
21722193
class Food101TestCase(datasets_utils.ImageDatasetTestCase):
21732194
DATASET_CLASS = datasets.Food101
21742195
FEATURE_TYPES = (PIL.Image.Image, int)
@@ -2206,6 +2227,57 @@ def inject_fake_data(self, tmpdir: str, config):
22062227
return len(sampled_classes * n_samples_per_class)
22072228

22082229

2230+
class FGVCAircraftTestCase(datasets_utils.ImageDatasetTestCase):
2231+
DATASET_CLASS = datasets.FGVCAircraft
2232+
ADDITIONAL_CONFIGS = datasets_utils.combinations_grid(
2233+
split=("train", "val", "trainval", "test"), annotation_level=("variant", "family", "manufacturer")
2234+
)
2235+
2236+
def inject_fake_data(self, tmpdir: str, config):
2237+
split = config["split"]
2238+
annotation_level = config["annotation_level"]
2239+
annotation_level_to_file = {
2240+
"variant": "variants.txt",
2241+
"family": "families.txt",
2242+
"manufacturer": "manufacturers.txt",
2243+
}
2244+
2245+
root_folder = pathlib.Path(tmpdir) / "fgvc-aircraft-2013b"
2246+
data_folder = root_folder / "data"
2247+
2248+
classes = ["707-320", "Hawk T1", "Tornado"]
2249+
num_images_per_class = 5
2250+
2251+
datasets_utils.create_image_folder(
2252+
data_folder,
2253+
"images",
2254+
file_name_fn=lambda idx: f"{idx}.jpg",
2255+
num_examples=num_images_per_class * len(classes),
2256+
)
2257+
2258+
annotation_file = data_folder / annotation_level_to_file[annotation_level]
2259+
with open(annotation_file, "w") as file:
2260+
file.write("\n".join(classes))
2261+
2262+
num_samples_per_class = 4 if split == "trainval" else 2
2263+
images_classes = []
2264+
for i in range(len(classes)):
2265+
images_classes.extend(
2266+
[
2267+
f"{idx} {classes[i]}"
2268+
for idx in random.sample(
2269+
range(i * num_images_per_class, (i + 1) * num_images_per_class), num_samples_per_class
2270+
)
2271+
]
2272+
)
2273+
2274+
images_annotation_file = data_folder / f"images_{annotation_level}_{split}.txt"
2275+
with open(images_annotation_file, "w") as file:
2276+
file.write("\n".join(images_classes))
2277+
2278+
return len(classes * num_samples_per_class)
2279+
2280+
22092281
class SUN397TestCase(datasets_utils.ImageDatasetTestCase):
22102282
DATASET_CLASS = datasets.SUN397
22112283

@@ -2463,6 +2535,50 @@ def _meta_to_split_and_classification_ann(self, meta, idx):
24632535
return (image_id, class_id, species, breed_id)
24642536

24652537

2538+
class StanfordCarsTestCase(datasets_utils.ImageDatasetTestCase):
2539+
DATASET_CLASS = datasets.StanfordCars
2540+
REQUIRED_PACKAGES = ("scipy",)
2541+
ADDITIONAL_CONFIGS = datasets_utils.combinations_grid(split=("train", "test"))
2542+
2543+
def inject_fake_data(self, tmpdir, config):
2544+
import scipy.io as io
2545+
from numpy.core.records import fromarrays
2546+
2547+
num_examples = {"train": 5, "test": 7}[config["split"]]
2548+
num_classes = 3
2549+
base_folder = pathlib.Path(tmpdir) / "stanford_cars"
2550+
2551+
devkit = base_folder / "devkit"
2552+
devkit.mkdir(parents=True)
2553+
2554+
if config["split"] == "train":
2555+
images_folder_name = "cars_train"
2556+
annotations_mat_path = devkit / "cars_train_annos.mat"
2557+
else:
2558+
images_folder_name = "cars_test"
2559+
annotations_mat_path = base_folder / "cars_test_annos_withlabels.mat"
2560+
2561+
datasets_utils.create_image_folder(
2562+
root=base_folder,
2563+
name=images_folder_name,
2564+
file_name_fn=lambda image_index: f"{image_index:5d}.jpg",
2565+
num_examples=num_examples,
2566+
)
2567+
2568+
classes = np.random.randint(1, num_classes + 1, num_examples, dtype=np.uint8)
2569+
fnames = [f"{i:5d}.jpg" for i in range(num_examples)]
2570+
rec_array = fromarrays(
2571+
[classes, fnames],
2572+
names=["class", "fname"],
2573+
)
2574+
io.savemat(annotations_mat_path, {"annotations": rec_array})
2575+
2576+
random_class_names = ["random_name"] * num_classes
2577+
io.savemat(devkit / "cars_meta.mat", {"class_names": random_class_names})
2578+
2579+
return num_examples
2580+
2581+
24662582
class Country211TestCase(datasets_utils.ImageDatasetTestCase):
24672583
DATASET_CLASS = datasets.Country211
24682584

@@ -2526,5 +2642,28 @@ def inject_fake_data(self, tmpdir: str, config):
25262642
return num_images_per_split[config["split"]]
25272643

25282644

2645+
class PCAMTestCase(datasets_utils.ImageDatasetTestCase):
2646+
DATASET_CLASS = datasets.PCAM
2647+
2648+
ADDITIONAL_CONFIGS = datasets_utils.combinations_grid(split=("train", "val", "test"))
2649+
REQUIRED_PACKAGES = ("h5py",)
2650+
2651+
def inject_fake_data(self, tmpdir: str, config):
2652+
base_folder = pathlib.Path(tmpdir) / "pcam"
2653+
base_folder.mkdir()
2654+
2655+
num_images = {"train": 2, "test": 3, "val": 4}[config["split"]]
2656+
2657+
images_file = datasets.PCAM._FILES[config["split"]]["images"][0]
2658+
with datasets_utils.lazy_importer.h5py.File(str(base_folder / images_file), "w") as f:
2659+
f["x"] = np.random.randint(0, 256, size=(num_images, 10, 10, 3), dtype=np.uint8)
2660+
2661+
targets_file = datasets.PCAM._FILES[config["split"]]["targets"][0]
2662+
with datasets_utils.lazy_importer.h5py.File(str(base_folder / targets_file), "w") as f:
2663+
f["y"] = np.random.randint(0, 2, size=(num_images, 1, 1, 1), dtype=np.uint8)
2664+
2665+
return num_images
2666+
2667+
25292668
if __name__ == "__main__":
25302669
unittest.main()

0 commit comments

Comments
 (0)