Coretex
image_dataset.py
1 # Copyright (C) 2023 Coretex LLC
2 
3 # This file is part of Coretex.ai
4 
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License as
7 # published by the Free Software Foundation, either version 3 of the
8 # License, or (at your option) any later version.
9 
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14 
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <https://www.gnu.org/licenses/>.
17 
18 from typing import Dict, List, Any
19 from typing_extensions import Self, override
20 from pathlib import Path
21 
22 from .base import BaseImageDataset
23 from ..network_dataset import NetworkDataset
24 from ...sample import ImageSample
25 from ...annotation import ImageDatasetClass, ImageDatasetClasses
26 from ....codable import KeyDescriptor, Codable
27 from ....networking import networkManager, FileData, NetworkRequestError
28 
29 
30 class ClassDistribution(Codable):
31 
32  name: str
33  color: str
34  count: int
35 
36 
37 class ImageDataset(BaseImageDataset[ImageSample], NetworkDataset[ImageSample]): # type: ignore
38 
39  """
40  Represents the Image Dataset class \n
41  Includes functionality for working with Image Datasets
42  that are uploaded to Coretex.ai
43  """
44 
45  classDistribution: List[ClassDistribution]
46 
47  def __init__(self) -> None:
48  super().__init__(ImageSample)
49 
50  @classmethod
51  def _keyDescriptors(cls) -> Dict[str, KeyDescriptor]:
52  descriptors = super()._keyDescriptors()
53 
54  descriptors["samples"] = KeyDescriptor("sessions", ImageSample, list)
55  descriptors["classes"] = KeyDescriptor("classes", ImageDatasetClass, ImageDatasetClasses)
56  descriptors["classDistribution"] = KeyDescriptor("class_distribution", ClassDistribution, list)
57 
58  return descriptors
59 
60  @classmethod
61  def fetchById(cls, objectId: int, **kwargs: Any) -> Self:
62  obj = super().fetchById(objectId, **kwargs)
63 
64  response = networkManager.get(f"annotation-class?dataset_id={obj.id}")
65 
66  if not response.hasFailed():
67  obj.classes = cls._decodeValue("classes", response.getJson(list))
68  obj._writeClassesToFile()
69 
70  return obj
71 
72  def saveClasses(self, classes: ImageDatasetClasses) -> bool:
73  parameters = {
74  "dataset_id": self.id,
75  "classes": [clazz.encode() for clazz in classes]
76  }
77 
78  response = networkManager.post("annotation-class", parameters)
79  if not response.hasFailed():
80  return super().saveClasses(classes)
81 
82  return not response.hasFailed()
83 
84  @override
85  def _uploadSample(self, samplePath: Path, sampleName: str, **metadata: Any) -> ImageSample:
86  params = {
87  "dataset_id": self.id
88  }
89 
90  files = [
91  FileData.createFromPath("file", samplePath)
92  ]
93 
94  response = networkManager.formData("session/import", params, files)
95  if response.hasFailed():
96  raise NetworkRequestError(response, f"Failed to create image Sample from \"{samplePath}\"")
97 
98  return self._sampleType_sampleType.decode(response.getJson(dict))