From 50c52dcdf5fc8fe64fd1a0532ec40649692d8acb Mon Sep 17 00:00:00 2001 From: ilia Date: Wed, 2 Nov 2022 12:15:13 +0300 Subject: [PATCH 1/2] add point cloud and patology generation --- config/api_router.py | 11 +++++++++++ config/settings/base.py | 2 +- image_markuper/dicom/api/serializers.py | 19 +++++++++++++++++++ image_markuper/dicom/api/views.py | 24 +++++++++++++++++++++++- requirements/base.txt | 2 ++ 5 files changed, 56 insertions(+), 2 deletions(-) diff --git a/config/api_router.py b/config/api_router.py index 7ddcb0f..b59dc46 100644 --- a/config/api_router.py +++ b/config/api_router.py @@ -15,6 +15,8 @@ from dicom.api.views import ( RetrieveUpdateDeleteRoiApi, RetrieveUpdateDeleteRulerApi, SmartFileUploadApi, + GeneratePatology, + GeneratePointCloud ) from django.urls import include, path from rest_framework_simplejwt.views import TokenObtainPairView, TokenRefreshView @@ -120,4 +122,13 @@ urlpatterns = [ ] ), ), + path( + 'generate/', + include( + [ + path('patology', GeneratePatology.as_view(), name='generate_patology'), + path('point_cloud', GeneratePointCloud.as_view(), name='generate_patology') + ] + ), + ) ] diff --git a/config/settings/base.py b/config/settings/base.py index f136cf5..176684d 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -299,7 +299,7 @@ SPECTACULAR_SETTINGS = { "SERVE_PERMISSIONS": ["rest_framework.permissions.AllowAny"], "SERVERS": [ {"url": "https://dev.akarpov.ru", "description": "Development server"}, - {"url": "https//127.0.0.1:8000", "description": "Development server"}, + {"url": "http://127.0.0.1:8000", "description": "Development server"}, ], } diff --git a/image_markuper/dicom/api/serializers.py b/image_markuper/dicom/api/serializers.py index 81779dd..192477e 100644 --- a/image_markuper/dicom/api/serializers.py +++ b/image_markuper/dicom/api/serializers.py @@ -177,3 +177,22 @@ class ProjectSerializer(serializers.ModelSerializer): class Meta: model = Project fields = ["files", "slug", "created"] + + + +class PatologyGenerateSerializer(serializers.Serializer): + project_slug = serializers.CharField() + points = serializers.ListField(child=CoordinateSerializer()) + depth = serializers.ListField(child=serializers.IntegerField()) + + +class VoxelSerializer(serializers.Serializer): + x = serializers.IntegerField() + y = serializers.IntegerField() + z = serializers.IntegerField() + value = serializers.IntegerField() + + +class PointCloudSerializer(serializers.Serializer): + voxels = serializers.ListField(child=serializers.IntegerField(), read_only=True) + project_slug = serializers.CharField(write_only=True) diff --git a/image_markuper/dicom/api/views.py b/image_markuper/dicom/api/views.py index ccdf4fd..82d6e68 100644 --- a/image_markuper/dicom/api/views.py +++ b/image_markuper/dicom/api/views.py @@ -6,7 +6,7 @@ from rest_framework.parsers import FormParser, MultiPartParser from rest_framework.response import Response from ..models import Circle, Dicom, Project, Roi -from ..services import process_files +from ..services import generate_3d_point_cloud, get_bbox, process_files from .serializers import ( BaseShapeLayerSerializer, BaseShapeSerializer, @@ -15,11 +15,13 @@ from .serializers import ( FreeHandSerializer, ListDicomSerializer, ListProjectSerializer, + PointCloudSerializer, ProjectSerializer, RoiSerializer, RulerSerializer, SmartFileUploadSerializer, create_coordinate, + PatologyGenerateSerializer ) @@ -216,3 +218,23 @@ class RetrieveUpdateDeleteProjectApi(generics.RetrieveUpdateDestroyAPIView): queryset = Project.objects.all() lookup_field = "slug" + + +class GeneratePatology(generics.CreateAPIView): + serializer_class = PatologyGenerateSerializer + + def create(self, request, *args, **kwargs): + data = self.get_serializer(request.data).data + bbox = get_bbox(data['project_slug'], data['points'], data['depth']) + return Response(data={}, status=200) + + +class GeneratePointCloud(generics.CreateAPIView): + serializer_class = PointCloudSerializer + + def create(self, request, *args, **kwargs): + print(request.data) + data = request.data + point_cloud = generate_3d_point_cloud(data['project_slug']) + print(point_cloud[0:5]) + return Response(data={'voxels': point_cloud}, status=200) \ No newline at end of file diff --git a/requirements/base.txt b/requirements/base.txt index 22a1d09..36f60c6 100644 --- a/requirements/base.txt +++ b/requirements/base.txt @@ -25,3 +25,5 @@ django-cors-headers==3.13.0 # https://github.com/adamchainz/django-cors-headers djangorestframework-simplejwt==5.2.2 # DRF-spectacular for api documentation drf-spectacular==0.24.2 # https://github.com/tfranzel/drf-spectacular +numpy==1.23.4 +pydicom==2.3.0 \ No newline at end of file From 312d145e3b4404e4157d8a6f0f06d20515ef03a2 Mon Sep 17 00:00:00 2001 From: ilia Date: Wed, 2 Nov 2022 12:15:47 +0300 Subject: [PATCH 2/2] add point cloud and patology generation --- image_markuper/dicom/services.py | 45 ++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/image_markuper/dicom/services.py b/image_markuper/dicom/services.py index 2a18e34..d6531fd 100644 --- a/image_markuper/dicom/services.py +++ b/image_markuper/dicom/services.py @@ -1,3 +1,4 @@ +from functools import lru_cache import glob import os import shutil @@ -9,10 +10,14 @@ from dicom.models import Coordinate, Dicom, Project from django.core.files import File from django.core.files.uploadedfile import InMemoryUploadedFile, TemporaryUploadedFile from utils.generators import generate_charset +from typing import List, Union +from django.conf import settings +import pydicom + def process_files( - files: list[TemporaryUploadedFile | InMemoryUploadedFile], user, slug=None + files: List[Union[TemporaryUploadedFile, InMemoryUploadedFile]], user, slug=None ): if slug: project = Project.objects.get(slug=slug) @@ -23,7 +28,7 @@ def process_files( if content_type == "DICOM medical imaging data": Dicom.objects.create(file=file, project=project, user=user) elif "Zip" in content_type: - dit_path = f"/tmp/{generate_charset(10)}" + dit_path = f"tmp/{generate_charset(10)}" os.mkdir(dit_path) with zipfile.ZipFile(file.temporary_file_path(), "r") as zip_ref: zip_ref.extractall(dit_path) @@ -51,3 +56,39 @@ def create_coordinate(coordinates, obj): y=coordinate["y"], shape=obj, ) + + +def get_bbox(project_id, points, image_range): + project: Project = Project.objects.get(slug=project_id) + #print(Dicom.objects.all()) + files = project.files.all() + bbox_data = [] + for file_number in range(image_range[0], image_range[1]+1): + print(points[0]['x']) + bbox_data.append( + pydicom.dcmread( + files[file_number].file.path).pixel_array[int(points[0]['x']):int(points[1]['x']), int(points[0]['y']):int(points[1]['y'])].tolist()) + print(pydicom.dcmread(files[file_number].file.path).pixel_array) + print(bbox_data) + #print(project.files.all(), "files", project) + return [] + +@lru_cache(512) +def generate_3d_point_cloud(project_slug: str): + project = Project.objects.get(slug=project_slug) + + point_clouds = [] + + for fileindex, file in enumerate(project.files.all()[::3]): + print(fileindex) + pixel_array = pydicom.dcmread( + file.file.path + ).pixel_array + for iindex, i in enumerate(pixel_array[::3]): + for jindex, j in enumerate(i[::3]): + if j <= 240: + pass + #point_clouds.append({'x': jindex, 'y': iindex, 'z': fileindex, 'value': 0}) + else: + point_clouds.append([jindex, iindex, fileindex, j]) + return point_clouds