fix image hashes (possible)

This commit is contained in:
Ross
2023-08-21 14:05:54 +01:00
parent fcd77514eb
commit 48cba9353e
6 changed files with 119 additions and 42 deletions
+36 -9
View File
@@ -21,17 +21,25 @@ from .models import Case, DuplicateDicom, Series, SeriesImage, UncategorisedDico
router = Router() router = Router()
class SeriesSchema(ModelSchema): class SeriesSchema(ModelSchema):
case_id: List[int] = [] case_id: List[int] = []
class Config: class Config:
model = Series model = Series
model_fields = ["id", "modality", "examination", "series_instance_uid", "author"] model_fields = [
"id",
"modality",
"examination",
"series_instance_uid",
"author",
]
@staticmethod @staticmethod
def resolve_case_id(obj): def resolve_case_id(obj):
return [i.id for i in obj.case.all()] return [i.id for i in obj.case.all()]
class CaseSchema(ModelSchema): class CaseSchema(ModelSchema):
class Config: class Config:
model = Case model = Case
@@ -51,6 +59,7 @@ def upload_dicom(request, files: List[UploadedFile] = File(...)):
uploaded.append((file.name, ud.image_md5_hash)) uploaded.append((file.name, ud.image_md5_hash))
except DuplicateDicom: except DuplicateDicom:
print(ud.check_for_duplicates(ud.image_md5_hash))
duplicate.append((file.name, ud.image_md5_hash)) duplicate.append((file.name, ud.image_md5_hash))
pass pass
@@ -75,10 +84,12 @@ def uncategorised_dicoms(request):
tags = d.get_basic_dicom_tags() tags = d.get_basic_dicom_tags()
data[tags["SeriesInstanceUID"]].append(tags) data[tags["SeriesInstanceUID"]].append(tags)
data["image_hash"].append(d.image_md5_hash)
return data 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) dicoms = UncategorisedDicom.objects.filter(user=request.user)
data = defaultdict(list) data = defaultdict(list)
@@ -89,7 +100,6 @@ def import_dicoms_helper(request, case_id: int | None=None):
series_list = [] series_list = []
for series_uid in data: for series_uid in data:
modality = get_object_or_404(Modality, short_code=tags["Modality"]) modality = get_object_or_404(Modality, short_code=tags["Modality"])
tags = data[series_uid][0][1] 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) series.case.add(case_id)
for dicom, dicom_tags in data[series_uid]: 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() series_image.save()
#dicom.image.delete() # dicom.image.delete()
dicom.delete() dicom.delete()
series_list.append(series) 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]) @router.get("/import_dicoms", auth=django_auth, response=List[SeriesSchema])
def import_dicoms(request): def import_dicoms(request):
return import_dicoms_helper(request) return import_dicoms_helper(request)
@router.get("/import_dicoms/{case_id}", auth=django_auth, response=List[SeriesSchema]) @router.get("/import_dicoms/{case_id}", auth=django_auth, response=List[SeriesSchema])
def import_dicoms_case(request, case_id: int): def import_dicoms_case(request, case_id: int):
return import_dicoms_helper(request, case_id=case_id) return import_dicoms_helper(request, case_id=case_id)
@router.get("/orphan_series", auth=django_auth, response=List[SeriesSchema]) @router.get("/orphan_series", auth=django_auth, response=List[SeriesSchema])
def orphan_series(request): 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) return request.user.series.filter(case=None)
@router.get("/series_remove_duplicate_images", auth=django_auth) @router.get("/series_remove_duplicate_images", auth=django_auth)
def series_remove_duplicate_images(request, series_id: int): def series_remove_duplicate_images(request, series_id: int):
series = get_object_or_404(Series, pk=series_id) series = get_object_or_404(Series, pk=series_id)
img_ids = set() img_ids = set()
@@ -154,14 +168,17 @@ def series_remove_duplicate_images(request, series_id: int):
return len(dupes) return len(dupes)
@router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema]) @router.get("/get_cases_user", auth=django_auth, response=List[CaseSchema])
def get_cases_user(request): def get_cases_user(request):
print("HELLO") print("HELLO")
return Case.objects.filter(author=request.user) return Case.objects.filter(author=request.user)
@router.get("/check_image_hash/{hash}", auth=django_auth) @router.get("/check_image_hash/{hash}", auth=django_auth)
def check_image_hash(request, hash: str): def check_image_hash(request, hash: str):
try: try:
print(hash)
series_image = SeriesImage.objects.get(image_md5_hash=hash) series_image = SeriesImage.objects.get(image_md5_hash=hash)
data = { data = {
"status": "success", "status": "success",
@@ -172,3 +189,13 @@ def check_image_hash(request, hash: str):
except SeriesImage.DoesNotExist: except SeriesImage.DoesNotExist:
data = {"status": "success", "id": False} data = {"status": "success", "id": False}
return data 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)
@@ -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',
),
]
+25 -10
View File
@@ -32,7 +32,7 @@ from sortedm2m.fields import SortedManyToManyField
import string import string
from collections import defaultdict 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 ( from generic.models import (
@@ -801,10 +801,9 @@ class SelfReview(models.Model):
class UncategorisedDicom(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) image = models.FileField(upload_to=uncategorised_dicom_directory_path)
user = models.ForeignKey( user = models.ForeignKey(
settings.AUTH_USER_MODEL, settings.AUTH_USER_MODEL,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@@ -816,24 +815,39 @@ class UncategorisedDicom(models.Model):
) )
image_md5_hash = models.CharField(max_length=32, null=True, blank=True) 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): def save(self, *args, **kwargs):
"""Override save method to add image hash""" """Override save method to add image hash"""
if self.image: 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 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 raise DuplicateDicom
if SeriesImage.objects.filter(image_md5_hash=image_hash):
raise DuplicateDicom
# Hack for tests # Hack for tests
if image_hash != "12345ABCD": if image_hash != "1234":
super().save(*args, **kwargs) # Call the "real" save() method. super().save(*args, **kwargs) # Call the "real" save() method.
def get_dicom_info(self): def get_dicom_info(self):
@@ -869,5 +883,6 @@ class UncategorisedDicom(models.Model):
except pydicom.errors.InvalidDicomError: except pydicom.errors.InvalidDicomError:
return None return None
class DuplicateDicom(Exception): class DuplicateDicom(Exception):
pass pass
+2 -2
View File
@@ -69,14 +69,14 @@
title="orders dicom by uploaded filename">Order by uploaded filename</a> title="orders dicom by uploaded filename">Order by uploaded filename</a>
</div> </div>
{% for image in series.images.all %} {% 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 %} {% if image.image %}
[{{image.image.size|filesizeformat}}] [{{image.image.size|filesizeformat}}]
{% endif %} {% endif %}
{{image.image_md5_hash}} {{image.image_md5_hash}} ({{image.is_dicom}})
<br /> <br />
{% comment %} {{image.get_dicom_info|safe}}<br /> {% endcomment %} {% comment %} {{image.get_dicom_info|safe}}<br /> {% endcomment %}
+3
View File
@@ -205,6 +205,8 @@ class SeriesImageBase(models.Model):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""Override save method to add image hash""" """Override save method to add image hash"""
if self.image: if self.image:
if not self.image_md5_hash:
image_hash, is_dicom = get_image_hash(self.image) image_hash, is_dicom = get_image_hash(self.image)
self.is_dicom = is_dicom self.is_dicom = is_dicom
@@ -214,6 +216,7 @@ class SeriesImageBase(models.Model):
if image_hash != "12345ABCD": if image_hash != "12345ABCD":
super().save(*args, **kwargs) # Call the "real" save() method. super().save(*args, **kwargs) # Call the "real" save() method.
super().save(*args, **kwargs) # Call the "real" save() method.
class SeriesBase(models.Model): class SeriesBase(models.Model):
info = models.TextField( info = models.TextField(
+17 -2
View File
@@ -155,9 +155,24 @@ def print_dicom(dataset, indent=0):
repr_value)) repr_value))
return "\n<br/>".join(l) return "\n<br/>".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): def get_image_hash(img) -> (str, bool):
is_dicom = False is_dicom = False
# Try and read the file as a dicom # Try and read the file as a dicom
hash = "1234"
try: try:
# and generate a hash from the pixel data # and generate a hash from the pixel data
# TODO: improve? # TODO: improve?
@@ -181,12 +196,12 @@ def get_image_hash(img) -> (str, bool):
is_dicom = True is_dicom = True
# ---- # ----
except pydicom.errors.InvalidDicomError: except pydicom.errors.InvalidDicomError as e:
try: # This is horrible (but needed for current unit tests) try: # This is horrible (but needed for current unit tests)
# (we use a temporary file that breaks here) # (we use a temporary file that breaks here)
img.file.open() img.file.open()
hash = hashlib.md5(img.read()).hexdigest() hash = hashlib.md5(img.read()).hexdigest()
except AttributeError: except AttributeError:
return "12345ABCD", False return hash, False
return hash, is_dicom return hash, is_dicom