diff --git a/atlas/api.py b/atlas/api.py
index 5938cceb..4ed20578 100644
--- a/atlas/api.py
+++ b/atlas/api.py
@@ -21,17 +21,25 @@ from .models import Case, DuplicateDicom, Series, SeriesImage, UncategorisedDico
router = Router()
+
class SeriesSchema(ModelSchema):
case_id: List[int] = []
class Config:
model = Series
- model_fields = ["id", "modality", "examination", "series_instance_uid", "author"]
+ model_fields = [
+ "id",
+ "modality",
+ "examination",
+ "series_instance_uid",
+ "author",
+ ]
@staticmethod
def resolve_case_id(obj):
return [i.id for i in obj.case.all()]
+
class CaseSchema(ModelSchema):
class Config:
model = Case
@@ -51,6 +59,7 @@ def upload_dicom(request, files: List[UploadedFile] = File(...)):
uploaded.append((file.name, ud.image_md5_hash))
except DuplicateDicom:
+ print(ud.check_for_duplicates(ud.image_md5_hash))
duplicate.append((file.name, ud.image_md5_hash))
pass
@@ -75,10 +84,12 @@ def uncategorised_dicoms(request):
tags = d.get_basic_dicom_tags()
data[tags["SeriesInstanceUID"]].append(tags)
+ data["image_hash"].append(d.image_md5_hash)
return data
-def import_dicoms_helper(request, case_id: int | None=None):
+
+def import_dicoms_helper(request, case_id: int | None = None):
dicoms = UncategorisedDicom.objects.filter(user=request.user)
data = defaultdict(list)
@@ -89,7 +100,6 @@ def import_dicoms_helper(request, case_id: int | None=None):
series_list = []
for series_uid in data:
-
modality = get_object_or_404(Modality, short_code=tags["Modality"])
tags = data[series_uid][0][1]
@@ -111,9 +121,14 @@ def import_dicoms_helper(request, case_id: int | None=None):
series.case.add(case_id)
for dicom, dicom_tags in data[series_uid]:
- series_image = SeriesImage(image=dicom.image, series=series)
+ series_image = SeriesImage(
+ image=dicom.image,
+ series=series,
+ is_dicom=True,
+ image_md5_hash=dicom.image_md5_hash,
+ )
series_image.save()
- #dicom.image.delete()
+ # dicom.image.delete()
dicom.delete()
series_list.append(series)
@@ -123,23 +138,22 @@ def import_dicoms_helper(request, case_id: int | None=None):
@router.get("/import_dicoms", auth=django_auth, response=List[SeriesSchema])
def import_dicoms(request):
-
return import_dicoms_helper(request)
+
@router.get("/import_dicoms/{case_id}", auth=django_auth, response=List[SeriesSchema])
def import_dicoms_case(request, case_id: int):
-
return import_dicoms_helper(request, case_id=case_id)
+
@router.get("/orphan_series", auth=django_auth, response=List[SeriesSchema])
def orphan_series(request):
- #print(request.user.series.filter(case=None))
+ # print(request.user.series.filter(case=None))
return request.user.series.filter(case=None)
@router.get("/series_remove_duplicate_images", auth=django_auth)
def series_remove_duplicate_images(request, series_id: int):
-
series = get_object_or_404(Series, pk=series_id)
img_ids = set()
@@ -154,21 +168,34 @@ def series_remove_duplicate_images(request, series_id: int):
return len(dupes)
+
@router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema])
def get_cases_user(request):
print("HELLO")
return Case.objects.filter(author=request.user)
+
@router.get("/check_image_hash/{hash}", auth=django_auth)
def check_image_hash(request, hash: str):
- try:
- series_image = SeriesImage.objects.get(image_md5_hash=hash)
- data = {
- "status": "success",
- "id": series_image.pk,
- "url": series_image.series.get_absolute_url(),
- }
- return data
- except SeriesImage.DoesNotExist:
- data = {"status": "success", "id": False}
- return data
\ No newline at end of file
+ try:
+ print(hash)
+ series_image = SeriesImage.objects.get(image_md5_hash=hash)
+ data = {
+ "status": "success",
+ "id": series_image.pk,
+ "url": series_image.series.get_absolute_url(),
+ }
+ return data
+ except SeriesImage.DoesNotExist:
+ data = {"status": "success", "id": False}
+ return data
+
+@router.get("/generate_image_hash/{id}", auth=django_auth)
+def generate_image_hash(request, id: int):
+ series_image = SeriesImage.objects.get(pk=id)
+
+ series_image.image_md5_hash = ""
+
+ series_image.save()
+
+ print(series_image)
diff --git a/atlas/migrations/0016_remove_uncategoriseddicom_is_dicom.py b/atlas/migrations/0016_remove_uncategoriseddicom_is_dicom.py
new file mode 100644
index 00000000..28b07780
--- /dev/null
+++ b/atlas/migrations/0016_remove_uncategoriseddicom_is_dicom.py
@@ -0,0 +1,17 @@
+# Generated by Django 4.1.4 on 2023-08-21 12:38
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('atlas', '0015_alter_seriesimage_series'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='uncategoriseddicom',
+ name='is_dicom',
+ ),
+ ]
diff --git a/atlas/models.py b/atlas/models.py
index 020fc61c..157f5b42 100644
--- a/atlas/models.py
+++ b/atlas/models.py
@@ -32,7 +32,7 @@ from sortedm2m.fields import SortedManyToManyField
import string
from collections import defaultdict
-from helpers.images import get_image_hash, image_as_base64, pretty_print_dicom
+from helpers.images import get_image_dicom_hash, get_image_hash, image_as_base64, pretty_print_dicom
from generic.models import (
@@ -801,10 +801,9 @@ class SelfReview(models.Model):
class UncategorisedDicom(models.Model):
- #We use image to maintain consitency across apps
+ # We use image to maintain consitency across apps
image = models.FileField(upload_to=uncategorised_dicom_directory_path)
-
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
@@ -816,24 +815,39 @@ class UncategorisedDicom(models.Model):
)
image_md5_hash = models.CharField(max_length=32, null=True, blank=True)
- is_dicom = models.BooleanField(default=False)
+
+ def check_for_duplicates(self, image_hash):
+ duplicate = None
+
+ if obj := UncategorisedDicom.objects.filter(image_md5_hash=image_hash).first():
+ duplicate = obj
+
+ print(image_hash)
+
+ if obj := SeriesImage.objects.filter(image_md5_hash=image_hash).first():
+ duplicate = obj
+
+
+ return duplicate
def save(self, *args, **kwargs):
"""Override save method to add image hash"""
if self.image:
- image_hash, is_dicom = get_image_hash(self.image)
+ # if self.check_for_duplicates():
+ # return DuplicateDicom
+ image_hash = get_image_dicom_hash(self.image)
+
- self.is_dicom = is_dicom
self.image_md5_hash = image_hash
- if UncategorisedDicom.objects.filter(image_md5_hash=image_hash):
+ duplicate = self.check_for_duplicates(image_hash)
+
+ if duplicate is not None:
raise DuplicateDicom
- if SeriesImage.objects.filter(image_md5_hash=image_hash):
- raise DuplicateDicom
-
+
# Hack for tests
- if image_hash != "12345ABCD":
+ if image_hash != "1234":
super().save(*args, **kwargs) # Call the "real" save() method.
def get_dicom_info(self):
@@ -869,5 +883,6 @@ class UncategorisedDicom(models.Model):
except pydicom.errors.InvalidDicomError:
return None
+
class DuplicateDicom(Exception):
- pass
\ No newline at end of file
+ pass
diff --git a/atlas/templates/atlas/series_viewer.html b/atlas/templates/atlas/series_viewer.html
index febfafa1..781e4506 100755
--- a/atlas/templates/atlas/series_viewer.html
+++ b/atlas/templates/atlas/series_viewer.html
@@ -69,14 +69,14 @@
title="orders dicom by uploaded filename">Order by uploaded filename
{% for image in series.images.all %}
- {{image.image.url}}, pos: {{image.position}}, {{image.upload_filename}}
+ [{{ image.id }}] {{image.image.url}}, pos: {{image.position}}, {{image.upload_filename}}
{% if image.image %}
[{{image.image.size|filesizeformat}}]
{% endif %}
- {{image.image_md5_hash}}
+ {{image.image_md5_hash}} ({{image.is_dicom}})
{% comment %} {{image.get_dicom_info|safe}}
{% endcomment %}
diff --git a/generic/models.py b/generic/models.py
index d43ed70f..a9f745b7 100644
--- a/generic/models.py
+++ b/generic/models.py
@@ -205,15 +205,18 @@ class SeriesImageBase(models.Model):
def save(self, *args, **kwargs):
"""Override save method to add image hash"""
if self.image:
- image_hash, is_dicom = get_image_hash(self.image)
- self.is_dicom = is_dicom
- self.image_md5_hash = image_hash
+ if not self.image_md5_hash:
+ image_hash, is_dicom = get_image_hash(self.image)
+
+ self.is_dicom = is_dicom
+ self.image_md5_hash = image_hash
- # Hack for tests
- if image_hash != "12345ABCD":
- super().save(*args, **kwargs) # Call the "real" save() method.
+ # Hack for tests
+ if image_hash != "12345ABCD":
+ super().save(*args, **kwargs) # Call the "real" save() method.
+ super().save(*args, **kwargs) # Call the "real" save() method.
class SeriesBase(models.Model):
info = models.TextField(
diff --git a/helpers/images.py b/helpers/images.py
index b600a23c..482c6df4 100644
--- a/helpers/images.py
+++ b/helpers/images.py
@@ -155,9 +155,24 @@ def print_dicom(dataset, indent=0):
repr_value))
return "\n
".join(l)
+def get_image_dicom_hash(img) -> (str, bool):
+ dataset = pydicom.dcmread(img)
+ md5 = hashlib.md5()
+ first = True
+ for i in dataset.pixel_array.astype(str).flatten():
+ if first:
+ first = False
+ md5.update(f"{i}".encode())
+ else:
+ md5.update(f",{i}".encode())
+
+ hash = md5.hexdigest()
+ return hash
+
def get_image_hash(img) -> (str, bool):
is_dicom = False
# Try and read the file as a dicom
+ hash = "1234"
try:
# and generate a hash from the pixel data
# TODO: improve?
@@ -181,12 +196,12 @@ def get_image_hash(img) -> (str, bool):
is_dicom = True
# ----
- except pydicom.errors.InvalidDicomError:
+ except pydicom.errors.InvalidDicomError as e:
try: # This is horrible (but needed for current unit tests)
# (we use a temporary file that breaks here)
img.file.open()
hash = hashlib.md5(img.read()).hexdigest()
except AttributeError:
- return "12345ABCD", False
+ return hash, False
return hash, is_dicom
\ No newline at end of file