lsun.py 5.55 KB
Newer Older
1
import io
soumith's avatar
soumith committed
2
3
import os
import os.path
4
import pickle
soumith's avatar
soumith committed
5
import string
6
from collections.abc import Iterable
7
from typing import Any, Callable, cast, List, Optional, Tuple, Union
8
9
10

from PIL import Image

11
from .utils import verify_str_arg, iterable_to_str
12
from .vision import VisionDataset
13

14

15
class LSUNClass(VisionDataset):
16
    def __init__(
17
        self, root: str, transform: Optional[Callable] = None, target_transform: Optional[Callable] = None
18
    ) -> None:
soumith's avatar
soumith committed
19
        import lmdb
Jason Park's avatar
Jason Park committed
20

21
        super().__init__(root, transform=transform, target_transform=target_transform)
22
23

        self.env = lmdb.open(root, max_readers=1, readonly=True, lock=False, readahead=False, meminit=False)
soumith's avatar
soumith committed
24
        with self.env.begin(write=False) as txn:
25
26
            self.length = txn.stat()["entries"]
        cache_file = "_cache_" + "".join(c for c in root if c in string.ascii_letters)
soumith's avatar
soumith committed
27
        if os.path.isfile(cache_file):
28
            self.keys = pickle.load(open(cache_file, "rb"))
soumith's avatar
soumith committed
29
30
        else:
            with self.env.begin(write=False) as txn:
31
                self.keys = [key for key in txn.cursor().iternext(keys=True, values=False)]
32
            pickle.dump(self.keys, open(cache_file, "wb"))
soumith's avatar
soumith committed
33

34
    def __getitem__(self, index: int) -> Tuple[Any, Any]:
soumith's avatar
soumith committed
35
36
37
38
39
        img, target = None, None
        env = self.env
        with env.begin(write=False) as txn:
            imgbuf = txn.get(self.keys[index])

Philip Meier's avatar
Philip Meier committed
40
        buf = io.BytesIO()
soumith's avatar
soumith committed
41
42
        buf.write(imgbuf)
        buf.seek(0)
43
        img = Image.open(buf).convert("RGB")
soumith's avatar
soumith committed
44
45
46
47
48
49
50
51
52

        if self.transform is not None:
            img = self.transform(img)

        if self.target_transform is not None:
            target = self.target_transform(target)

        return img, target

53
    def __len__(self) -> int:
soumith's avatar
soumith committed
54
55
        return self.length

56

57
class LSUN(VisionDataset):
Nicolas Hug's avatar
Nicolas Hug committed
58
59
60
61
    """`LSUN <https://www.yf.io/p/lsun>`_ dataset.

    You will need to install the ``lmdb`` package to use this dataset: run
    ``pip install lmdb``
62
63

    Args:
Jason Park's avatar
Jason Park committed
64
        root (string): Root directory for the database files.
65
        classes (string or list): One of {'train', 'val', 'test'} or a list of
66
            categories to load. e,g. ['bedroom_train', 'church_outdoor_train'].
67
68
69
70
        transform (callable, optional): A function/transform that  takes in an PIL image
            and returns a transformed version. E.g, ``transforms.RandomCrop``
        target_transform (callable, optional): A function/transform that takes in the
            target and transforms it.
soumith's avatar
soumith committed
71
    """
72

73
    def __init__(
74
75
76
77
78
        self,
        root: str,
        classes: Union[str, List[str]] = "train",
        transform: Optional[Callable] = None,
        target_transform: Optional[Callable] = None,
79
    ) -> None:
80
        super().__init__(root, transform=transform, target_transform=target_transform)
81
82
83
84
85
        self.classes = self._verify_classes(classes)

        # for each class, create an LSUNClassDataset
        self.dbs = []
        for c in self.classes:
86
            self.dbs.append(LSUNClass(root=os.path.join(root, f"{c}_lmdb"), transform=transform))
87
88
89
90
91
92
93
94
95

        self.indices = []
        count = 0
        for db in self.dbs:
            count += len(db)
            self.indices.append(count)

        self.length = count

96
    def _verify_classes(self, classes: Union[str, List[str]]) -> List[str]:
97
98
99
100
101
102
103
104
105
106
107
108
109
        categories = [
            "bedroom",
            "bridge",
            "church_outdoor",
            "classroom",
            "conference_room",
            "dining_room",
            "kitchen",
            "living_room",
            "restaurant",
            "tower",
        ]
        dset_opts = ["train", "val", "test"]
Jason Park's avatar
Jason Park committed
110

111
        try:
112
            classes = cast(str, classes)
113
            verify_str_arg(classes, "classes", dset_opts)
114
            if classes == "test":
soumith's avatar
soumith committed
115
116
                classes = [classes]
            else:
117
                classes = [c + "_" + classes for c in categories]
118
        except ValueError:
119
            if not isinstance(classes, Iterable):
120
                msg = "Expected type str or Iterable for argument classes, but got type {}."
121
122
123
                raise ValueError(msg.format(type(classes)))

            classes = list(classes)
124
            msg_fmtstr_type = "Expected type str for elements in argument classes, but got type {}."
soumith's avatar
soumith committed
125
            for c in classes:
126
                verify_str_arg(c, custom_msg=msg_fmtstr_type.format(type(c)))
127
128
                c_short = c.split("_")
                category, dset_opt = "_".join(c_short[:-1]), c_short[-1]
129

130
                msg_fmtstr = "Unknown value '{}' for {}. Valid values are {{{}}}."
131
                msg = msg_fmtstr.format(category, "LSUN class", iterable_to_str(categories))
132
133
134
135
                verify_str_arg(category, valid_values=categories, custom_msg=msg)

                msg = msg_fmtstr.format(dset_opt, "postfix", iterable_to_str(dset_opts))
                verify_str_arg(dset_opt, valid_values=dset_opts, custom_msg=msg)
soumith's avatar
soumith committed
136

137
        return classes
soumith's avatar
soumith committed
138

139
    def __getitem__(self, index: int) -> Tuple[Any, Any]:
140
141
142
143
144
145
146
        """
        Args:
            index (int): Index

        Returns:
            tuple: Tuple (image, target) where target is the index of the target category.
        """
soumith's avatar
soumith committed
147
148
149
150
151
152
        target = 0
        sub = 0
        for ind in self.indices:
            if index < ind:
                break
            target += 1
Zhou Le's avatar
Zhou Le committed
153
            sub = ind
soumith's avatar
soumith committed
154
155
156
157
158
159
160

        db = self.dbs[target]
        index = index - sub

        if self.target_transform is not None:
            target = self.target_transform(target)

soumith's avatar
soumith committed
161
162
        img, _ = db[index]
        return img, target
soumith's avatar
soumith committed
163

164
    def __len__(self) -> int:
soumith's avatar
soumith committed
165
166
        return self.length

167
    def extra_repr(self) -> str:
168
        return "Classes: {classes}".format(**self.__dict__)