endoreg-db 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- endoreg_db/data/__init__.py +14 -0
- endoreg_db/data/active_model/data.yaml +3 -0
- endoreg_db/data/center/data.yaml +7 -0
- endoreg_db/data/endoscope_type/data.yaml +11 -0
- endoreg_db/data/endoscopy_processor/data.yaml +45 -0
- endoreg_db/data/examination/examinations/data.yaml +17 -0
- endoreg_db/data/examination/time/data.yaml +48 -0
- endoreg_db/data/examination/time-type/data.yaml +8 -0
- endoreg_db/data/examination/type/data.yaml +5 -0
- endoreg_db/data/information_source/data.yaml +30 -0
- endoreg_db/data/label/label/data.yaml +62 -0
- endoreg_db/data/label/label-set/data.yaml +18 -0
- endoreg_db/data/label/label-type/data.yaml +7 -0
- endoreg_db/data/model_type/data.yaml +7 -0
- endoreg_db/data/profession/data.yaml +70 -0
- endoreg_db/data/unit/data.yaml +17 -0
- endoreg_db/data/unit/length.yaml +31 -0
- endoreg_db/data/unit/volume.yaml +26 -0
- endoreg_db/data/unit/weight.yaml +31 -0
- endoreg_db/forms/__init__.py +2 -0
- endoreg_db/forms/settings/__init__.py +8 -0
- endoreg_db/forms/unit.py +6 -0
- endoreg_db/management/commands/_load_model_template.py +41 -0
- endoreg_db/management/commands/delete_legacy_images.py +19 -0
- endoreg_db/management/commands/delete_legacy_videos.py +17 -0
- endoreg_db/management/commands/extract_legacy_video_frames.py +18 -0
- endoreg_db/management/commands/fetch_legacy_image_dataset.py +32 -0
- endoreg_db/management/commands/import_legacy_images.py +94 -0
- endoreg_db/management/commands/import_legacy_videos.py +76 -0
- endoreg_db/management/commands/load_active_model_data.py +45 -0
- endoreg_db/management/commands/load_ai_model_data.py +45 -0
- endoreg_db/management/commands/load_base_db_data.py +62 -0
- endoreg_db/management/commands/load_center_data.py +43 -0
- endoreg_db/management/commands/load_endoscope_type_data.py +45 -0
- endoreg_db/management/commands/load_endoscopy_processor_data.py +45 -0
- endoreg_db/management/commands/load_examination_data.py +75 -0
- endoreg_db/management/commands/load_information_source.py +45 -0
- endoreg_db/management/commands/load_label_data.py +67 -0
- endoreg_db/management/commands/load_profession_data.py +44 -0
- endoreg_db/management/commands/load_unit_data.py +46 -0
- endoreg_db/management/commands/load_user_groups.py +67 -0
- endoreg_db/management/commands/register_ai_model.py +65 -0
- endoreg_db/migrations/0001_initial.py +582 -0
- endoreg_db/models/__init__.py +53 -0
- endoreg_db/models/ai_model/__init__.py +3 -0
- endoreg_db/models/ai_model/active_model.py +9 -0
- endoreg_db/models/ai_model/model_meta.py +24 -0
- endoreg_db/models/ai_model/model_type.py +26 -0
- endoreg_db/models/ai_model/utils.py +8 -0
- endoreg_db/models/annotation/__init__.py +2 -0
- endoreg_db/models/annotation/binary_classification_annotation_task.py +80 -0
- endoreg_db/models/annotation/image_classification.py +27 -0
- endoreg_db/models/center.py +19 -0
- endoreg_db/models/data_file/__init__.py +4 -0
- endoreg_db/models/data_file/base_classes/__init__.py +3 -0
- endoreg_db/models/data_file/base_classes/abstract_frame.py +51 -0
- endoreg_db/models/data_file/base_classes/abstract_video.py +200 -0
- endoreg_db/models/data_file/frame.py +45 -0
- endoreg_db/models/data_file/report_file.py +88 -0
- endoreg_db/models/data_file/video/__init__.py +7 -0
- endoreg_db/models/data_file/video/import_meta.py +25 -0
- endoreg_db/models/data_file/video/video.py +25 -0
- endoreg_db/models/data_file/video_segment.py +107 -0
- endoreg_db/models/examination/__init__.py +4 -0
- endoreg_db/models/examination/examination.py +26 -0
- endoreg_db/models/examination/examination_time.py +27 -0
- endoreg_db/models/examination/examination_time_type.py +24 -0
- endoreg_db/models/examination/examination_type.py +18 -0
- endoreg_db/models/hardware/__init__.py +2 -0
- endoreg_db/models/hardware/endoscope.py +44 -0
- endoreg_db/models/hardware/endoscopy_processor.py +143 -0
- endoreg_db/models/information_source.py +22 -0
- endoreg_db/models/label/__init__.py +1 -0
- endoreg_db/models/label/label.py +84 -0
- endoreg_db/models/legacy_data/__init__.py +3 -0
- endoreg_db/models/legacy_data/image.py +34 -0
- endoreg_db/models/patient_examination/__init__.py +35 -0
- endoreg_db/models/persons/__init__.py +4 -0
- endoreg_db/models/persons/examiner/__init__.py +2 -0
- endoreg_db/models/persons/examiner/examiner.py +16 -0
- endoreg_db/models/persons/examiner/examiner_type.py +2 -0
- endoreg_db/models/persons/patient.py +58 -0
- endoreg_db/models/persons/person.py +34 -0
- endoreg_db/models/persons/portal_user_information.py +29 -0
- endoreg_db/models/prediction/__init__.py +2 -0
- endoreg_db/models/prediction/image_classification.py +37 -0
- endoreg_db/models/prediction/video_prediction_meta.py +244 -0
- endoreg_db/models/unit.py +20 -0
- endoreg_db/queries/__init__.py +5 -0
- endoreg_db/queries/annotations/__init__.py +3 -0
- endoreg_db/queries/annotations/legacy.py +159 -0
- endoreg_db/queries/get/__init__.py +6 -0
- endoreg_db/queries/get/annotation.py +0 -0
- endoreg_db/queries/get/center.py +42 -0
- endoreg_db/queries/get/model.py +13 -0
- endoreg_db/queries/get/patient.py +14 -0
- endoreg_db/queries/get/patient_examination.py +20 -0
- endoreg_db/queries/get/prediction.py +0 -0
- endoreg_db/queries/get/report_file.py +33 -0
- endoreg_db/queries/get/video.py +31 -0
- endoreg_db/queries/get/video_import_meta.py +0 -0
- endoreg_db/queries/get/video_prediction_meta.py +0 -0
- endoreg_db/queries/sanity/__init_.py +0 -0
- endoreg_db/serializers/__init__.py +10 -0
- endoreg_db/serializers/ai_model.py +19 -0
- endoreg_db/serializers/annotation.py +17 -0
- endoreg_db/serializers/center.py +11 -0
- endoreg_db/serializers/examination.py +33 -0
- endoreg_db/serializers/frame.py +13 -0
- endoreg_db/serializers/hardware.py +21 -0
- endoreg_db/serializers/label.py +22 -0
- endoreg_db/serializers/patient.py +10 -0
- endoreg_db/serializers/prediction.py +15 -0
- endoreg_db/serializers/report_file.py +7 -0
- endoreg_db/serializers/video.py +27 -0
- endoreg_db-0.2.0.dist-info/LICENSE +674 -0
- endoreg_db-0.2.0.dist-info/METADATA +26 -0
- endoreg_db-0.2.0.dist-info/RECORD +126 -0
- endoreg_db-0.1.0.dist-info/METADATA +0 -19
- endoreg_db-0.1.0.dist-info/RECORD +0 -10
- {endoreg_db-0.1.0.dist-info → endoreg_db-0.2.0.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
import numpy as np
|
|
3
|
+
from ..annotation import ImageClassificationAnnotation
|
|
4
|
+
|
|
5
|
+
def find_segments_in_prediction_array(prediction_array: np.array, min_frame_len: int):
|
|
6
|
+
"""
|
|
7
|
+
Expects a prediction array of shape (num_frames) and a minimum frame length.
|
|
8
|
+
Returns a list of tuples (start_frame_number, end_frame_number) that represent the segments.
|
|
9
|
+
"""
|
|
10
|
+
# Add False to the beginning and end to detect changes at the array boundaries
|
|
11
|
+
padded_prediction = np.pad(prediction_array, (1, 1), 'constant', constant_values=False)
|
|
12
|
+
|
|
13
|
+
# Find the start points and end points of the segments
|
|
14
|
+
diffs = np.diff(padded_prediction.astype(int))
|
|
15
|
+
segment_starts = np.where(diffs == 1)[0]
|
|
16
|
+
segment_ends = np.where(diffs == -1)[0]
|
|
17
|
+
|
|
18
|
+
# Filter segments based on min_frame_len
|
|
19
|
+
segments = [(start, end) for start, end in zip(segment_starts, segment_ends) if end - start >= min_frame_len]
|
|
20
|
+
|
|
21
|
+
return segments
|
|
22
|
+
|
|
23
|
+
class AbstractLabelVideoSegment(models.Model):
|
|
24
|
+
video = None # Placeholder for the video field, to be defined in derived classes
|
|
25
|
+
prediction_meta = None # Placeholder for the prediction_meta field, to be defined in derived classes
|
|
26
|
+
start_frame_number = models.IntegerField()
|
|
27
|
+
end_frame_number = models.IntegerField()
|
|
28
|
+
source = models.ForeignKey("InformationSource", on_delete=models.CASCADE)
|
|
29
|
+
label = models.ForeignKey("Label", on_delete=models.CASCADE)
|
|
30
|
+
|
|
31
|
+
class Meta:
|
|
32
|
+
abstract = True
|
|
33
|
+
|
|
34
|
+
def __str__(self):
|
|
35
|
+
return self.video.file.path + " Label - " + self.label.name + " - " + str(self.start_frame_number) + " - " + str(self.end_frame_number)
|
|
36
|
+
|
|
37
|
+
def get_frames(self):
|
|
38
|
+
return self.video.get_frame_range(self.start_frame_number, self.end_frame_number)
|
|
39
|
+
|
|
40
|
+
def get_annotations(self) -> ImageClassificationAnnotation.objects:
|
|
41
|
+
frames = self.get_frames()
|
|
42
|
+
annotations = ImageClassificationAnnotation.objects.filter(frame__in=frames, label=self.label)
|
|
43
|
+
|
|
44
|
+
return annotations
|
|
45
|
+
|
|
46
|
+
def get_frames_without_annotation(self):
|
|
47
|
+
"""
|
|
48
|
+
Get a frame without an annotation.
|
|
49
|
+
"""
|
|
50
|
+
assert 1 == 2, "This method should be overridden in derived classes"
|
|
51
|
+
|
|
52
|
+
def get_segment_len_in_s(self):
|
|
53
|
+
return (self.end_frame_number - self.start_frame_number) / self.video.fps
|
|
54
|
+
|
|
55
|
+
class LegacyLabelVideoSegment(AbstractLabelVideoSegment):
|
|
56
|
+
video = models.ForeignKey("LegacyVideo", on_delete=models.CASCADE)
|
|
57
|
+
prediction_meta = models.ForeignKey("LegacyVideoPredictionMeta", on_delete=models.CASCADE, related_name="video_segments")
|
|
58
|
+
|
|
59
|
+
def get_video_model(self):
|
|
60
|
+
from endoreg_db.models.data_file.video import LegacyVideo
|
|
61
|
+
return LegacyVideo
|
|
62
|
+
|
|
63
|
+
def get_annotations(self) -> ImageClassificationAnnotation.objects:
|
|
64
|
+
frames = self.get_frames()
|
|
65
|
+
annotations = ImageClassificationAnnotation.objects.filter(legacy_frame__in=frames, label=self.label)
|
|
66
|
+
|
|
67
|
+
return annotations
|
|
68
|
+
|
|
69
|
+
def get_frames_without_annotation(self, n_frames):
|
|
70
|
+
"""
|
|
71
|
+
Get a frame without an annotation.
|
|
72
|
+
"""
|
|
73
|
+
frames = self.get_frames()
|
|
74
|
+
annotations = ImageClassificationAnnotation.objects.filter(legacy_frame__in=frames, label=self.label)
|
|
75
|
+
|
|
76
|
+
annotated_frames = [annotation.legacy_frame for annotation in annotations]
|
|
77
|
+
frames_without_annotation = [frame for frame in frames if frame not in annotated_frames]
|
|
78
|
+
|
|
79
|
+
# draw n random frames
|
|
80
|
+
if len(frames_without_annotation) > n_frames:
|
|
81
|
+
frames_without_annotation = np.random.choice(frames_without_annotation, n_frames, replace=False)
|
|
82
|
+
|
|
83
|
+
return frames_without_annotation
|
|
84
|
+
|
|
85
|
+
class LabelVideoSegment(AbstractLabelVideoSegment):
|
|
86
|
+
video = models.ForeignKey("Video", on_delete=models.CASCADE)
|
|
87
|
+
prediction_meta = models.ForeignKey("VideoPredictionMeta", on_delete=models.CASCADE, related_name="video_segments")
|
|
88
|
+
|
|
89
|
+
def get_video_model(self):
|
|
90
|
+
from endoreg_db.models.data_file.video import Video
|
|
91
|
+
return Video
|
|
92
|
+
|
|
93
|
+
def get_frames_without_annotation(self, n_frames):
|
|
94
|
+
"""
|
|
95
|
+
Get a frame without an annotation.
|
|
96
|
+
"""
|
|
97
|
+
frames = self.get_frames()
|
|
98
|
+
annotations = ImageClassificationAnnotation.objects.filter(frame__in=frames, label=self.label)
|
|
99
|
+
|
|
100
|
+
annotated_frames = [annotation.frame for annotation in annotations]
|
|
101
|
+
frames_without_annotation = [frame for frame in frames if frame not in annotated_frames]
|
|
102
|
+
|
|
103
|
+
# draw n random frames
|
|
104
|
+
if len(frames_without_annotation) > n_frames:
|
|
105
|
+
frames_without_annotation = np.random.choice(frames_without_annotation, n_frames, replace=False)
|
|
106
|
+
|
|
107
|
+
return frames_without_annotation
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
class ExaminationManager(models.Manager):
|
|
4
|
+
def get_by_natural_key(self, name):
|
|
5
|
+
return self.get(name=name)
|
|
6
|
+
|
|
7
|
+
class Examination(models.Model):
|
|
8
|
+
name = models.CharField(max_length=100, unique=True)
|
|
9
|
+
name_de = models.CharField(max_length=100, blank=True, null=True)
|
|
10
|
+
name_en = models.CharField(max_length=100, blank=True, null=True)
|
|
11
|
+
examination_types = models.ManyToManyField('ExaminationType', blank=True)
|
|
12
|
+
objects = ExaminationManager()
|
|
13
|
+
date = models.DateField(blank=True, null=True)
|
|
14
|
+
time = models.TimeField(blank=True, null=True)
|
|
15
|
+
|
|
16
|
+
def __str__(self):
|
|
17
|
+
return self.name
|
|
18
|
+
|
|
19
|
+
def natural_key(self):
|
|
20
|
+
return (self.name,)
|
|
21
|
+
|
|
22
|
+
class Meta:
|
|
23
|
+
verbose_name = 'Examination'
|
|
24
|
+
verbose_name_plural = 'Examinations'
|
|
25
|
+
ordering = ['name']
|
|
26
|
+
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from rest_framework import serializers
|
|
3
|
+
|
|
4
|
+
class ExaminationTimeManager(models.Manager):
|
|
5
|
+
def get_by_natural_key(self, name):
|
|
6
|
+
return self.get(name=name)
|
|
7
|
+
|
|
8
|
+
class ExaminationTime(models.Model):
|
|
9
|
+
name = models.CharField(max_length=100, unique=True)
|
|
10
|
+
name_de = models.CharField(max_length=100, blank=True, null=True)
|
|
11
|
+
name_en = models.CharField(max_length=100, blank=True, null=True)
|
|
12
|
+
start_time = models.TimeField(blank=True, null=True)
|
|
13
|
+
time_types = models.ManyToManyField('ExaminationTimeType', blank=True)
|
|
14
|
+
end_time = models.TimeField(blank=True, null=True)
|
|
15
|
+
objects = ExaminationTimeManager()
|
|
16
|
+
|
|
17
|
+
def __str__(self):
|
|
18
|
+
return self.name
|
|
19
|
+
|
|
20
|
+
def natural_key(self):
|
|
21
|
+
return (self.name,)
|
|
22
|
+
|
|
23
|
+
class Meta:
|
|
24
|
+
verbose_name = 'Examination Time'
|
|
25
|
+
verbose_name_plural = 'Examination Times'
|
|
26
|
+
ordering = ['name']
|
|
27
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
class ExaminationTimeTypeManager(models.Manager):
|
|
4
|
+
def get_by_natural_key(self, name):
|
|
5
|
+
return self.get(name=name)
|
|
6
|
+
|
|
7
|
+
class ExaminationTimeType(models.Model):
|
|
8
|
+
objects = ExaminationTimeTypeManager()
|
|
9
|
+
name = models.CharField(max_length=100, unique=True)
|
|
10
|
+
name_de = models.CharField(max_length=100, blank=True, null=True)
|
|
11
|
+
name_en = models.CharField(max_length=100, blank=True, null=True)
|
|
12
|
+
examinations = models.ManyToManyField('Examination', blank=True)
|
|
13
|
+
|
|
14
|
+
def __str__(self):
|
|
15
|
+
return self.name
|
|
16
|
+
|
|
17
|
+
def natural_key(self):
|
|
18
|
+
return (self.name,)
|
|
19
|
+
|
|
20
|
+
class Meta:
|
|
21
|
+
verbose_name = 'Examination Time Type'
|
|
22
|
+
verbose_name_plural = 'Examination Time Types'
|
|
23
|
+
ordering = ['name']
|
|
24
|
+
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
class ExaminationTypeManager(models.Manager):
|
|
4
|
+
def get_by_natural_key(self, name):
|
|
5
|
+
return self.get(name=name)
|
|
6
|
+
|
|
7
|
+
class ExaminationType(models.Model):
|
|
8
|
+
objects = ExaminationTypeManager()
|
|
9
|
+
name = models.CharField(max_length=100, unique=True)
|
|
10
|
+
name_de = models.CharField(max_length=100, blank=True, null=True)
|
|
11
|
+
name_en = models.CharField(max_length=100, blank=True, null=True)
|
|
12
|
+
|
|
13
|
+
def __str__(self):
|
|
14
|
+
return self.name_en
|
|
15
|
+
|
|
16
|
+
def natural_key(self):
|
|
17
|
+
return (self.name,)
|
|
18
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
class EndoscopeManager(models.Manager):
|
|
4
|
+
def get_by_natural_key(self, name, sn):
|
|
5
|
+
return self.get(name=name, sn=sn)
|
|
6
|
+
|
|
7
|
+
class Endoscope(models.Model):
|
|
8
|
+
objects = EndoscopeManager()
|
|
9
|
+
|
|
10
|
+
name = models.CharField(max_length=255)
|
|
11
|
+
sn = models.CharField(max_length=255)
|
|
12
|
+
|
|
13
|
+
def natural_key(self):
|
|
14
|
+
return (self.name, self.sn)
|
|
15
|
+
|
|
16
|
+
def __str__(self):
|
|
17
|
+
return self.name
|
|
18
|
+
|
|
19
|
+
class Meta:
|
|
20
|
+
ordering = ['name']
|
|
21
|
+
verbose_name = 'Endoscope'
|
|
22
|
+
verbose_name_plural = 'Endoscopes'
|
|
23
|
+
|
|
24
|
+
class EndoscopeTypeManager(models.Manager):
|
|
25
|
+
def get_by_natural_key(self, name):
|
|
26
|
+
return self.get(name=name)
|
|
27
|
+
|
|
28
|
+
class EndoscopeType(models.Model):
|
|
29
|
+
objects = EndoscopeTypeManager()
|
|
30
|
+
|
|
31
|
+
name = models.CharField(max_length=255)
|
|
32
|
+
name_de = models.CharField(max_length=255, blank=True, null=True)
|
|
33
|
+
name_en = models.CharField(max_length=255, blank=True, null=True)
|
|
34
|
+
|
|
35
|
+
def natural_key(self):
|
|
36
|
+
return (self.name,)
|
|
37
|
+
|
|
38
|
+
def __str__(self):
|
|
39
|
+
return self.name
|
|
40
|
+
|
|
41
|
+
class Meta:
|
|
42
|
+
ordering = ['name']
|
|
43
|
+
verbose_name = 'Endoscope Type'
|
|
44
|
+
verbose_name_plural = 'Endoscope Types'
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
class EndoscopyProcessorManager(models.Manager):
|
|
4
|
+
def get_by_natural_key(self, name):
|
|
5
|
+
return self.get(name=name)
|
|
6
|
+
|
|
7
|
+
class EndoscopyProcessor(models.Model):
|
|
8
|
+
objects = EndoscopyProcessorManager()
|
|
9
|
+
|
|
10
|
+
name = models.CharField(max_length=255)
|
|
11
|
+
image_width = models.IntegerField()
|
|
12
|
+
image_height = models.IntegerField()
|
|
13
|
+
# image_fps = models.IntegerField()
|
|
14
|
+
|
|
15
|
+
# Roi for endoscope image
|
|
16
|
+
endoscope_image_x = models.IntegerField()
|
|
17
|
+
endoscope_image_y = models.IntegerField()
|
|
18
|
+
endoscope_image_width = models.IntegerField()
|
|
19
|
+
endoscope_image_height = models.IntegerField()
|
|
20
|
+
|
|
21
|
+
# Roi for examination date
|
|
22
|
+
examination_date_x = models.IntegerField()
|
|
23
|
+
examination_date_y = models.IntegerField()
|
|
24
|
+
examination_date_width = models.IntegerField()
|
|
25
|
+
examination_date_height = models.IntegerField()
|
|
26
|
+
|
|
27
|
+
# Roi for examination time
|
|
28
|
+
examination_time_x = models.IntegerField(blank=True, null=True)
|
|
29
|
+
examination_time_y = models.IntegerField(blank=True, null=True)
|
|
30
|
+
examination_time_width = models.IntegerField(blank=True, null=True)
|
|
31
|
+
examination_time_height = models.IntegerField(blank=True, null=True)
|
|
32
|
+
|
|
33
|
+
# Roi for patient first name
|
|
34
|
+
patient_first_name_x = models.IntegerField()
|
|
35
|
+
patient_first_name_y = models.IntegerField()
|
|
36
|
+
patient_first_name_width = models.IntegerField()
|
|
37
|
+
patient_first_name_height = models.IntegerField()
|
|
38
|
+
|
|
39
|
+
# Roi for patient name
|
|
40
|
+
patient_last_name_x = models.IntegerField()
|
|
41
|
+
patient_last_name_y = models.IntegerField()
|
|
42
|
+
patient_last_name_width = models.IntegerField()
|
|
43
|
+
patient_last_name_height = models.IntegerField()
|
|
44
|
+
|
|
45
|
+
# Roi for patient dob
|
|
46
|
+
patient_dob_x = models.IntegerField()
|
|
47
|
+
patient_dob_y = models.IntegerField()
|
|
48
|
+
patient_dob_width = models.IntegerField()
|
|
49
|
+
patient_dob_height = models.IntegerField()
|
|
50
|
+
|
|
51
|
+
# Roi for endoscope type
|
|
52
|
+
endoscope_type_x = models.IntegerField(blank=True, null=True)
|
|
53
|
+
endoscope_type_y = models.IntegerField(blank=True, null=True)
|
|
54
|
+
endoscope_type_width = models.IntegerField(blank=True, null=True)
|
|
55
|
+
endoscope_type_height = models.IntegerField(blank=True, null=True)
|
|
56
|
+
|
|
57
|
+
# Roi for endoscopy sn
|
|
58
|
+
endoscope_sn_x = models.IntegerField(blank=True, null=True)
|
|
59
|
+
endoscope_sn_y = models.IntegerField(blank=True, null=True)
|
|
60
|
+
endoscope_sn_width = models.IntegerField(blank=True, null=True)
|
|
61
|
+
endoscope_sn_height = models.IntegerField(blank=True, null=True)
|
|
62
|
+
|
|
63
|
+
def natural_key(self):
|
|
64
|
+
return (self.name,)
|
|
65
|
+
|
|
66
|
+
def __str__(self):
|
|
67
|
+
return self.name
|
|
68
|
+
|
|
69
|
+
def get_roi_endoscope_image(self):
|
|
70
|
+
return {
|
|
71
|
+
"x": self.endoscope_image_x,
|
|
72
|
+
"y": self.endoscope_image_y,
|
|
73
|
+
"width": self.endoscope_image_width,
|
|
74
|
+
"height": self.endoscope_image_height
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
def get_roi_examination_date(self):
|
|
78
|
+
return {
|
|
79
|
+
"x": self.examination_date_x,
|
|
80
|
+
"y": self.examination_date_y,
|
|
81
|
+
"width": self.examination_date_width,
|
|
82
|
+
"height": self.examination_date_height
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
def get_roi_examination_time(self):
|
|
86
|
+
return {
|
|
87
|
+
"x": self.examination_time_x,
|
|
88
|
+
"y": self.examination_time_y,
|
|
89
|
+
"width": self.examination_time_width,
|
|
90
|
+
"height": self.examination_time_height
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
def get_roi_patient_last_name(self):
|
|
94
|
+
return {
|
|
95
|
+
"x": self.patient_last_name_x,
|
|
96
|
+
"y": self.patient_last_name_y,
|
|
97
|
+
"width": self.patient_last_name_width,
|
|
98
|
+
"height": self.patient_last_name_height
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
def get_roi_patient_first_name(self):
|
|
102
|
+
return {
|
|
103
|
+
"x": self.patient_first_name_x,
|
|
104
|
+
"y": self.patient_first_name_y,
|
|
105
|
+
"width": self.patient_first_name_width,
|
|
106
|
+
"height": self.patient_first_name_height
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
def get_roi_patient_dob(self):
|
|
110
|
+
return {
|
|
111
|
+
"x": self.patient_dob_x,
|
|
112
|
+
"y": self.patient_dob_y,
|
|
113
|
+
"width": self.patient_dob_width,
|
|
114
|
+
"height": self.patient_dob_height
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
def get_roi_endoscope_type(self):
|
|
118
|
+
return {
|
|
119
|
+
"x": self.endoscope_type_x,
|
|
120
|
+
"y": self.endoscope_type_y,
|
|
121
|
+
"width": self.endoscope_type_width,
|
|
122
|
+
"height": self.endoscope_type_height
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
def get_roi_endoscopy_sn(self):
|
|
126
|
+
return {
|
|
127
|
+
"x": self.endoscope_sn_x,
|
|
128
|
+
"y": self.endoscope_sn_y,
|
|
129
|
+
"width": self.endoscope_sn_width,
|
|
130
|
+
"height": self.endoscope_sn_height
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
def get_rois(self):
|
|
134
|
+
return {
|
|
135
|
+
"endoscope_image": self.get_roi_endoscope_image(),
|
|
136
|
+
"examination_date": self.get_roi_examination_date(),
|
|
137
|
+
"examination_time": self.get_roi_examination_time(),
|
|
138
|
+
"patient_first_name": self.get_roi_patient_first_name(),
|
|
139
|
+
"patient_last_name": self.get_roi_patient_last_name(),
|
|
140
|
+
"patient_dob": self.get_roi_patient_dob(),
|
|
141
|
+
"endoscope_type": self.get_roi_endoscope_type(),
|
|
142
|
+
"endoscope_sn": self.get_roi_endoscopy_sn()
|
|
143
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
def get_prediction_information_source():
|
|
4
|
+
_source = InformationSource.objects.get(name="prediction")
|
|
5
|
+
|
|
6
|
+
# make sure to return only one object
|
|
7
|
+
assert _source, "No prediction information source found"
|
|
8
|
+
return _source
|
|
9
|
+
|
|
10
|
+
class InformationSourceManager(models.Manager):
|
|
11
|
+
def get_by_natural_key(self, name):
|
|
12
|
+
return self.get(name=name)
|
|
13
|
+
|
|
14
|
+
class InformationSource(models.Model):
|
|
15
|
+
objects = InformationSourceManager()
|
|
16
|
+
|
|
17
|
+
name = models.CharField(max_length=100)
|
|
18
|
+
name_de = models.CharField(max_length=100, blank=True, null=True)
|
|
19
|
+
name_en = models.CharField(max_length=100, blank=True, null=True)
|
|
20
|
+
|
|
21
|
+
def __str__(self):
|
|
22
|
+
return self.name
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .label import Label, LabelType, LabelSet
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
class LabelTypeManager(models.Manager):
|
|
4
|
+
def get_by_natural_key(self, name):
|
|
5
|
+
return self.get(name=name)
|
|
6
|
+
|
|
7
|
+
class LabelType(models.Model):
|
|
8
|
+
"""
|
|
9
|
+
A class representing a label type.
|
|
10
|
+
|
|
11
|
+
Attributes:
|
|
12
|
+
name (str): The name of the label type.
|
|
13
|
+
description (str): A description of the label type.
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
name = models.CharField(max_length=255)
|
|
17
|
+
description = models.TextField(blank=True, null=True)
|
|
18
|
+
|
|
19
|
+
objects = LabelTypeManager()
|
|
20
|
+
|
|
21
|
+
def natural_key(self):
|
|
22
|
+
return (self.name,)
|
|
23
|
+
|
|
24
|
+
def __str__(self):
|
|
25
|
+
return self.name
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class LabelManager(models.Manager):
|
|
29
|
+
def get_by_natural_key(self, name):
|
|
30
|
+
return self.get(name=name)
|
|
31
|
+
|
|
32
|
+
class Label(models.Model):
|
|
33
|
+
"""
|
|
34
|
+
A class representing a label.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
name (str): The name of the label.
|
|
38
|
+
label_type (LabelType): The type of the label.
|
|
39
|
+
description (str): A description of the label.
|
|
40
|
+
|
|
41
|
+
"""
|
|
42
|
+
name = models.CharField(max_length=255)
|
|
43
|
+
label_type = models.ForeignKey("LabelType", on_delete=models.CASCADE, related_name="labels")
|
|
44
|
+
description = models.TextField(blank=True, null=True)
|
|
45
|
+
|
|
46
|
+
objects = LabelManager()
|
|
47
|
+
|
|
48
|
+
def natural_key(self):
|
|
49
|
+
return (self.name,)
|
|
50
|
+
|
|
51
|
+
def __str__(self):
|
|
52
|
+
return self.name
|
|
53
|
+
|
|
54
|
+
class LabelSetManager(models.Manager):
|
|
55
|
+
def get_by_natural_key(self, name):
|
|
56
|
+
return self.get(name=name)
|
|
57
|
+
|
|
58
|
+
class LabelSet(models.Model):
|
|
59
|
+
"""
|
|
60
|
+
A class representing a label set.
|
|
61
|
+
|
|
62
|
+
Attributes:
|
|
63
|
+
name (str): The name of the label set.
|
|
64
|
+
description (str): A description of the label set.
|
|
65
|
+
|
|
66
|
+
"""
|
|
67
|
+
name = models.CharField(max_length=255)
|
|
68
|
+
description = models.TextField(blank=True, null=True)
|
|
69
|
+
version = models.IntegerField()
|
|
70
|
+
labels = models.ManyToManyField("Label", related_name="labels")
|
|
71
|
+
|
|
72
|
+
objects = LabelSetManager()
|
|
73
|
+
|
|
74
|
+
def natural_key(self):
|
|
75
|
+
return (self.name,)
|
|
76
|
+
|
|
77
|
+
def __str__(self):
|
|
78
|
+
return self.name
|
|
79
|
+
|
|
80
|
+
def get_labels_in_order(self):
|
|
81
|
+
"""
|
|
82
|
+
Get all labels in this label set as list in the correct order.
|
|
83
|
+
"""
|
|
84
|
+
return list(self.labels.all().order_by('id'))
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from ..label.label import Label
|
|
3
|
+
from ..annotation.image_classification import ImageClassificationAnnotation
|
|
4
|
+
|
|
5
|
+
class LegacyImage(models.Model):
|
|
6
|
+
# Add any other fields you need to store frame-related information
|
|
7
|
+
image = models.ImageField(upload_to="legacy_images") # Or some other field type, depending on how you're storing the frame
|
|
8
|
+
suffix = models.CharField(max_length=255)
|
|
9
|
+
# ImageClassificationAnnotation has a foreign key to this model (related name: image_classification_annotations)
|
|
10
|
+
|
|
11
|
+
def get_classification_annotations(self):
|
|
12
|
+
"""
|
|
13
|
+
Get all image classification annotations for this image.
|
|
14
|
+
"""
|
|
15
|
+
return ImageClassificationAnnotation.objects.filter(legacy_image=self)
|
|
16
|
+
|
|
17
|
+
def get_classification_annotations_by_label(self, label:Label):
|
|
18
|
+
"""
|
|
19
|
+
Get all image classification annotations for this image with the given label.
|
|
20
|
+
"""
|
|
21
|
+
return ImageClassificationAnnotation.objects.filter(legacy_image=self, label=label)
|
|
22
|
+
|
|
23
|
+
def get_classification_annotations_by_value(self, value:bool):
|
|
24
|
+
"""
|
|
25
|
+
Get all image classification annotations for this image with the given value.
|
|
26
|
+
"""
|
|
27
|
+
return ImageClassificationAnnotation.objects.filter(legacy_image=self, value=value)
|
|
28
|
+
|
|
29
|
+
def get_classification_annotations_by_label_and_value(self, label:Label, value:bool):
|
|
30
|
+
"""
|
|
31
|
+
Get all image classification annotations for this image with the given label and value.
|
|
32
|
+
"""
|
|
33
|
+
return ImageClassificationAnnotation.objects.filter(legacy_image=self, label=label, value=value)
|
|
34
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
# Serializer located in serializers/examination.py
|
|
4
|
+
class PatientExamination(models.Model):
|
|
5
|
+
patient = models.ForeignKey('Patient', on_delete=models.CASCADE, related_name='patient_examinations')
|
|
6
|
+
examination = models.ForeignKey('Examination', on_delete=models.CASCADE, null = True, blank = True)
|
|
7
|
+
video = models.OneToOneField('Video', on_delete=models.CASCADE, null = True, blank = True, related_name='patient_examination')
|
|
8
|
+
report_file = models.OneToOneField('ReportFile', on_delete=models.CASCADE, null = True, blank = True, related_name='patient_examination')
|
|
9
|
+
|
|
10
|
+
class Meta:
|
|
11
|
+
verbose_name = 'Patient Examination'
|
|
12
|
+
verbose_name_plural = 'Patient Examinations'
|
|
13
|
+
ordering = ['patient', 'examination']
|
|
14
|
+
|
|
15
|
+
def __str__(self):
|
|
16
|
+
return f"{self.patient} - {self.report_file}"
|
|
17
|
+
|
|
18
|
+
def find_matching_video_from_patient(self):
|
|
19
|
+
"""
|
|
20
|
+
Finds a video for this patient examination based on the patient's videos.
|
|
21
|
+
For this, the videos date must be the same as the report file's date.
|
|
22
|
+
#TODO add more criteria for matching: Examination type
|
|
23
|
+
"""
|
|
24
|
+
videos = self.patient.video_set.filter(date=self.report_file.date, patient_examination__isnull=True)
|
|
25
|
+
if videos:
|
|
26
|
+
if len(videos) > 1:
|
|
27
|
+
print(f"Warning: Found more than one video for patient {self.patient} on date {self.report_file.date}. Choosing the first one.")
|
|
28
|
+
return videos[0]
|
|
29
|
+
else:
|
|
30
|
+
videos = self.patient.video_set.filter(patient_examination__isnull=True)
|
|
31
|
+
if len(videos)==1:
|
|
32
|
+
return videos[0]
|
|
33
|
+
|
|
34
|
+
return None
|
|
35
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from ..person import Person
|
|
3
|
+
from rest_framework import serializers
|
|
4
|
+
|
|
5
|
+
class Examiner(Person):
|
|
6
|
+
center = models.ForeignKey('Center', on_delete=models.CASCADE, blank=True, null=True)
|
|
7
|
+
|
|
8
|
+
def __str__(self):
|
|
9
|
+
return self.first_name + " " + self.last_name
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ExaminerSerializer(serializers.ModelSerializer):
|
|
13
|
+
|
|
14
|
+
class Meta:
|
|
15
|
+
model = Examiner
|
|
16
|
+
fields = '__all__'
|