Addax-Data-Science commited on
Commit
8f97b66
·
verified ·
1 Parent(s): 59eb79e

Upload 2 files

Browse files
Files changed (2) hide show
  1. inference.py +237 -0
  2. taxonomy.csv +142 -0
inference.py ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ ModelInference for the addax-sppnet model family.
3
+
4
+ Architecture: SpeciesNet GraphModule backbone (frozen) + a thin nn.Linear
5
+ head fine-tuned per region. Originally written for AddaxAI's legacy
6
+ classify_detections.py (Peter van Lunteren, 13 May 2025); ported here to
7
+ the WebUI's class-based ModelInference interface.
8
+
9
+ Files expected in the model directory:
10
+ - <model_fname>.pt fine-tuned head checkpoint, e.g. final-20260317.pt
11
+ - <backbone>.pt frozen SpeciesNet backbone, one of:
12
+ - always_crop_99710272_22x8_v12_epoch_00148.pt
13
+ - full_image_88545560_22x8_v12_epoch_00153.pt
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ # Allow loading checkpoints saved on a Windows runner on a POSIX machine.
19
+ import pathlib
20
+ import platform
21
+ from pathlib import Path
22
+
23
+ import numpy as np
24
+ import torch
25
+ import torch.nn as nn
26
+ import torch.nn.functional as F
27
+ from PIL import Image
28
+ from torchvision import transforms
29
+
30
+ if platform.system() != "Windows":
31
+ pathlib.WindowsPath = pathlib.PosixPath # type: ignore[assignment]
32
+
33
+ # Don't fail on truncated images during inference.
34
+ from PIL import ImageFile
35
+
36
+ ImageFile.LOAD_TRUNCATED_IMAGES = True
37
+
38
+
39
+ _BACKBONE_FILENAMES = (
40
+ "always_crop_99710272_22x8_v12_epoch_00148.pt",
41
+ "full_image_88545560_22x8_v12_epoch_00153.pt",
42
+ )
43
+
44
+
45
+ def _load_fx_checkpoint(weights_path: Path, map_location: str = "cpu") -> nn.Module:
46
+ """Load a SpeciesNet onnx2torch GraphModule.
47
+
48
+ The backbone is shipped as a torch.fx GraphModule. PyTorch 2.4+
49
+ requires `reduce_graph_module` to be in the safe-globals allowlist
50
+ when loading with `weights_only=True`; older versions don't have
51
+ this concept. Try both paths.
52
+ """
53
+ try:
54
+ from torch.fx.graph_module import reduce_graph_module
55
+ from torch.serialization import add_safe_globals
56
+ add_safe_globals([reduce_graph_module])
57
+ except Exception:
58
+ pass
59
+
60
+ try:
61
+ obj = torch.load(weights_path, map_location=map_location, weights_only=True)
62
+ except Exception:
63
+ obj = torch.load(weights_path, map_location=map_location, weights_only=False)
64
+
65
+ if hasattr(obj, "state_dict") and hasattr(obj, "forward"):
66
+ return obj
67
+ raise ValueError(f"{weights_path} is not a torch.nn.Module GraphModule")
68
+
69
+
70
+ class _FXClassifier(nn.Module):
71
+ """SpeciesNet backbone (frozen) + linear head."""
72
+
73
+ def __init__(
74
+ self,
75
+ backbone: nn.Module,
76
+ num_classes: int,
77
+ img_size: int = 480,
78
+ input_layout: str = "nhwc",
79
+ ) -> None:
80
+ super().__init__()
81
+ self.backbone = backbone
82
+ self.input_layout = input_layout.lower()
83
+
84
+ for p in self.backbone.parameters():
85
+ p.requires_grad = False
86
+ self.backbone.eval()
87
+
88
+ # Probe the backbone to discover output feature size at this
89
+ # img_size + layout combo, so the head matches exactly.
90
+ with torch.no_grad():
91
+ x = torch.zeros(1, 3, img_size, img_size)
92
+ if self.input_layout == "nhwc":
93
+ x = x.permute(0, 2, 3, 1).contiguous()
94
+ z = self.backbone(x)
95
+ z = self._pool(z)
96
+ in_features = z.shape[1]
97
+
98
+ self.head = nn.Linear(in_features, num_classes)
99
+
100
+ @staticmethod
101
+ def _pool(z: torch.Tensor) -> torch.Tensor:
102
+ if z.ndim == 4:
103
+ return F.adaptive_avg_pool2d(z, 1).flatten(1)
104
+ if z.ndim == 3:
105
+ return z.mean(dim=1)
106
+ return z.flatten(1)
107
+
108
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
109
+ if self.input_layout == "nhwc":
110
+ x = x.permute(0, 2, 3, 1).contiguous()
111
+ z = self.backbone(x)
112
+ z = self._pool(z)
113
+ return self.head(z)
114
+
115
+
116
+ class ModelInference:
117
+ """ModelInference for the addax-sppnet family (SpeciesNet backbone + linear head)."""
118
+
119
+ def __init__(self, model_dir: Path, model_path: Path) -> None:
120
+ self.model_dir = Path(model_dir)
121
+ self.model_path = Path(model_path)
122
+ self.model: _FXClassifier | None = None
123
+ self.device: torch.device | None = None
124
+ self._class_names: list[str] = []
125
+ self._preprocess: transforms.Compose | None = None
126
+
127
+ # ------------------------------------------------------------------
128
+ # Required interface
129
+ # ------------------------------------------------------------------
130
+
131
+ def check_gpu(self) -> bool:
132
+ try:
133
+ if torch.backends.mps.is_built() and torch.backends.mps.is_available():
134
+ return True
135
+ except Exception:
136
+ pass
137
+ return torch.cuda.is_available()
138
+
139
+ def load_model(self) -> None:
140
+ if self.check_gpu():
141
+ self.device = torch.device(
142
+ "mps" if torch.backends.mps.is_available() else "cuda"
143
+ )
144
+ else:
145
+ self.device = torch.device("cpu")
146
+
147
+ # Load fine-tuned head checkpoint.
148
+ try:
149
+ checkpoint = torch.load(
150
+ self.model_path, map_location=self.device, weights_only=True
151
+ )
152
+ except Exception:
153
+ checkpoint = torch.load(
154
+ self.model_path, map_location=self.device, weights_only=False
155
+ )
156
+
157
+ # Resolve backbone path. The fine-tuned model ships alongside
158
+ # one of two known backbone files, depending on the recipe.
159
+ backbone_path: Path | None = None
160
+ for name in _BACKBONE_FILENAMES:
161
+ candidate = self.model_dir / name
162
+ if candidate.exists():
163
+ backbone_path = candidate
164
+ break
165
+ if backbone_path is None:
166
+ raise FileNotFoundError(
167
+ "Backbone weights not found. Expected one of "
168
+ f"{_BACKBONE_FILENAMES} in {self.model_dir}."
169
+ )
170
+
171
+ backbone = _load_fx_checkpoint(backbone_path, map_location="cpu")
172
+ model = _FXClassifier(
173
+ backbone=backbone,
174
+ num_classes=checkpoint["num_classes"],
175
+ img_size=checkpoint["img_size"],
176
+ input_layout=checkpoint["input_layout"],
177
+ )
178
+ model.load_state_dict(checkpoint["model"])
179
+ self.model = model.to(self.device).eval()
180
+
181
+ self._class_names = list(checkpoint["class_names"])
182
+
183
+ norm = checkpoint["normalize"]
184
+ img_size = checkpoint["img_size"]
185
+ self._preprocess = transforms.Compose([
186
+ transforms.Resize((img_size, img_size), antialias=True),
187
+ transforms.ToTensor(),
188
+ transforms.Normalize(mean=norm["mean"], std=norm["std"]),
189
+ ])
190
+
191
+ def get_crop(
192
+ self, image: Image.Image, bbox: tuple[float, float, float, float]
193
+ ) -> Image.Image:
194
+ """Crop the bbox region. SpeciesNet head was trained on tight crops."""
195
+ W, H = image.size
196
+ x, y, w, h = bbox
197
+ left = max(0, int(round(x * W)))
198
+ top = max(0, int(round(y * H)))
199
+ right = min(W, int(round((x + w) * W)))
200
+ bottom = min(H, int(round((y + h) * H)))
201
+ if right <= left or bottom <= top:
202
+ return image
203
+ return image.crop((left, top, right, bottom))
204
+
205
+ def get_classification(self, crop: Image.Image) -> list[list]:
206
+ """Per-image inference. Returns [[name, prob], ...] for all classes."""
207
+ assert self.model is not None and self._preprocess is not None
208
+ if crop.mode != "RGB":
209
+ crop = crop.convert("RGB")
210
+ tensor = self._preprocess(crop).unsqueeze(0).to(self.device)
211
+ with torch.no_grad():
212
+ probs = F.softmax(self.model(tensor), dim=1).cpu().numpy()[0]
213
+ return [[self._class_names[i], float(probs[i])] for i in range(len(probs))]
214
+
215
+ def get_class_names(self) -> dict[str, str]:
216
+ """1-indexed mapping {id: class_name} for the output JSON."""
217
+ return {str(i + 1): name for i, name in enumerate(self._class_names)}
218
+
219
+ # ------------------------------------------------------------------
220
+ # Optional batch interface (5-15x GPU speedup vs per-crop calls)
221
+ # ------------------------------------------------------------------
222
+
223
+ def get_tensor(self, crop: Image.Image) -> np.ndarray:
224
+ assert self._preprocess is not None
225
+ if crop.mode != "RGB":
226
+ crop = crop.convert("RGB")
227
+ return self._preprocess(crop).numpy()
228
+
229
+ def classify_batch(self, batch: np.ndarray) -> list[list[list]]:
230
+ assert self.model is not None
231
+ tensor = torch.from_numpy(batch).to(self.device)
232
+ with torch.no_grad():
233
+ probs = F.softmax(self.model(tensor), dim=1).cpu().numpy()
234
+ return [
235
+ [[self._class_names[j], float(p[j])] for j in range(len(p))]
236
+ for p in probs
237
+ ]
taxonomy.csv ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ model_class,class,order,family,genus,species
2
+ dingo,mammalia,carnivora,canidae,canis,lupus dingo
3
+ bird sp,aves,,,,
4
+ frog sp,amphibia,anura,,,
5
+ owl sp,aves,strigiformes,,,
6
+ rodent sp,mammalia,rodentia,,,
7
+ kingfisher sp,aves,coraciiformes,alcedinidae,,
8
+ gecko sp,reptilia,squamata,gekkonidae,,
9
+ skink sp,reptilia,squamata,scincidae,,
10
+ dragon sp,reptilia,squamata,agamidae,,
11
+ quail spp,aves,galliformes,phasianidae,,
12
+ giant stick insect,insecta,phasmida,phasmatidae,extatosoma,tiaratum
13
+ short-beaked echidna,mammalia,monotremata,tachyglossidae,tachyglossus,aculeatus
14
+ cat,mammalia,carnivora,felidae,felis,catus
15
+ northern brown bandicoot,mammalia,peramelemorphia,peramelidae,isoodon,macrourus
16
+ northern quoll,mammalia,dasyuromorphia,dasyuridae,dasyurus,hallucatus
17
+ fawn antechinus,mammalia,dasyuromorphia,dasyuridae,antechinus,bellus
18
+ sandstone pseudantechinus,mammalia,dasyuromorphia,dasyuridae,pseudantechinus,bilarni
19
+ dunnart sp,mammalia,dasyuromorphia,dasyuridae,sminthopsis,
20
+ red-cheeked dunnart,mammalia,dasyuromorphia,dasyuridae,sminthopsis,virginiae
21
+ planigale sp,mammalia,dasyuromorphia,dasyuridae,planigale,maculata
22
+ brush-tailed rabbit-rat,mammalia,rodentia,muridae,conilurus,penicillatus
23
+ pseudomys sp,mammalia,rodentia,muridae,pseudomys,
24
+ black-footed tree-rat,mammalia,rodentia,muridae,mesembriomys,gouldii
25
+ water-rat,mammalia,rodentia,muridae,hydromys,chrysogaster
26
+ northern hopping mouse,mammalia,rodentia,muridae,notomys,aquilo
27
+ grassland melomys,mammalia,rodentia,muridae,melomys,burtoni
28
+ rattus sp,mammalia,rodentia,muridae,rattus,
29
+ pale field-rat,mammalia,rodentia,muridae,rattus,tunneyi
30
+ dusky rat,mammalia,rodentia,muridae,rattus,colletti
31
+ black rat,mammalia,rodentia,muridae,rattus,rattus
32
+ rock-rat sp,mammalia,rodentia,muridae,zyzomys,
33
+ arnhem rock-rat,mammalia,rodentia,muridae,zyzomys,maini
34
+ common rock-rat,mammalia,rodentia,muridae,zyzomys,argurus
35
+ rock ringtail possum,mammalia,diprotodontia,pseudocheiridae,petropseudes,dahli
36
+ rock-wallaby sp,mammalia,diprotodontia,macropodidae,petrogale,
37
+ northern nailtail wallaby,mammalia,diprotodontia,macropodidae,onychogalea,unguifera
38
+ northern brushtail possum,mammalia,diprotodontia,phalangeridae,trichosurus,vulpecula
39
+ horse,mammalia,perissodactyla,equidae,equus,caballus
40
+ donkey,mammalia,perissodactyla,equidae,equus,asinus
41
+ rusa deer,mammalia,artiodactyla,cervidae,rusa,timorensis
42
+ cow,mammalia,artiodactyla,bovidae,bos,
43
+ banteng,mammalia,artiodactyla,bovidae,bos,javanicus
44
+ northern brown snake,reptilia,squamata,elapidae,pseudonaja,nuchalis
45
+ northern bluetongue,reptilia,squamata,scincidae,tiliqua,scincoides
46
+ frill-neck lizard,reptilia,squamata,agamidae,chlamydosaurus,kingii
47
+ gilbert's dragon,reptilia,squamata,agamidae,lophognathus,gilberti
48
+ kimberley rock goanna,reptilia,squamata,varanidae,varanus,glauerti
49
+ mertens' water goanna,reptilia,squamata,varanidae,varanus,mertensi
50
+ yellow-spotted goanna,reptilia,squamata,varanidae,varanus,panoptes
51
+ black-palmed goanna,reptilia,squamata,varanidae,varanus,glebopalma
52
+ spotted tree goanna,reptilia,squamata,varanidae,varanus,scalaris
53
+ goanna sp,reptilia,squamata,varanidae,varanus,
54
+ brown quail,aves,galliformes,phasianidae,synoicus,ypsilophorus
55
+ chestnut rail,aves,gruiformes,rallidae,eulabeornis,castaneoventris
56
+ red-backed button-quail,aves,charadriiformes,turnicidae,turnix,maculosus
57
+ chestnut-backed button-quail,aves,charadriiformes,turnicidae,turnix,castanotus
58
+ rainbow bee-eater,aves,coraciiformes,meropidae,merops,ornatus
59
+ blue-winged kookaburra,aves,coraciiformes,alcedinidae,dacelo,leachii
60
+ forest kingfisher,aves,coraciiformes,alcedinidae,todiramphus,macleayii
61
+ northern rosella,aves,psittaciformes,psittacidae,platycercus,venustus
62
+ red-winged parrot,aves,psittaciformes,psittacidae,aprosmictus,erythropterus
63
+ little corella,aves,psittaciformes,psittacidae,cacatua,sanguinea
64
+ sulphur-crested cockatoo,aves,psittaciformes,psittacidae,cacatua,galerita
65
+ red-tailed black cockatoo,aves,psittaciformes,psittacidae,calyptorhynchus,banksii
66
+ whistling kite,aves,accipitriformes,accipitridae,haliastur,sphenurus
67
+ black-breasted buzzard,aves,accipitriformes,accipitridae,hamirostra,melanosternon
68
+ collared sparrowhawk,aves,accipitriformes,accipitridae,accipiter,cirrocephalus
69
+ brown goshawk,aves,accipitriformes,accipitridae,accipiter,fasciatus
70
+ black bittern,aves,pelecaniformes,ardeidae,botaurus,
71
+ peregrine falcon,aves,falconiformes,falconidae,falco,peregrinus
72
+ brown falcon,aves,falconiformes,falconidae,falco,berigora
73
+ bush stone-curlew,aves,charadriiformes,burhinidae,burhinus,grallarius
74
+ orange-footed scrubfowl,aves,galliformes,megapodiidae,megapodius,reinwardt
75
+ little shrike-thrush,aves,passeriformes,pachycephalidae,colluricincla,megarhyncha
76
+ sandstone shrike-thrush,aves,passeriformes,pachycephalidae,colluricincla,woodwardi
77
+ grey shrike-thrush,aves,passeriformes,pachycephalidae,colluricincla,harmonica
78
+ torresian crow,aves,passeriformes,corvidae,corvus,orru
79
+ striated pardalote,aves,passeriformes,pardalotidae,pardalotus,striatus
80
+ leaden flycatcher,aves,passeriformes,monarchidae,myiagra,rubecula
81
+ magpie-lark,aves,passeriformes,monarchidae,grallina,cyanoleuca
82
+ blue-faced honeyeater,aves,passeriformes,meliphagidae,entomyzon,cyanotis
83
+ friarbird sp,aves,passeriformes,meliphagidae,philemon,
84
+ little friarbird,aves,passeriformes,meliphagidae,philemon,citreogularis
85
+ silver-crowned friarbird,aves,passeriformes,meliphagidae,philemon,argenticeps
86
+ brown honeyeater,aves,passeriformes,meliphagidae,lichmera,indistincta
87
+ yellow-throated miner,aves,passeriformes,meliphagidae,manorina,flavigula
88
+ white-throated grasswren,aves,passeriformes,maluridae,amytornis,woodwardi
89
+ pied butcherbird,aves,passeriformes,cracticidae,cracticus,nigrogularis
90
+ grey butcherbird,aves,passeriformes,cracticidae,cracticus,torquatus
91
+ rainbow pitta,aves,passeriformes,pittidae,pitta,iris
92
+ grey-crowned babbler,aves,passeriformes,pomatostomidae,pomatostomus,temporalis
93
+ rufous songlark,aves,passeriformes,locustellidae,cincloramphus,mathewsi
94
+ crimson finch,aves,passeriformes,estrildidae,neochmia,phaeton
95
+ great bowerbird,aves,passeriformes,ptilonorhynchidae,chlamydera,nuchalis
96
+ common bronzewing,aves,columbiformes,columbidae,phaps,chalcoptera
97
+ spinifex pigeon,aves,columbiformes,columbidae,geophaps,plumifera
98
+ eastern partridge pigeon,aves,columbiformes,columbidae,geophaps,smithii
99
+ bar-shouldered dove,aves,columbiformes,columbidae,geopelia,humeralis
100
+ diamond dove,aves,columbiformes,columbidae,geopelia,cuneata
101
+ peaceful dove,aves,columbiformes,columbidae,geopelia,placida
102
+ emerald dove,aves,columbiformes,columbidae,chalcophaps,indica
103
+ chestnut-quilled rock-pigeon,aves,columbiformes,columbidae,petrophassa,rufipennis
104
+ australian owlet-nightjar,aves,apodiformes,aegothelidae,aegotheles,cristatus
105
+ tawny frogmouth,aves,caprimulgiformes,podargidae,podargus,strigoides
106
+ australian boobook,aves,strigiformes,strigidae,ninox,novaeseelandiae
107
+ antilopine wallaroo,mammalia,diprotodontia,macropodidae,osphranter,antilopinus
108
+ wallaroo sp,mammalia,diprotodontia,macropodidae,osphranter,
109
+ eastern short-eared rock-wallaby,mammalia,diprotodontia,macropodidae,petrogale,wilkinsi
110
+ australian pipit,aves,passeriformes,motacillidae,anthus,australis
111
+ pallid cuckoo,aves,cuculiformes,cuculidae,heteroscenes,pallidus
112
+ cane toad,amphibia,anura,bufonidae,rhinella,marina
113
+ delicate mouse,mammalia,rodentia,muridae,pseudomys,delicatulus
114
+ western chestnut mouse,mammalia,rodentia,muridae,pseudomys,nanus
115
+ carpet python,reptilia,squamata,pythonidae,morelia,spilota variegata
116
+ wedge-tailed eagle,aves,accipitriformes,accipitridae,aquila,audax
117
+ long-tailed finch,aves,passeriformes,estrildidae,poephila,acuticauda
118
+ willie wagtail,aves,passeriformes,rhipiduridae,rhipidura,leucophrys
119
+ northern fantail,aves,passeriformes,rhipiduridae,rhipidura,rufiventris
120
+ pheasant coucal,aves,cuculiformes,cuculidae,centropus,phasianinus
121
+ masked owl,aves,strigiformes,tytonidae,tyto,novaehollandiae
122
+ northern brush-tailed phascogale,mammalia,dasyuromorphia,dasyuridae,phascogale,pirata
123
+ rufous whistler,aves,passeriformes,pachycephalidae,pachycephala,rufiventris
124
+ grey whistler,aves,passeriformes,pachycephalidae,pachycephala,simplex
125
+ banded honeyeater,aves,passeriformes,meliphagidae,cissomela,pectoralis
126
+ singing honeyeater,aves,passeriformes,meliphagidae,gavicalis,virescens
127
+ buffalo,mammalia,artiodactyla,bovidae,bubalus,bubalis
128
+ pig,mammalia,artiodactyla,suidae,sus,scrofa
129
+ yellow-tinted honeyeater,aves,passeriformes,meliphagidae,ptilotula,flavescens
130
+ savanna glider,mammalia,diprotodontia,petauridae,petaurus,
131
+ sand goanna,reptilia,squamata,varanidae,varanus,gouldii
132
+ black-tailed goanna,reptilia,squamata,varanidae,varanus,tristis
133
+ dusky myzomela,aves,passeriformes,meliphagidae,myzomela,obscura
134
+ radjah shelduck,aves,anseriformes,anatidae,radjah,radjah
135
+ double-barred finch,aves,passeriformes,estrildidae,stizoptera,bichenovii
136
+ mangrove robin,aves,passeriformes,petroicidae,peneoenanthe,pulverulenta
137
+ snake sp,reptilia,squamata,,,
138
+ agile wallaby,mammalia,diprotodontia,macropodidae,notamacropus,agilis
139
+ black wallaroo,mammalia,diprotodontia,macropodidae,osphranter,bernardus
140
+ common wallaroo,mammalia,diprotodontia,macropodidae,osphranter,robustus
141
+ black-spotted ridge-tailed goanna,reptilia,squamata,varanidae,varanus,insulanicus
142
+ false detection,,,,,