start adding uncat dicom

This commit is contained in:
Ross
2023-08-14 10:15:56 +01:00
parent c3a735fa52
commit 9ae1874073
8 changed files with 196 additions and 42 deletions
@@ -0,0 +1,37 @@
# Generated by Django 4.1.4 on 2023-08-14 09:15
import atlas.models
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('atlas', '0011_seriesimage_dicom_tags_ohif'),
]
operations = [
migrations.AddField(
model_name='seriesimage',
name='image_md5_hash',
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.AddField(
model_name='seriesimage',
name='is_dicom',
field=models.BooleanField(default=False),
),
migrations.CreateModel(
name='UncategorisedDicom',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image', models.FileField(upload_to=atlas.models.uncategorised_dicom_directory_path)),
('image_md5_hash', models.CharField(blank=True, max_length=32, null=True)),
('series', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='imported_dicoms', to='atlas.series')),
('user', models.ForeignKey(help_text='The user that uploaded the file', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
+60 -4
View File
@@ -77,6 +77,10 @@ def image_directory_path(instance, filename):
return "atlas/picture/{0}".format(filename) return "atlas/picture/{0}".format(filename)
def uncategorised_dicom_directory_path(instance, filename):
return "atlas/dicom/{0}".format(filename)
def findMiddle(input_list): def findMiddle(input_list):
middle = float(len(input_list)) / 2 middle = float(len(input_list)) / 2
if middle % 2 != 0: if middle % 2 != 0:
@@ -409,7 +413,7 @@ class SeriesImage(SeriesImageBase):
except InvalidDicomError: except InvalidDicomError:
return {} return {}
#def get_image_dicom_json(self, image_index): # def get_image_dicom_json(self, image_index):
# try: # try:
# with pydicom.dcmread(self.image) as ds: # with pydicom.dcmread(self.image) as ds:
# return extract_image_dicom_json_from_ds(ds, url=f"{REMOTE_URL}{self.image.url}") # return extract_image_dicom_json_from_ds(ds, url=f"{REMOTE_URL}{self.image.url}")
@@ -511,7 +515,6 @@ class Series(SeriesBase):
for series_n, image in enumerate(self.images.all()): for series_n, image in enumerate(self.images.all()):
ds = image.get_dicom_data() ds = image.get_dicom_data()
if series_n == 0: if series_n == 0:
for tag in to_keep: for tag in to_keep:
if tag in ds: if tag in ds:
val = ds[tag].value val = ds[tag].value
@@ -522,9 +525,11 @@ class Series(SeriesBase):
series_json[tag] = val series_json[tag] = val
instances.append( instances.append(
extract_image_dicom_json_from_ds(ds, url=f"{REMOTE_URL}{image.image.url}", image_index=series_n) extract_image_dicom_json_from_ds(
ds, url=f"{REMOTE_URL}{image.image.url}", image_index=series_n
)
) )
#else: # else:
# instances.append(image.get_image_dicom_json(image_index)) # instances.append(image.get_image_dicom_json(image_index))
description = f"{self.examination} ({self.plane})" description = f"{self.examination} ({self.plane})"
@@ -788,3 +793,54 @@ class SelfReview(models.Model):
id=self.pk, id=self.pk,
edit_url=edit_url, edit_url=edit_url,
) )
class UncategorisedDicom(models.Model):
#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,
help_text="The user that uploaded the file",
)
series = models.ForeignKey(
"Series", related_name="imported_dicoms", on_delete=models.SET_NULL, null=True
)
image_md5_hash = models.CharField(max_length=32, null=True, blank=True)
def get_dicom_info(self):
try:
ds = pydicom.read_file(self.image)
return ds
except pydicom.errors.InvalidDicomError:
return None
def get_basic_dicom_tags(self):
try:
ds = pydicom.read_file(self.image)
to_include = (
"StudyDescription",
"Modality",
"ProtocolName",
"SeriesDescription",
"SeriesInstanceUID",
"BodyPartExamined",
"SliceThickness",
)
tags = {}
for tag in to_include:
if tag in ds:
tags[tag] = ds[tag]
else:
tags[tag] = ""
return ds
except pydicom.errors.InvalidDicomError:
return None
+1
View File
@@ -141,6 +141,7 @@ urlpatterns = [
views.case_dicom_json, views.case_dicom_json,
name="case_dicom_json", name="case_dicom_json",
), ),
path("uncategorised_dicoms/", views.uncategorised_dicoms, name="uncategorised_dicoms_view"),
path("condition/", views.ConditionView.as_view(), name="condition_view"), path("condition/", views.ConditionView.as_view(), name="condition_view"),
path("categories/", views.categories_list, name="categories_list"), path("categories/", views.categories_list, name="categories_list"),
+14 -1
View File
@@ -63,6 +63,7 @@ from .models import (
Subspecialty, Subspecialty,
SeriesFinding, SeriesFinding,
SeriesImage, SeriesImage,
UncategorisedDicom,
UserReportAnswer, UserReportAnswer,
) )
from .tables import ( from .tables import (
@@ -1877,4 +1878,16 @@ class AddSelfReview(CreateView):
# #
# response = super().form_valid(form) # response = super().form_valid(form)
# #
# return response # return response
@login_required
def uncategorised_dicoms(request, pk):
dicoms = UncategorisedDicom.objects.filter(user=request.User)
data = {}
for d in dicoms:
d[d.image] = d.get_basic_dicom_tags()
return JsonResponse(data)
+16 -1
View File
@@ -26,7 +26,7 @@ from django.contrib.auth.models import User
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.dispatch import receiver from django.dispatch import receiver
from helpers.images import pretty_print_dicom from helpers.images import get_image_hash, pretty_print_dicom
from rad.settings import REMOTE_URL from rad.settings import REMOTE_URL
from django.utils.html import format_html from django.utils.html import format_html
@@ -187,6 +187,9 @@ class SeriesImageBase(models.Model):
) )
dicom_tags_ohif = models.JSONField(help_text="Holds the dicom tags required for the OHIF viewer", blank=True, null=True) dicom_tags_ohif = models.JSONField(help_text="Holds the dicom tags required for the OHIF viewer", blank=True, null=True)
image_md5_hash = models.CharField(max_length=32, null=True, blank=True)
is_dicom = models.BooleanField(default=False)
class Meta: class Meta:
ordering = ["position"] ordering = ["position"]
abstract = True abstract = True
@@ -198,6 +201,18 @@ class SeriesImageBase(models.Model):
info = "File is not a dicom." info = "File is not a dicom."
return info return info
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
# Hack for tests
if image_hash != "12345ABCD":
super().save(*args, **kwargs) # Call the "real" save() method.
class SeriesBase(models.Model): class SeriesBase(models.Model):
info = models.TextField( info = models.TextField(
+38 -1
View File
@@ -1,4 +1,5 @@
import base64 import base64
import hashlib
import mimetypes import mimetypes
from io import BytesIO from io import BytesIO
@@ -152,4 +153,40 @@ def print_dicom(dataset, indent=0):
l.append("{0:s} {1:s} = {2:s}".format(indent_string, l.append("{0:s} {1:s} = {2:s}".format(indent_string,
data_element.name, data_element.name,
repr_value)) repr_value))
return "\n<br/>".join(l) return "\n<br/>".join(l)
def get_image_hash(img) -> (str, bool):
is_dicom = False
# Try and read the file as a dicom
try:
# and generate a hash from the pixel data
# TODO: improve?
dataset = pydicom.dcmread(img)
# flatten = dataset.pixel_array.astype(str).flatten()
# print("flatteded")
# pre_join = ",".join(flatten)
# print(pre_join)
# hash = hashlib.md5(pre_join.encode()).hexdigest()
# ----
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()
is_dicom = True
# ----
except pydicom.errors.InvalidDicomError:
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, is_dicom
@@ -0,0 +1,23 @@
# Generated by Django 4.1.4 on 2023-08-14 09:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('longs', '0006_longseriesimage_dicom_tags_ohif'),
]
operations = [
migrations.AddField(
model_name='longseriesimage',
name='image_md5_hash',
field=models.CharField(blank=True, max_length=32, null=True),
),
migrations.AddField(
model_name='longseriesimage',
name='is_dicom',
field=models.BooleanField(default=False),
),
]
+7 -35
View File
@@ -21,7 +21,7 @@ from sortedm2m.fields import SortedManyToManyField
import string import string
from collections import defaultdict from collections import defaultdict
from helpers.images import image_as_base64 from helpers.images import get_image_hash, image_as_base64
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
@@ -575,42 +575,14 @@ class RapidImage(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"""
# TODO: consider moving to signal to reuse across apps
if self.image: if self.image:
# Try and read the file as a dicom image_hash, is_dicom = get_image_hash(self.image)
try: self.is_dicom = is_dicom
# and generate a hash from the pixel data self.image_md5_hash = image_hash
# TODO: improve?
dataset = pydicom.dcmread(self.image)
# flatten = dataset.pixel_array.astype(str).flatten()
# print("flatteded")
# pre_join = ",".join(flatten)
# print(pre_join)
# hash = hashlib.md5(pre_join.encode()).hexdigest()
# ----
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() # Hack for tests
# ---- if image_hash != "12345ABCD":
super().save(*args, **kwargs) # Call the "real" save() method.
self.is_dicom = True
except pydicom.errors.InvalidDicomError:
try: # This is horrible (but needed for current unit tests)
# (we use a temporary file that breaks here)
self.image.file.open()
hash = hashlib.md5(self.image.read()).hexdigest()
except AttributeError:
return
self.image_md5_hash = hash
super().save(*args, **kwargs) # Call the "real" save() method.
class RapidCreationDefault(models.Model): class RapidCreationDefault(models.Model):