add basic dicom diff support
This commit is contained in:
+2
-2
@@ -285,7 +285,7 @@ def series_split_by_tag(request, series_id: int, dicom_tag:str):
|
|||||||
print(image)
|
print(image)
|
||||||
ds = image.get_dicom_data()
|
ds = image.get_dicom_data()
|
||||||
|
|
||||||
print("2a", ds[dicom_tag])
|
#print("2a", ds[dicom_tag])
|
||||||
|
|
||||||
if dicom_tag in ds:
|
if dicom_tag in ds:
|
||||||
val = ds[dicom_tag].value
|
val = ds[dicom_tag].value
|
||||||
@@ -295,7 +295,7 @@ def series_split_by_tag(request, series_id: int, dicom_tag:str):
|
|||||||
else:
|
else:
|
||||||
image_map[val].append(image)
|
image_map[val].append(image)
|
||||||
else:
|
else:
|
||||||
return [f"{dicom_tag} not found"]
|
return [f"{dicom_tag} not found {image}"]
|
||||||
|
|
||||||
new_series = []
|
new_series = []
|
||||||
case = series.case.all()
|
case = series.case.all()
|
||||||
|
|||||||
@@ -92,6 +92,9 @@
|
|||||||
<summary>
|
<summary>
|
||||||
Image details
|
Image details
|
||||||
</summary>
|
</summary>
|
||||||
|
<form hx-post="{% url 'atlas:image_diff' %}"
|
||||||
|
hx-target="#temp"
|
||||||
|
>
|
||||||
{% for image in series.get_images %}
|
{% for image in series.get_images %}
|
||||||
[{{ image.id }}] <a href='{% url "atlas:series_image_dicom" image.pk %}'>{{image.image.url}}</a>, pos: {{image.position}}, {{image.upload_filename}}
|
[{{ image.id }}] <a href='{% url "atlas:series_image_dicom" image.pk %}'>{{image.image.url}}</a>, pos: {{image.position}}, {{image.upload_filename}}
|
||||||
|
|
||||||
@@ -102,15 +105,34 @@
|
|||||||
|
|
||||||
{{image.image_md5_hash}} ({{image.is_dicom}}) {{image.image_blake3_hash}}
|
{{image.image_md5_hash}} ({{image.is_dicom}}) {{image.image_blake3_hash}}
|
||||||
|
|
||||||
|
<input type="checkbox" name="image-id" value="{{image.id}}">
|
||||||
|
|
||||||
<br />
|
<br />
|
||||||
{% comment %} {{image.get_dicom_info|safe}}<br /> {% endcomment %}
|
{% comment %} {{image.get_dicom_info|safe}}<br /> {% endcomment %}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
<button>Diff files</button>
|
||||||
|
</form>
|
||||||
|
<span id="temp"></span>
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<p>Total image size: {{series.get_total_image_size|filesizeformat}}</p>
|
<p>Total image size: {{series.get_total_image_size|filesizeformat}}</p>
|
||||||
<p><a href="{% url 'atlas:series_download' pk=series.pk %}">Download images</a></p>
|
<p><a href="{% url 'atlas:series_download' pk=series.pk %}">Download images</a></p>
|
||||||
</details>
|
</details>
|
||||||
|
<span id="diff-controls">show <span id="diff-show-all" _="on click
|
||||||
|
log '1'
|
||||||
|
for row in .diff.rows
|
||||||
|
show row
|
||||||
|
end">all</span> <span id="diff-show-diff" _="
|
||||||
|
on click
|
||||||
|
log 'go'
|
||||||
|
for row in <.diff tr/>
|
||||||
|
log row.children[0].innerHTML
|
||||||
|
if row.children[0].innerHTML is empty
|
||||||
|
hide row
|
||||||
|
end
|
||||||
|
end
|
||||||
|
">diff</span></span>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
@@ -208,6 +230,14 @@
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$("#diff-show-all").click(() => {
|
||||||
|
console.log("Helol");
|
||||||
|
})
|
||||||
|
$("#diff-show-diff").click(() => {
|
||||||
|
console.log("diff");
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
{% if editing_finding > 0 %}
|
{% if editing_finding > 0 %}
|
||||||
$("#hidden-form").show()
|
$("#hidden-form").show()
|
||||||
setTimeout(function() {
|
setTimeout(function() {
|
||||||
|
|||||||
@@ -214,6 +214,11 @@ urlpatterns = [
|
|||||||
views.series_order_dicom,
|
views.series_order_dicom,
|
||||||
name="series_order_dicom",
|
name="series_order_dicom",
|
||||||
),
|
),
|
||||||
|
path(
|
||||||
|
"images/<diff",
|
||||||
|
views.image_diff,
|
||||||
|
name="image_diff",
|
||||||
|
),
|
||||||
path(
|
path(
|
||||||
"series/<int:pk>/download",
|
"series/<int:pk>/download",
|
||||||
views.SeriesImagesZipView.as_view(),
|
views.SeriesImagesZipView.as_view(),
|
||||||
|
|||||||
+29
-1
@@ -17,6 +17,7 @@ from django.core.exceptions import (
|
|||||||
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
|
||||||
from django.views.generic import View
|
from django.views.generic import View
|
||||||
from django.views.generic.detail import DetailView
|
from django.views.generic.detail import DetailView
|
||||||
|
import pydicom
|
||||||
from generic.mixins import SuperuserRequiredMixin
|
from generic.mixins import SuperuserRequiredMixin
|
||||||
|
|
||||||
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
from django.views.generic.edit import CreateView, UpdateView, DeleteView, FormView
|
||||||
@@ -116,7 +117,7 @@ import plotly.express as px
|
|||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from helpers.images import image_as_base64
|
from helpers.images import image_as_base64, print_dicom
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
from copy import deepcopy
|
from copy import deepcopy
|
||||||
@@ -130,6 +131,7 @@ import reversion
|
|||||||
from rest_framework import viewsets
|
from rest_framework import viewsets
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import difflib
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -1039,6 +1041,32 @@ def series_order_dicom(request, pk):
|
|||||||
|
|
||||||
return redirect("atlas:series_detail", pk=pk)
|
return redirect("atlas:series_detail", pk=pk)
|
||||||
|
|
||||||
|
@login_required
|
||||||
|
def image_diff(request):
|
||||||
|
if request.htmx:
|
||||||
|
|
||||||
|
image_ids = request.POST.getlist("image-id")
|
||||||
|
|
||||||
|
first: SeriesImage = get_object_or_404(SeriesImage, pk=image_ids[0])
|
||||||
|
second: SeriesImage = get_object_or_404(SeriesImage, pk=image_ids[1])
|
||||||
|
|
||||||
|
first_ds = pydicom.read_file(first.image.file)
|
||||||
|
#del first_ds.PixelData
|
||||||
|
#j1 = first_ds.to_json_dict()
|
||||||
|
f = print_dicom(first_ds, join=False)
|
||||||
|
|
||||||
|
second_ds = pydicom.read_file(second.image.file)
|
||||||
|
#del second_ds.PixelData
|
||||||
|
#j2 = second_ds.to_json_dict()
|
||||||
|
s = print_dicom(second_ds, join=False)
|
||||||
|
|
||||||
|
diff = difflib.HtmlDiff().make_table(f, s)
|
||||||
|
|
||||||
|
return HttpResponse(diff)
|
||||||
|
|
||||||
|
|
||||||
|
return HttpResponse("Fail")
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def series_image_dicom(request, pk):
|
def series_image_dicom(request, pk):
|
||||||
|
|||||||
+11
-3
@@ -126,7 +126,7 @@ def pretty_print_dicom(dataset, indent=0):
|
|||||||
return "".join(l)
|
return "".join(l)
|
||||||
|
|
||||||
|
|
||||||
def print_dicom(dataset, indent=0, include_tag_ids=True):
|
def print_dicom(dataset, indent=0, include_tag_ids=True, join=True, join_text="\n<br/>"):
|
||||||
"""Go through all items in the dataset and print them with custom format
|
"""Go through all items in the dataset and print them with custom format
|
||||||
|
|
||||||
Modelled after Dataset._pretty_str()
|
Modelled after Dataset._pretty_str()
|
||||||
@@ -142,7 +142,12 @@ def print_dicom(dataset, indent=0, include_tag_ids=True):
|
|||||||
if data_element.VR == "SQ": # a sequence
|
if data_element.VR == "SQ": # a sequence
|
||||||
l.append(f"{data_element.tag}: {indent_string}{data_element.name}")
|
l.append(f"{data_element.tag}: {indent_string}{data_element.name}")
|
||||||
for sequence_item in data_element.value:
|
for sequence_item in data_element.value:
|
||||||
l.append(print_dicom(sequence_item, indent + 1))
|
l_ = print_dicom(sequence_item, indent + 1, join=join, join_text=join_text)
|
||||||
|
if join:
|
||||||
|
l.append(l_)
|
||||||
|
else:
|
||||||
|
l.extend(l_)
|
||||||
|
|
||||||
l.append(next_indent_string + "---------")
|
l.append(next_indent_string + "---------")
|
||||||
else:
|
else:
|
||||||
if data_element.name in dont_print:
|
if data_element.name in dont_print:
|
||||||
@@ -152,7 +157,10 @@ def print_dicom(dataset, indent=0, include_tag_ids=True):
|
|||||||
if len(repr_value) > 50:
|
if len(repr_value) > 50:
|
||||||
repr_value = repr_value[:50] + "..."
|
repr_value = repr_value[:50] + "..."
|
||||||
l.append(f"{data_element.tag}: {indent_string:s} {data_element.name:s} = {repr_value:s}")
|
l.append(f"{data_element.tag}: {indent_string:s} {data_element.name:s} = {repr_value:s}")
|
||||||
return "\n<br/>".join(l)
|
if not join:
|
||||||
|
return l
|
||||||
|
|
||||||
|
return join_text.join(l)
|
||||||
|
|
||||||
def get_image_dicom_hash(img, dataset=None, hash_type="md5", direct_pixel_data=True) -> (str, bool):
|
def get_image_dicom_hash(img, dataset=None, hash_type="md5", direct_pixel_data=True) -> (str, bool):
|
||||||
if dataset is None:
|
if dataset is None:
|
||||||
|
|||||||
@@ -1118,3 +1118,15 @@ table .peninsula-trainee::before {
|
|||||||
.show-on-hover:hover {
|
.show-on-hover:hover {
|
||||||
opacity: 100%;
|
opacity: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.diff_add {
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
|
||||||
|
.diff_sub {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:has(> td > a) {
|
||||||
|
border: 1px blue solid
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user